//go:build integration

package tilt

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

func TestIntegration_RemoteSourceStagesOneGitAwareRoot(t *testing.T) {
	repository := t.TempDir()
	runGit := func(args ...string) {
		t.Helper()
		command := exec.Command("git", append([]string{"-C", repository}, args...)...)
		if output, err := command.CombinedOutput(); err != nil {
			t.Fatalf("git %v: %v\n%s", args, err, output)
		}
	}
	runGit("init", "-q")
	runGit("config", "user.email", "cp9@example.com")
	runGit("config", "user.name", "cp9")
	if err := os.MkdirAll(filepath.Join(repository, "service-a"), 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.MkdirAll(filepath.Join(repository, "service-b"), 0o755); err != nil {
		t.Fatal(err)
	}
	write := func(name, value string) {
		t.Helper()
		if err := os.WriteFile(filepath.Join(repository, name), []byte(value), 0o600); err != nil {
			t.Fatal(err)
		}
	}
	write(".gitignore", "ignored.txt\n")
	write("service-a/tracked.txt", "tracked\n")
	write("service-b/untracked.txt", "untracked\n")
	if err := os.MkdirAll(filepath.Join(repository, "bin"), 0o755); err != nil {
		t.Fatal(err)
	}
	write("bin/untracked.txt", "untracked build input\n")
	write("ignored.txt", "ignored\n")
	runGit("add", ".gitignore", "service-a/tracked.txt")
	runGit("commit", "-qm", "fixture")

	workingDirectory, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	cp9Root, ok := findRepositoryRoot(filepath.Join(workingDirectory, "remote_source_integration_test.go"))
	if !ok {
		t.Fatal("CP9 repository root not found")
	}
	stagedRoots, cleanup, err := stageGitRepository(
		context.Background(),
		filepath.Join(cp9Root, "personas", "dev-full", "build", "remote", "source-manifest.py"),
		repository,
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanup()
	if len(stagedRoots) != 1 {
		t.Fatalf("staged roots = %d, want 1", len(stagedRoots))
	}
	staged := stagedRoots[0].stagedRoot
	for _, name := range []string{
		"service-a/tracked.txt",
		"service-b/untracked.txt",
		"bin/untracked.txt",
	} {
		if _, err := os.Stat(filepath.Join(staged, name)); err != nil {
			t.Fatalf("%s missing from staged git root: %v", name, err)
		}
	}
	if _, err := os.Stat(filepath.Join(staged, "ignored.txt")); !os.IsNotExist(err) {
		t.Fatalf("git-ignored file was staged: %v", err)
	}
	remoteA := remoteRepositoryRoot("/remote/work/repo/service-a", "service-a")
	remoteB := remoteRepositoryRoot("/remote/work/repo/service-b", "service-b")
	if remoteA != "/remote/work/repo" || remoteB != remoteA {
		t.Fatalf("services map to different remote roots: %q, %q", remoteA, remoteB)
	}
}

func TestIntegration_RemoteSourceStagesLocalReplacementRoot(t *testing.T) {
	workspace := t.TempDir()
	primary := filepath.Join(workspace, "primary")
	replacement := filepath.Join(workspace, "replacement")
	for _, repository := range []string{primary, replacement} {
		if err := os.MkdirAll(repository, 0o755); err != nil {
			t.Fatal(err)
		}
		command := exec.Command("git", "-C", repository, "init", "-q")
		if output, err := command.CombinedOutput(); err != nil {
			t.Fatalf("init %s: %v\n%s", repository, err, output)
		}
		for _, args := range [][]string{{"config", "user.email", "cp9@example.com"}, {"config", "user.name", "cp9"}} {
			if output, err := exec.Command("git", append([]string{"-C", repository}, args...)...).CombinedOutput(); err != nil {
				t.Fatalf("git %v: %v\n%s", args, err, output)
			}
		}
	}
	if err := os.WriteFile(filepath.Join(primary, "go.mod"), []byte("module example.com/primary\n\nreplace example.com/replacement => ../replacement\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(replacement, "go.mod"), []byte("module example.com/replacement\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(replacement, "replacement.go"), []byte("package replacement\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	for _, repository := range []string{primary, replacement} {
		if output, err := exec.Command("git", "-C", repository, "add", ".").CombinedOutput(); err != nil {
			t.Fatalf("git add %s: %v\n%s", repository, err, output)
		}
		if output, err := exec.Command("git", "-C", repository, "commit", "-qm", "fixture").CombinedOutput(); err != nil {
			t.Fatalf("git commit %s: %v\n%s", repository, err, output)
		}
	}

	workingDirectory, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	cp9Root, ok := findRepositoryRoot(filepath.Join(workingDirectory, "remote_source_integration_test.go"))
	if !ok {
		t.Fatal("CP9 repository root not found")
	}
	stagedRoots, cleanup, err := stageGitRepository(
		context.Background(),
		filepath.Join(cp9Root, "personas", "dev-full", "build", "remote", "source-manifest.py"),
		primary,
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanup()
	if len(stagedRoots) != 2 {
		t.Fatalf("staged roots = %d, want 2", len(stagedRoots))
	}
	foundReplacement := false
	for _, staged := range stagedRoots {
		if filepath.Base(staged.localRoot) != "replacement" {
			continue
		}
		foundReplacement = true
		if _, err := os.Stat(filepath.Join(staged.stagedRoot, "replacement.go")); err != nil {
			t.Fatalf("local replacement was not staged: %v", err)
		}
	}
	if !foundReplacement {
		t.Fatal("local replacement root was not discovered")
	}
}

func TestIntegration_RemoteDirectoryChangeParserUsesRealRsyncOutput(t *testing.T) {
	source := t.TempDir()
	current := t.TempDir()
	if err := os.Mkdir(filepath.Join(source, "empty"), 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(source, "file.go"), []byte("one"), 0o600); err != nil {
		t.Fatal(err)
	}
	rsync := func(dryRun bool) string {
		t.Helper()
		args := []string{
			"-a", "--delete", "--checksum", "--itemize-changes", "--omit-dir-times",
			"--exclude=.cp9-partial", "--exclude=._*", "--out-format=%i %n%L",
		}
		if dryRun {
			args = append(args, "--dry-run")
		}
		args = append(args, source+string(os.PathSeparator), current+string(os.PathSeparator))
		output, err := exec.Command("rsync", args...).CombinedOutput()
		if err != nil {
			t.Fatalf("rsync %v: %v\n%s", args, err, output)
		}
		return string(output)
	}
	rsync(false)
	if remoteDirectoryHasChanges(rsync(true)) {
		t.Fatal("unchanged source was reported as changed")
	}
	if err := os.WriteFile(filepath.Join(source, "file.go"), []byte("two"), 0o600); err != nil {
		t.Fatal(err)
	}
	if !remoteDirectoryHasChanges(rsync(true)) {
		t.Fatal("file content change was not reported")
	}
	if err := os.Remove(filepath.Join(source, "empty")); err != nil {
		t.Fatal(err)
	}
	if !remoteDirectoryHasChanges(rsync(true)) {
		t.Fatal("empty-directory deletion masked a file content change")
	}
	rsync(false)
	if err := os.Remove(filepath.Join(source, "file.go")); err != nil {
		t.Fatal(err)
	}
	if !remoteDirectoryHasChanges(rsync(true)) {
		t.Fatal("file deletion was not reported")
	}
	rsync(false)
	if err := os.Mkdir(filepath.Join(source, "empty"), 0o755); err != nil {
		t.Fatal(err)
	}
	rsync(false)
	if err := os.Chmod(filepath.Join(current, "empty"), 0o700); err != nil {
		t.Fatal(err)
	}
	if remoteDirectoryHasChanges(rsync(true)) {
		t.Fatal("directory-only metadata change was reported as source content")
	}
	if err := os.Remove(filepath.Join(source, "empty")); err != nil {
		t.Fatal(err)
	}
	if remoteDirectoryHasChanges(rsync(true)) {
		t.Fatal("empty-directory deletion was reported as source content")
	}
}

func TestIntegration_RemoteGoUsesGitHubAppWithoutLaptopProxy(t *testing.T) {
	environment := strings.Join(remoteGoEnvironment(), "\n")
	if strings.Contains(environment, "file://") || strings.Contains(environment, "GONOPROXY=none") {
		t.Fatalf("remote Go environment still uses the local module proxy:\n%s", environment)
	}
	for _, expected := range []string{
		"GOPRIVATE=github.com/tidbcloud/*",
		"GONOPROXY=github.com/tidbcloud/*",
		"GOPROXY=https://proxy.golang.org,direct",
	} {
		if !strings.Contains(environment, expected) {
			t.Fatalf("remote Go environment is missing %q:\n%s", expected, environment)
		}
	}

}

func TestIntegration_GitHubAppAuthorizationIsSharedRefreshedAndRevoked(t *testing.T) {
	workingDirectory, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	cp9Root, ok := findRepositoryRoot(filepath.Join(workingDirectory, "remote_source_integration_test.go"))
	if !ok {
		t.Fatal("CP9 repository root not found")
	}

	fixture := t.TempDir()
	fakeBin := filepath.Join(fixture, "bin")
	moduleRoot := filepath.Join(fixture, "module")
	tempRoot := filepath.Join(fixture, "tmp")
	homeRoot := filepath.Join(fixture, "home")
	sessionRoots := []string{filepath.Join(fixture, "session-a"), filepath.Join(fixture, "session-b")}
	for _, directory := range append([]string{fakeBin, moduleRoot, tempRoot, homeRoot}, sessionRoots...) {
		if err := os.MkdirAll(directory, 0o755); err != nil {
			t.Fatal(err)
		}
	}
	if err := os.WriteFile(filepath.Join(moduleRoot, "go.mod"), []byte("module example.com/test\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	curlCount := filepath.Join(fixture, "curl-count")
	deviceCount := filepath.Join(fixture, "device-count")
	oauthCount := filepath.Join(fixture, "oauth-count")
	refreshCount := filepath.Join(fixture, "refresh-count")
	goCount := filepath.Join(fixture, "go-count")
	const clientID = "Iv1.public-client-id"
	tokenStore := filepath.Join(homeRoot, ".cp9", "data", "shared", "github-app")
	clientKey := fmt.Sprintf("%x", sha256.Sum256([]byte(clientID)))
	tokenCache := filepath.Join(tokenStore, clientKey+".json")
	writeExecutable := func(name, content string) {
		t.Helper()
		if err := os.WriteFile(filepath.Join(fakeBin, name), []byte(content), 0o700); err != nil {
			t.Fatal(err)
		}
	}
	writeExecutable("curl", `#!/bin/sh
printf '%s\n' call >>"$FAKE_CURL_COUNT"
all_args=$*
case "$all_args" in *ghu_*|*ghr_*) exit 11 ;; esac
case "$all_args" in *device-code*) exit 8 ;; esac
url=
request_file=
output_file=
config_file=
while [ "$#" -gt 0 ]; do
  case "$1" in
    https://*) url=$1; shift ;;
    @*) request_file=${1#@}; shift ;;
    --output) output_file=$2; shift 2 ;;
    --config) config_file=$2; shift 2 ;;
    *) shift ;;
  esac
done
case "$url" in
  https://github.com/login/device/code)
    case "$all_args" in *"--data-urlencode client_id=Iv1.public-client-id"*) ;; *) exit 4 ;; esac
    count=0
    [ ! -f "$FAKE_DEVICE_COUNT" ] || count=$(cat "$FAKE_DEVICE_COUNT")
    count=$((count + 1))
    printf '%s' "$count" >"$FAKE_DEVICE_COUNT"
    printf '{"device_code":"device-code-%s","user_code":"ABCD-EFGH","verification_uri":"https://github.com/login/device","expires_in":900,"interval":1}' "$count"
    ;;
  https://github.com/login/oauth/access_token)
    [ -f "$request_file" ] || exit 5
    request=$(cat "$request_file")
    case "$request" in
      *"grant_type=refresh_token"*)
        case "$request" in *"refresh_token=ghr_fixture-refresh"*) ;; *) exit 6 ;; esac
        count=0
        [ ! -f "$FAKE_REFRESH_COUNT" ] || count=$(cat "$FAKE_REFRESH_COUNT")
        count=$((count + 1))
        printf '%s' "$count" >"$FAKE_REFRESH_COUNT"
        response='{"access_token":"ghu_refreshed-token","expires_in":28800,"refresh_token":"ghr_refreshed","refresh_token_expires_in":15897600,"token_type":"bearer"}'
        ;;
      *"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"*)
        count=0
        [ ! -f "$FAKE_OAUTH_COUNT" ] || count=$(cat "$FAKE_OAUTH_COUNT")
        count=$((count + 1))
        printf '%s' "$count" >"$FAKE_OAUTH_COUNT"
        [ -z "$output_file" ] || : >"$output_file"
        [ "$count" -gt 1 ] || exit 35
        case "$request" in
          *"device_code=device-code-1"*)
            response='{"access_token":"ghu_fixture-token","expires_in":28800,"refresh_token":"ghr_fixture-refresh","refresh_token_expires_in":15897600,"token_type":"bearer"}'
            ;;
          *"device_code=device-code-2"*)
            response='{"access_token":"ghu_reauthorized-token","expires_in":28800,"refresh_token":"ghr_reauthorized","refresh_token_expires_in":15897600,"token_type":"bearer"}'
            ;;
          *) exit 7 ;;
        esac
        ;;
      *) exit 7 ;;
    esac
    if [ -n "$output_file" ]; then printf '%s' "$response" >"$output_file"; else printf '%s' "$response"; fi
    ;;
  https://api.github.com/user)
    [ -f "$config_file" ] || exit 9
    case "$(cat "$config_file")" in
      *"Authorization: Bearer ghu_fixture-token"*|*"Authorization: Bearer ghu_refreshed-token"*|*"Authorization: Bearer ghu_reauthorized-token"*)
        printf '%s' '{"id":1001,"login":"fixture-user"}' >"$output_file"
        printf '%s' 200
        ;;
      *"Authorization: Bearer ghu_revoked-token"*)
        printf '%s' '{"message":"Bad credentials"}' >"$output_file"
        printf '%s' 401
        ;;
      *"Authorization: Bearer ghu_network-token"*) exit 28 ;;
      *) exit 10 ;;
    esac
    ;;
  *) exit 3 ;;
esac
`)
	writeExecutable("open", "#!/bin/sh\nexit 0\n")
	writeExecutable("go", `#!/bin/sh
[ "$("$GIT_ASKPASS" "Password for 'https://x-access-token@github.com':")" = ghu_fixture-token ] || exit 2
if "$GIT_ASKPASS" "Password for 'https://gitlab.example.com':" >/dev/null 2>&1; then exit 3; fi
count=0
[ ! -f "$FAKE_GO_COUNT" ] || count=$(cat "$FAKE_GO_COUNT")
count=$((count + 1))
printf '%s' "$count" >"$FAKE_GO_COUNT"
[ "$count" -gt 1 ]
`)

	userTokenHelper := filepath.Join(
		cp9Root,
		"personas", "dev-full", "build", "remote", "github-app-user-token.sh",
	)
	env := []string{
		"PATH=" + fakeBin + ":" + os.Getenv("PATH"),
		"HOME=" + homeRoot,
		"TMPDIR=" + tempRoot,
		"FAKE_CURL_COUNT=" + curlCount,
		"FAKE_DEVICE_COUNT=" + deviceCount,
		"FAKE_OAUTH_COUNT=" + oauthCount,
		"FAKE_REFRESH_COUNT=" + refreshCount,
	}
	lockHelper := filepath.Join(
		cp9Root,
		"personas", "dev-full", "build", "remote", "remote-source-lock.sh",
	)
	for name, fields := range map[string]map[string]string{
		"missing-token": {"lease-expires": "1"},
		"missing-lease": {"token": "interrupted-owner"},
	} {
		lock := filepath.Join(fixture, name+".lock")
		if err := os.Mkdir(lock, 0o700); err != nil {
			t.Fatal(err)
		}
		for field, value := range fields {
			if err := os.WriteFile(filepath.Join(lock, field), []byte(value+"\n"), 0o600); err != nil {
				t.Fatal(err)
			}
		}
		ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
		command := exec.CommandContext(ctx, "sh", lockHelper, "acquire", lock, "new-owner", "30", "6", name)
		if output, err := command.CombinedOutput(); err != nil {
			cancel()
			t.Fatalf("recover %s lock: %v\n%s", name, err, output)
		}
		cancel()
		if output, err := exec.Command("sh", lockHelper, "release", lock, "new-owner").CombinedOutput(); err != nil {
			t.Fatalf("release recovered %s lock: %v\n%s", name, err, output)
		}
	}
	replacedLock := filepath.Join(fixture, "replaced-owner.lock")
	if output, err := exec.Command("sh", lockHelper, "acquire", replacedLock, "old-owner", "1", "3", "old").CombinedOutput(); err != nil {
		t.Fatalf("acquire old lock owner: %v\n%s", err, output)
	}
	time.Sleep(2 * time.Second)
	if output, err := exec.Command("sh", lockHelper, "acquire", replacedLock, "new-owner", "30", "5", "new").CombinedOutput(); err != nil {
		t.Fatalf("replace expired lock owner: %v\n%s", err, output)
	}
	for _, operation := range [][]string{
		{"heartbeat", replacedLock, "old-owner", "30"},
		{"release", replacedLock, "old-owner"},
	} {
		if err := exec.Command("sh", append([]string{lockHelper}, operation...)...).Run(); err == nil {
			t.Fatalf("stale owner unexpectedly completed %s", operation[0])
		}
	}
	if output, err := exec.Command("sh", lockHelper, "check", replacedLock, "new-owner").CombinedOutput(); err != nil {
		t.Fatalf("replacement owner lost its lock: %v\n%s", err, output)
	}
	if output, err := exec.Command("sh", lockHelper, "release", replacedLock, "new-owner").CombinedOutput(); err != nil {
		t.Fatalf("release replacement owner: %v\n%s", err, output)
	}
	staleLock := filepath.Join(tokenStore, clientKey+".lock")
	if err := os.MkdirAll(tokenStore, 0o700); err != nil {
		t.Fatal(err)
	}
	if err := os.Mkdir(staleLock, 0o700); err != nil {
		t.Fatal(err)
	}
	for name, value := range map[string]string{
		"token":         "stale-owner",
		"lease-expires": "1",
		"owner":         "interrupted authorization",
	} {
		if err := os.WriteFile(filepath.Join(staleLock, name), []byte(value+"\n"), 0o600); err != nil {
			t.Fatal(err)
		}
	}
	type invocation struct {
		command *exec.Cmd
		stdout  bytes.Buffer
		stderr  bytes.Buffer
	}
	runHelpers := func(count int) []invocation {
		t.Helper()
		invocations := make([]invocation, count)
		for i := range invocations {
			command := exec.Command(userTokenHelper, clientID)
			command.Env = env
			command.Dir = sessionRoots[i%len(sessionRoots)]
			invocations[i].command = command
			command.Stdout = &invocations[i].stdout
			command.Stderr = &invocations[i].stderr
			if err := command.Start(); err != nil {
				t.Fatal(err)
			}
		}
		for i := range invocations {
			if err := invocations[i].command.Wait(); err != nil {
				t.Fatalf("GitHub App user token helper %d: %v\n%s", i, err, invocations[i].stderr.Bytes())
			}
		}
		return invocations
	}
	invocations := runHelpers(2)
	for i := range invocations {
		if strings.TrimSpace(invocations[i].stdout.String()) != "ghu_fixture-token" {
			t.Fatalf("GitHub App user token %d = %q; stderr=%q", i, invocations[i].stdout.String(), invocations[i].stderr.String())
		}
	}
	calls, err := os.ReadFile(curlCount)
	if err != nil {
		t.Fatal(err)
	}
	if strings.Count(string(calls), "\n") != 5 {
		t.Fatalf("GitHub calls = %q, want device + two polls + two identity checks", calls)
	}
	deviceCalls, err := os.ReadFile(deviceCount)
	if err != nil {
		t.Fatal(err)
	}
	if string(deviceCalls) != "1" {
		t.Fatalf("Device Flow count = %q, want one across concurrent sessions", deviceCalls)
	}
	for _, sessionRoot := range sessionRoots {
		if _, err := os.Stat(filepath.Join(sessionRoot, "github-app-user-token.json")); !os.IsNotExist(err) {
			t.Fatalf("session credential exists under %s: %v", sessionRoot, err)
		}
	}
	oauthCalls, err := os.ReadFile(oauthCount)
	if err != nil {
		t.Fatal(err)
	}
	if string(oauthCalls) != "2" {
		t.Fatalf("OAuth poll calls = %q, want first transient and second success", oauthCalls)
	}
	cache, err := os.ReadFile(tokenCache)
	if err != nil {
		t.Fatal(err)
	}
	var cached map[string]any
	if err := json.Unmarshal(cache, &cached); err != nil {
		t.Fatal(err)
	}
	if cached["client_id"] != clientID || cached["github_user_id"] != "1001" || cached["github_login"] != "fixture-user" {
		t.Fatalf("shared GitHub authorization identity = client %v, user %v, login %v", cached["client_id"], cached["github_user_id"], cached["github_login"])
	}
	if cached["refresh_token"] != "ghr_fixture-refresh" {
		t.Fatal("GitHub App refresh token was not cached for rotation")
	}
	info, err := os.Stat(tokenCache)
	if err != nil {
		t.Fatal(err)
	}
	if info.Mode().Perm() != 0o600 {
		t.Fatalf("token cache mode = %o, want 600", info.Mode().Perm())
	}
	storeInfo, err := os.Stat(tokenStore)
	if err != nil {
		t.Fatal(err)
	}
	if storeInfo.Mode().Perm() != 0o700 {
		t.Fatalf("token store mode = %o, want 700", storeInfo.Mode().Perm())
	}
	writeCache := func(update func(map[string]any)) {
		t.Helper()
		data, err := os.ReadFile(tokenCache)
		if err != nil {
			t.Fatal(err)
		}
		value := map[string]any{}
		if err := json.Unmarshal(data, &value); err != nil {
			t.Fatal(err)
		}
		update(value)
		data, err = json.Marshal(value)
		if err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(tokenCache, data, 0o600); err != nil {
			t.Fatal(err)
		}
	}
	writeCache(func(value map[string]any) {
		value["expires_at"] = time.Now().Unix() + 1
	})
	refreshed := runHelpers(2)
	for i := range refreshed {
		if strings.TrimSpace(refreshed[i].stdout.String()) != "ghu_refreshed-token" {
			t.Fatalf("refreshed GitHub App user token %d = %q; stderr=%q", i, refreshed[i].stdout.String(), refreshed[i].stderr.String())
		}
	}
	refreshCalls, err := os.ReadFile(refreshCount)
	if err != nil {
		t.Fatal(err)
	}
	if string(refreshCalls) != "1" {
		t.Fatalf("refresh calls = %q, want one across concurrent sessions", refreshCalls)
	}
	deviceCalls, err = os.ReadFile(deviceCount)
	if err != nil {
		t.Fatal(err)
	}
	if string(deviceCalls) != "1" {
		t.Fatalf("Device Flow count after refresh = %q, want one", deviceCalls)
	}
	writeCache(func(value map[string]any) {
		value["access_token"] = "ghu_network-token"
		value["expires_at"] = time.Now().Unix() + 3600
	})
	networkFailure := exec.Command(userTokenHelper, clientID)
	networkFailure.Env = env
	networkFailure.Dir = sessionRoots[0]
	if output, err := networkFailure.CombinedOutput(); err == nil {
		t.Fatalf("GitHub identity network failure unexpectedly succeeded: %s", output)
	}
	cache, err = os.ReadFile(tokenCache)
	if err != nil {
		t.Fatal(err)
	}
	if err := json.Unmarshal(cache, &cached); err != nil {
		t.Fatal(err)
	}
	if cached["access_token"] != "ghu_network-token" {
		t.Fatal("transient GitHub identity failure removed the shared authorization")
	}
	writeCache(func(value map[string]any) {
		value["access_token"] = "ghu_revoked-token"
		value["expires_at"] = time.Now().Unix() + 3600
	})
	reauthorized := runHelpers(1)[0]
	if strings.TrimSpace(reauthorized.stdout.String()) != "ghu_reauthorized-token" {
		t.Fatalf("reauthorized GitHub App user token = %q; stderr=%q", reauthorized.stdout.String(), reauthorized.stderr.String())
	}
	if !strings.Contains(reauthorized.stderr.String(), "Cached GitHub App authorization is revoked or invalid; starting a new Device Flow") {
		t.Fatalf("revoked authorization message = %q", reauthorized.stderr.String())
	}
	if strings.Contains(reauthorized.stderr.String(), "ghu_") || strings.Contains(reauthorized.stderr.String(), "ghr_") {
		t.Fatalf("GitHub credential leaked to stderr: %q", reauthorized.stderr.String())
	}
	deviceCalls, err = os.ReadFile(deviceCount)
	if err != nil {
		t.Fatal(err)
	}
	if string(deviceCalls) != "2" {
		t.Fatalf("Device Flow count after revoked authorization = %q, want two", deviceCalls)
	}
	if _, err := os.Stat(staleLock); !os.IsNotExist(err) {
		t.Fatalf("GitHub user auth lock remains after concurrent stale reclaim: %v", err)
	}

	remoteHelper := filepath.Join(
		cp9Root,
		"personas", "dev-full", "build", "remote", "github-user-module-download.sh",
	)
	command := exec.Command(remoteHelper, moduleRoot)
	command.Stdin = strings.NewReader("ghu_fixture-token")
	command.Env = append(env,
		"FAKE_GO_COUNT="+goCount,
	)
	if output, err := command.CombinedOutput(); err != nil {
		t.Fatalf("GitHub user module helper: %v\n%s", err, output)
	}
	count, err := os.ReadFile(goCount)
	if err != nil {
		t.Fatal(err)
	}
	if string(count) != "2" {
		t.Fatalf("go count = %q, want 2", count)
	}
	entries, err := os.ReadDir(tempRoot)
	if err != nil {
		t.Fatal(err)
	}
	if len(entries) != 0 {
		t.Fatalf("temporary GitHub user credentials remain: %v", entries)
	}
}
