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