返回 DeepSeek-Reasonix
hooks_trust_test.go
根目录 / desktop / hooks_trust_test.go
1 package main
2
3 import (
4 "errors"
5 "os"
6 "path/filepath"
7 "testing"
8
9 "reasonix/internal/hook"
10 )
11
12 // Hooks a checkout ships are shown as waiting, and load once approved.
13 func TestTrustProjectHooksApprovesTheCheckoutsHooks(t *testing.T) {
14 isolateDesktopUserDirs(t)
15 project := t.TempDir()
16 if err := os.MkdirAll(filepath.Join(project, ".reasonix"), 0o755); err != nil {
17 t.Fatal(err)
18 }
19 if err := os.WriteFile(filepath.Join(project, ".reasonix", "settings.json"), []byte(`{"hooks":{"Stop":[{"command":"echo done"}]}}`), 0o644); err != nil {
20 t.Fatal(err)
21 }
22 app := NewApp()
23 app.tabs = map[string]*WorkspaceTab{
24 "project": {ID: "project", Scope: "project", WorkspaceRoot: project, Ready: true},
25 }
26 app.activeTabID = "project"
27 if view := app.HooksSettings("project"); view.Trusted {
28 t.Fatalf("unapproved checkout hooks read as trusted: %+v", view)
29 }
30 if loaded := hook.Load(hook.LoadOptions{ProjectRoot: project}); len(loaded) != 0 {
31 t.Fatalf("unapproved checkout hooks loaded: %+v", loaded)
32 }
33 if err := app.TrustProjectHooks(); err != nil {
34 t.Fatalf("TrustProjectHooks: %v", err)
35 }
36 if view := app.HooksSettings("project"); !view.Trusted {
37 t.Fatalf("approved hooks still read as waiting: %+v", view)
38 }
39 if loaded := hook.Load(hook.LoadOptions{ProjectRoot: project}); len(loaded) != 1 {
40 t.Fatalf("approved hooks = %+v, want one", loaded)
41 }
42 if err := app.TrustProjectHooksForRoot(""); err == nil {
43 t.Fatal("approving with no workspace succeeded")
44 }
45 }
46
47 // Approving from the editor covers only what the editor shows.
48 func TestTrustProjectHooksRefusesFieldsTheEditorCannotShow(t *testing.T) {
49 isolateDesktopUserDirs(t)
50 project := t.TempDir()
51 if err := os.MkdirAll(filepath.Join(project, ".reasonix"), 0o755); err != nil {
52 t.Fatal(err)
53 }
54 if err := os.WriteFile(filepath.Join(project, ".reasonix", "settings.json"), []byte(`{"hooks":{"Stop":[{"command":"echo done","env":{"NODE_OPTIONS":"--require ./x.js"}}]}}`), 0o644); err != nil {
55 t.Fatal(err)
56 }
57 if err := NewApp().TrustProjectHooksForRoot(project); !errors.Is(err, errHooksNotShown) {
58 t.Fatalf("TrustProjectHooksForRoot = %v, want errHooksNotShown", err)
59 }
60 if loaded := hook.Load(hook.LoadOptions{ProjectRoot: project}); len(loaded) != 0 {
61 t.Fatalf("hooks loaded after a refused approval: %+v", loaded)
62 }
63 }
64
65 // The Approve button covers what the view showed, not a later rewrite.
66 func TestTrustProjectHooksRefusesHooksChangedSinceShown(t *testing.T) {
67 isolateDesktopUserDirs(t)
68 project := t.TempDir()
69 settings := filepath.Join(project, ".reasonix", "settings.json")
70 if err := os.MkdirAll(filepath.Dir(settings), 0o755); err != nil {
71 t.Fatal(err)
72 }
73 if err := os.WriteFile(settings, []byte(`{"hooks":{"Stop":[{"command":"echo shown"}]}}`), 0o644); err != nil {
74 t.Fatal(err)
75 }
76 app := NewApp()
77 if err := app.TrustProjectHooksForRoot(project); !errors.Is(err, errHooksChangedSinceShown) {
78 t.Fatalf("approving hooks never shown = %v, want errHooksChangedSinceShown", err)
79 }
80 app.tabs = map[string]*WorkspaceTab{"p": {ID: "p", Scope: "project", WorkspaceRoot: project, Ready: true}}
81 app.activeTabID = "p"
82 _ = app.HooksSettings("project")
83 if err := os.WriteFile(settings, []byte(`{"hooks":{"Stop":[{"command":"echo swapped"}]}}`), 0o644); err != nil {
84 t.Fatal(err)
85 }
86 if err := app.TrustProjectHooksForRoot(project); !errors.Is(err, errHooksChangedSinceShown) {
87 t.Fatalf("approving a rewrite = %v, want errHooksChangedSinceShown", err)
88 }
89 if loaded := hook.Load(hook.LoadOptions{ProjectRoot: project}); len(loaded) != 0 {
90 t.Fatalf("rewritten hooks loaded: %+v", loaded)
91 }
92 }
93
93 lines GO