返回 DeepSeek-Reasonix
credential_key_claim_test.go
根目录 / internal / config / credential_key_claim_test.go
1 package config
2
3 import (
4 "errors"
5 "os"
6 "path/filepath"
7 "testing"
8 )
9
10 func unsetForTest(t *testing.T, key string) {
11 t.Helper()
12 t.Setenv(key, "") // registers the restore; staging pins the key into the process
13 _ = os.Unsetenv(key)
14 }
15
16 func stageNamedAndStop(t *testing.T, path, key, value string) *Config {
17 t.Helper()
18 unsetForTest(t, key)
19 unlockConfig := LockUserConfigEdits()
20 defer unlockConfig()
21 unlockCredentials, err := LockUserCredentialEdits()
22 if err != nil {
23 t.Fatal(err)
24 }
25 defer unlockCredentials()
26 cfg, err := LoadForEditReadOnlyStrict(path)
27 if err != nil {
28 t.Fatal(err)
29 }
30 if err := cfg.BeginModelCredentialCommitLocked(path, "cli-setup"); err != nil {
31 t.Fatal(err)
32 }
33 if _, err := cfg.StageNamedModelCredentialLocked(key, "p", value); err != nil {
34 t.Fatal(err)
35 }
36 return cfg
37 }
38
39 func recoverLocked(t *testing.T, path string) {
40 t.Helper()
41 unlockConfig := LockUserConfigEdits()
42 defer unlockConfig()
43 unlockCredentials, err := LockUserCredentialEdits()
44 if err != nil {
45 t.Fatal(err)
46 }
47 defer unlockCredentials()
48 if err := RecoverModelCredentialCommitsLocked(path); err != nil {
49 t.Fatal(err)
50 }
51 }
52
53 func journalCount(t *testing.T) int {
54 t.Helper()
55 entries, err := os.ReadDir(modelCredentialTransactionDir())
56 if os.IsNotExist(err) {
57 return 0
58 }
59 if err != nil {
60 t.Fatal(err)
61 }
62 n := 0
63 for _, e := range entries {
64 if filepath.Ext(e.Name()) == ".json" {
65 n++
66 }
67 }
68 return n
69 }
70
71 func TestRecoveryKeepsANamedSlotAnotherWriterTookOver(t *testing.T) {
72 isolateUserConfigHome(t)
73 path := UserConfigPath()
74 if err := Default().SaveTo(path); err != nil {
75 t.Fatal(err)
76 }
77 stageNamedAndStop(t, path, "TEAM_KEY", "sk-mine")
78 if _, err := SetCredential("TEAM_KEY", "sk-team-owned-elsewhere"); err != nil {
79 t.Fatal(err)
80 }
81 recoverLocked(t, path)
82 if value, _ := envFileValue(UserCredentialsPath(), "TEAM_KEY"); value != "sk-team-owned-elsewhere" {
83 t.Fatalf("recovery removed a value it did not stage: TEAM_KEY=%q", value)
84 }
85 }
86
87 func TestRecoveryStillRemovesAnUnpublishedNamedSlot(t *testing.T) {
88 isolateUserConfigHome(t)
89 path := UserConfigPath()
90 if err := Default().SaveTo(path); err != nil {
91 t.Fatal(err)
92 }
93 stageNamedAndStop(t, path, "TEAM_KEY", "sk-mine")
94 recoverLocked(t, path)
95 if CredentialStored("TEAM_KEY") || journalCount(t) != 0 {
96 t.Fatalf("unpublished slot stored=%v journals=%d, want both gone", CredentialStored("TEAM_KEY"), journalCount(t))
97 }
98 }
99
100 func TestCleanupReadsReferencesStructurallyNotBySubstring(t *testing.T) {
101 isolateUserConfigHome(t)
102 path := UserConfigPath()
103 cfg := Default()
104 cfg.Providers = []ProviderEntry{{Name: "deep", Kind: "openai", BaseURL: "https://deep.invalid/v1", Model: "chat", APIKeyEnv: "DEEPSEEK_API_KEY"}}
105 if err := cfg.SaveTo(path); err != nil {
106 t.Fatal(err)
107 }
108 staged := stageNamedAndStop(t, path, "API_KEY", "sk-short")
109 func() {
110 unlockConfig := LockUserConfigEdits()
111 defer unlockConfig()
112 unlockCredentials, err := LockUserCredentialEdits()
113 if err != nil {
114 t.Fatal(err)
115 }
116 defer unlockCredentials()
117 staged.CleanupStagedModelCredentialsLocked(path)
118 }()
119 if CredentialStored("API_KEY") || journalCount(t) != 0 {
120 t.Fatalf("API_KEY stored=%v journals=%d: a substring of DEEPSEEK_API_KEY kept the orphan", CredentialStored("API_KEY"), journalCount(t))
121 }
122 }
123
124 func TestClaimableRefusesNamesOtherReadersHold(t *testing.T) {
125 isolateUserConfigHome(t)
126 t.Setenv("OPENAI_API_KEY", "sk-shell")
127 cfg := Default()
128 cfg.Remote.Hosts = append(cfg.Remote.Hosts, RemoteHostEntry{PasswordEnv: "BOX_PASSWORD"})
129 cases := map[string]CredentialKeyHolder{
130 cfg.Bot.QQ.AppSecretEnv: CredentialKeyHeldBySetting,
131 "BOX_PASSWORD": CredentialKeyHeldBySetting,
132 "PATH": CredentialKeyHeldByEnvironment,
133 "OPENAI_API_KEY": CredentialKeyHeldByEnvironment,
134 }
135 for key, want := range cases {
136 err := cfg.CredentialKeyClaimable(key, "p")
137 var inUse *CredentialKeyInUseError
138 if !errors.As(err, &inUse) || inUse.Holder != want {
139 t.Errorf("CredentialKeyClaimable(%q) = %v, want holder %d", key, err, want)
140 }
141 }
142 }
143
143 lines GO