返回 DeepSeek-Reasonix
credential_diagnostics_test.go
根目录 / internal / config / credential_diagnostics_test.go
1 package config
2
3 import (
4 "os"
5 "path/filepath"
6 "runtime"
7 "testing"
8 )
9
10 func diagnosticStatus(report CredentialDiagnosticReport, id string) string {
11 for _, check := range report.Checks {
12 if check.ID == id {
13 return check.Status
14 }
15 }
16 return ""
17 }
18
19 func TestCredentialDiagnosticsRejectsDirectoryAndSymlink(t *testing.T) {
20 for _, tc := range []struct {
21 name string
22 setup func(t *testing.T, path string)
23 }{
24 {name: "directory", setup: func(t *testing.T, path string) {
25 t.Helper()
26 if err := os.MkdirAll(path, 0o700); err != nil {
27 t.Fatal(err)
28 }
29 }},
30 {name: "symlink", setup: func(t *testing.T, path string) {
31 t.Helper()
32 target := filepath.Join(t.TempDir(), "outside")
33 if err := os.WriteFile(target, []byte("KEY=value\n"), 0o600); err != nil {
34 t.Fatal(err)
35 }
36 if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
37 t.Fatal(err)
38 }
39 if err := os.Symlink(target, path); err != nil {
40 t.Fatal(err)
41 }
42 }},
43 } {
44 t.Run(tc.name, func(t *testing.T) {
45 t.Setenv("REASONIX_HOME", t.TempDir())
46 path := UserCredentialsPath()
47 tc.setup(t, path)
48 report, err := DiagnoseCredentials(CredentialDiagnosticOptions{Repair: true})
49 if err != nil {
50 t.Fatal(err)
51 }
52 if diagnosticStatus(report, "file_type") != "failed" || diagnosticStatus(report, "repair") != "failed" {
53 t.Fatalf("report = %+v", report)
54 }
55 })
56 }
57 }
58
59 func TestCredentialDiagnosticsProbeAndDryRunDoNotChangeCredential(t *testing.T) {
60 t.Setenv("REASONIX_HOME", t.TempDir())
61 path := UserCredentialsPath()
62 if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
63 t.Fatal(err)
64 }
65 if err := os.WriteFile(path, []byte("KEEP=secret\n"), 0o400); err != nil {
66 t.Fatal(err)
67 }
68 before, err := os.Stat(path)
69 if err != nil {
70 t.Fatal(err)
71 }
72 report, err := DiagnoseCredentials(CredentialDiagnosticOptions{Probe: true, Repair: true, DryRun: true})
73 if err != nil {
74 t.Fatal(err)
75 }
76 after, err := os.Stat(path)
77 if err != nil {
78 t.Fatal(err)
79 }
80 raw, err := os.ReadFile(path)
81 if err != nil {
82 t.Fatal(err)
83 }
84 if string(raw) != "KEEP=secret\n" || before.Mode().Perm() != after.Mode().Perm() {
85 t.Fatalf("dry-run mutated credentials: mode %o -> %o, body %q", before.Mode().Perm(), after.Mode().Perm(), raw)
86 }
87 if diagnosticStatus(report, "directory_replace_probe") != "passed" {
88 t.Fatalf("report = %+v", report)
89 }
90 // GitHub's elevated Windows runner creates temporary files owned by the
91 // built-in Administrators group, so repair correctly stays fail-closed even
92 // though the non-mutating directory probe remains valid there.
93 if runtime.GOOS != "windows" && (diagnosticStatus(report, "repair") != "passed" || len(report.Actions) == 0) {
94 t.Fatalf("report = %+v", report)
95 }
96 }
97
98 func TestCredentialDiagnosticsReportsPendingTransaction(t *testing.T) {
99 t.Setenv("REASONIX_HOME", t.TempDir())
100 path := UserConfigPath()
101 cfg := Default()
102 if err := cfg.SaveTo(path); err != nil {
103 t.Fatal(err)
104 }
105 unlockConfig := LockUserConfigEdits()
106 unlockCredentials, err := LockUserCredentialEdits()
107 if err != nil {
108 unlockConfig()
109 t.Fatal(err)
110 }
111 if err := cfg.BeginModelCredentialCommitLocked(path, "pending", "digest"); err != nil {
112 unlockCredentials()
113 unlockConfig()
114 t.Fatal(err)
115 }
116 unlockCredentials()
117 unlockConfig()
118 report, err := DiagnoseCredentials(CredentialDiagnosticOptions{})
119 if err != nil {
120 t.Fatal(err)
121 }
122 if report.PendingTransactions != 1 || diagnosticStatus(report, "transactions") != "failed" {
123 t.Fatalf("report = %+v", report)
124 }
125 }
126
126 lines GO