返回 DeepSeek-Reasonix
workspace_git_scope_test.go
根目录 / desktop / workspace_git_scope_test.go
1 package main
2
3 import (
4 "context"
5 "os"
6 "path/filepath"
7 "slices"
8 "strings"
9 "testing"
10
11 "reasonix/internal/gitcmd"
12 )
13
14 func gitScopeRepo(t *testing.T) string {
15 t.Helper()
16 root := t.TempDir()
17 for _, args := range [][]string{
18 {"init", "-b", "main"},
19 {"-c", "user.name=Test", "-c", "user.email=test@example.invalid", "commit", "--allow-empty", "-m", "init"},
20 {"branch", "shared"},
21 } {
22 if out, err := gitcmd.Command(context.Background(), root, args...).CombinedOutput(); err != nil {
23 t.Fatalf("git fixture: %v: %s", err, out)
24 }
25 }
26 return root
27 }
28
29 func TestWorkspaceGitBranchScope(t *testing.T) {
30 rootA, rootB := gitScopeRepo(t), gitScopeRepo(t)
31 a := &App{tabs: map[string]*WorkspaceTab{
32 "a": {WorkspaceRoot: rootA}, "b": {WorkspaceRoot: rootB},
33 }}
34 // The requested tab owns the target even when it is not active.
35 list, err := a.GitBranchesForTab("a", rootA)
36 if err != nil || !slices.Contains(list, "shared") {
37 t.Fatalf("branches: %v, %v", list, err)
38 }
39 base, err := a.gitWorkspaceRepoForTab("a", rootA)
40 if err != nil {
41 t.Fatal(err)
42 }
43 a.tabs["a"].WorkspaceRoot = rootB
44 if err := a.GitCheckoutForTab("a", rootA, "shared"); err == nil {
45 t.Fatal("accepted stale workspace identity")
46 }
47 if err := workspaceCheckoutBranch(base, "shared", false); err != nil {
48 t.Fatal(err)
49 }
50 if workspaceGitBranch(openWorkspaceRepo(rootA)) != "shared" || workspaceGitBranch(openWorkspaceRepo(rootB)) != "main" {
51 t.Fatal("checkout changed the replacement workspace")
52 }
53 if err := a.GitCreateBranchForTab("b", rootB, "new-branch"); err != nil {
54 t.Fatal(err)
55 }
56 if workspaceGitBranch(openWorkspaceRepo(rootA)) != "shared" || workspaceGitBranch(openWorkspaceRepo(rootB)) != "new-branch" {
57 t.Fatal("create crossed project boundaries")
58 }
59 if err := a.GitCheckoutForTab("a", rootB, "--detach"); err == nil {
60 t.Fatal("accepted option as branch")
61 }
62 for _, id := range []string{"", "missing", "relative", "deleted"} {
63 a.tabs["relative"] = &WorkspaceTab{WorkspaceRoot: "."}
64 a.tabs["deleted"] = &WorkspaceTab{WorkspaceRoot: filepath.Join(t.TempDir(), "gone")}
65 if _, err := a.GitBranchesForTab(id, rootA); err == nil {
66 t.Fatalf("accepted %q", id)
67 }
68 if err := a.GitCheckoutForTab(id, rootA, "shared"); err == nil {
69 t.Fatalf("checkout accepted %q", id)
70 }
71 if err := a.GitCreateBranchForTab(id, rootA, "new"); err == nil {
72 t.Fatalf("create accepted %q", id)
73 }
74 }
75 }
76
77 func TestWorkspaceTallyBudgetAndFileKinds(t *testing.T) {
78 base := gitScopeRepo(t)
79 root, err := os.OpenRoot(base)
80 if err != nil {
81 t.Fatal(err)
82 }
83 defer root.Close()
84 for _, fixture := range []struct {
85 name, body string
86 want int
87 partial bool
88 }{
89 {"empty", "", 0, false}, {"text", "one\ntwo", 2, false},
90 {"binary", "one\n\x00two", 0, false},
91 {"large", strings.Repeat("x\n", workspaceDiffTallyFileLimit/2) + "tail", workspaceDiffTallyFileLimit / 2, true},
92 } {
93 if err := root.WriteFile(fixture.name, []byte(fixture.body), 0600); err != nil {
94 t.Fatal(err)
95 }
96 remaining := int64(workspaceDiffTallyReadLimit)
97 count, partial := workspaceCountFileLines(context.Background(), root, fixture.name, &remaining)
98 if count != fixture.want || partial != fixture.partial || remaining < workspaceDiffTallyReadLimit-workspaceDiffTallyFileLimit {
99 t.Fatalf("%s: count=%d partial=%v remaining=%d", fixture.name, count, partial, remaining)
100 }
101 }
102 for _, path := range []string{".git", "../outside", "missing"} {
103 remaining := int64(100)
104 if n, partial := workspaceCountFileLines(context.Background(), root, path, &remaining); n != 0 || !partial || remaining != 100 {
105 t.Fatalf("read nonregular path %s", path)
106 }
107 }
108 if err := root.Symlink("text", "link"); err == nil {
109 remaining := int64(100)
110 if n, partial := workspaceCountFileLines(context.Background(), root, "link", &remaining); n != 0 || !partial || remaining != 100 {
111 t.Fatal("followed symlink")
112 }
113 if f, err := openWorkspaceTallyFile(root, "link"); err == nil {
114 f.Close()
115 t.Fatal("platform open followed symlink")
116 }
117 }
118 paths := make([]string, 17)
119 for i := range paths {
120 paths[i] = "large"
121 }
122 added, _, partial := workspaceGitDiffTally(context.Background(), openWorkspaceRepo(base), paths)
123 if added != workspaceDiffTallyReadLimit/2 || !partial {
124 t.Fatalf("aggregate budget: %d, %v", added, partial)
125 }
126 }
127
128 func TestWorkspaceChangesExpiredDeadlineIsIncomplete(t *testing.T) {
129 base := gitScopeRepo(t)
130 a := &App{tabs: map[string]*WorkspaceTab{"a": {WorkspaceRoot: base}}}
131 ctx, cancel := context.WithCancel(context.Background())
132 cancel()
133 view := a.workspaceChanges(ctx, "a")
134 if view.GitAvailable || !view.Incomplete || view.GitErr == "" {
135 t.Fatalf("expired scan: %+v", view)
136 }
137 root, err := os.OpenRoot(base)
138 if err != nil {
139 t.Fatal(err)
140 }
141 defer root.Close()
142 remaining := int64(100)
143 if _, partial := workspaceCountFileLines(ctx, root, "anything", &remaining); !partial || remaining != 100 {
144 t.Fatal("read after deadline")
145 }
146 }
147
147 lines GO