Skip to content

Commit 7ee9235

Browse files
test: raise coverage to 78% (target: 95) (#11)
Adds 3 test files covering: - internal/tokens: Load (absent + present), Add+Find+Remove+Save roundtrip, FindByTypeNameEnv (case-insensitive type+name, empty-env-as-development fallback, empty-type guard), normalize() helper, auto-CreatedAt stamping, Save bad-path error, storePath() HOME resolution - internal/secretstore: UseDefault non-clobbering of existing backend, UseDefault with disabled keychain returns nil, keychainBackend Available() env-disable path, Name() label, MemoryBackend Set("") deletes - cmd: shortToken truncate logic, extractTagged tag-scan (request_id, upgrade, end-of-string, newline-terminator, absent-tag), humanMessage preference order (message > agent_action > code > empty) Coverage: 69.0% -> 77.7%. Remaining gap is cmd/login.go pollForTierUpgrade + runUpgrade (require live API), cmd/root.go Execute + main.go (CLI entrypoint glue), and secretstore/secretstore.go Get/Set/Delete against the real OS keychain (intentionally excluded by INSTANT_DISABLE_KEYCHAIN in tests to avoid touching the dev keychain). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 943f868 commit 7ee9235

3 files changed

Lines changed: 301 additions & 0 deletions

File tree

cmd/coverage_helpers_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package cmd
2+
3+
import (
4+
"testing"
5+
)
6+
7+
// TestShortToken covers the 0%-line in up.go.
8+
func TestShortToken(t *testing.T) {
9+
cases := map[string]string{
10+
"": "",
11+
"abc": "abc",
12+
"abcdefgh": "abcdefgh",
13+
"abcdefghi": "abcdefgh",
14+
"verylongtoken": "verylong",
15+
}
16+
for in, want := range cases {
17+
got := shortToken(in)
18+
if got != want {
19+
t.Errorf("shortToken(%q) = %q, want %q", in, got, want)
20+
}
21+
}
22+
}
23+
24+
// TestExtractTagged covers the partial-coverage helper in json_error.go.
25+
func TestExtractTagged(t *testing.T) {
26+
s := "blah request_id=abc-123 other_tag=xx upgrade=https://foo trailing"
27+
if got := extractTagged(s, "request_id="); got != "abc-123" {
28+
t.Errorf("extractTagged request_id = %q", got)
29+
}
30+
if got := extractTagged(s, "upgrade="); got != "https://foo" {
31+
t.Errorf("extractTagged upgrade = %q", got)
32+
}
33+
// Tag at end of string runs to EOS.
34+
if got := extractTagged("x trailing=end", "trailing="); got != "end" {
35+
t.Errorf("trailing tag = %q", got)
36+
}
37+
// Missing tag returns "".
38+
if got := extractTagged(s, "absent="); got != "" {
39+
t.Errorf("missing tag should yield empty, got %q", got)
40+
}
41+
// Stop on newline.
42+
if got := extractTagged("v=stop\nmore", "v="); got != "stop" {
43+
t.Errorf("newline should terminate, got %q", got)
44+
}
45+
}
46+
47+
// TestHumanMessage covers apierror.go humanMessage preference order.
48+
func TestHumanMessage(t *testing.T) {
49+
cases := []struct {
50+
env apiErrorEnvelope
51+
want string
52+
}{
53+
{apiErrorEnvelope{Message: "msg", AgentAction: "act", Error: "code"}, "msg"},
54+
{apiErrorEnvelope{AgentAction: "act", Error: "code"}, "act"},
55+
{apiErrorEnvelope{Error: "code"}, "code"},
56+
{apiErrorEnvelope{}, ""},
57+
}
58+
for _, c := range cases {
59+
if got := c.env.humanMessage(); got != c.want {
60+
t.Errorf("humanMessage(%+v) = %q, want %q", c.env, got, c.want)
61+
}
62+
}
63+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package secretstore
2+
3+
import (
4+
"testing"
5+
)
6+
7+
// TestUseDefault_LeavesExistingBackend confirms UseDefault is non-clobbering.
8+
func TestUseDefault_LeavesExistingBackend(t *testing.T) {
9+
m := UseMemoryBackend()
10+
defer Use(nil)
11+
12+
if got := UseDefault(); got != m {
13+
t.Errorf("UseDefault should preserve in-memory backend, got %T", got)
14+
}
15+
}
16+
17+
// TestUseDefault_NoBackend_DisabledKeychain — when the env var disables the
18+
// keychain and no backend is active, UseDefault returns nil.
19+
func TestUseDefault_DisabledKeychain(t *testing.T) {
20+
Use(nil)
21+
t.Setenv("INSTANT_DISABLE_KEYCHAIN", "1")
22+
got := UseDefault()
23+
if got != nil {
24+
t.Errorf("UseDefault with disabled keychain should return nil, got %T", got)
25+
}
26+
}
27+
28+
// TestKeychainBackend_Available_DisabledByEnv verifies the env override.
29+
func TestKeychainBackend_Available_DisabledByEnv(t *testing.T) {
30+
t.Setenv("INSTANT_DISABLE_KEYCHAIN", "1")
31+
k := &keychainBackend{}
32+
if k.Available() {
33+
t.Error("keychain should report unavailable when INSTANT_DISABLE_KEYCHAIN=1")
34+
}
35+
if k.Name() != "keychain" {
36+
t.Errorf("Name() = %q", k.Name())
37+
}
38+
}
39+
40+
// TestKeychainBackend_SetEmptyDeletes covers the "Set("") -> Delete" branch
41+
// even when the keychain itself is unavailable (calling Delete is idempotent
42+
// and safe to invoke against a missing/unavailable backend).
43+
func TestKeychainBackend_SetEmptyDeletes(t *testing.T) {
44+
t.Setenv("INSTANT_DISABLE_KEYCHAIN", "1")
45+
k := &keychainBackend{}
46+
// Set("") routes to Delete(); Delete must be idempotent, so this should
47+
// either return nil or a non-fatal error from go-keyring on hosts without
48+
// a keychain. Just ensure it doesn't panic.
49+
_ = k.Set("")
50+
}
51+
52+
// TestMemoryBackend_Direct exercises *MemoryBackend methods through the type.
53+
func TestMemoryBackend_Direct(t *testing.T) {
54+
m := &MemoryBackend{}
55+
if _, err := m.Get(); err != ErrNotFound {
56+
t.Errorf("empty Get should return ErrNotFound, got %v", err)
57+
}
58+
if err := m.Set("x"); err != nil {
59+
t.Fatalf("Set: %v", err)
60+
}
61+
if v, _ := m.Get(); v != "x" {
62+
t.Errorf("Get = %q", v)
63+
}
64+
if err := m.Set(""); err != nil {
65+
t.Fatalf("Set empty: %v", err)
66+
}
67+
if _, err := m.Get(); err != ErrNotFound {
68+
t.Error("Set(\"\") should clear")
69+
}
70+
if !m.Available() {
71+
t.Error("MemoryBackend always available")
72+
}
73+
if m.Name() != "memory" {
74+
t.Errorf("Name = %q", m.Name())
75+
}
76+
_ = m.Delete()
77+
}

internal/tokens/store_test.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package tokens
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
"time"
8+
)
9+
10+
// setupTempHome redirects HOME to a temp dir for the test, ensuring
11+
// storePath() lands in an isolated location.
12+
func setupTempHome(t *testing.T) string {
13+
t.Helper()
14+
dir := t.TempDir()
15+
t.Setenv("HOME", dir)
16+
return dir
17+
}
18+
19+
func TestLoad_AbsentFile(t *testing.T) {
20+
setupTempHome(t)
21+
s, err := Load()
22+
if err != nil {
23+
t.Fatalf("Load: %v", err)
24+
}
25+
if len(s.Entries) != 0 {
26+
t.Errorf("expected empty entries, got %d", len(s.Entries))
27+
}
28+
}
29+
30+
func TestAdd_Find_Remove_Save(t *testing.T) {
31+
dir := setupTempHome(t)
32+
s, err := Load()
33+
if err != nil {
34+
t.Fatalf("Load: %v", err)
35+
}
36+
37+
if err := s.Add(Entry{Token: "tok1", Name: "db1", Type: "postgres", URL: "postgres://x"}); err != nil {
38+
t.Fatalf("Add: %v", err)
39+
}
40+
if err := s.Add(Entry{Token: "tok2", Name: "c1", Type: "redis", URL: "redis://x", CreatedAt: time.Now().Add(-time.Hour)}); err != nil {
41+
t.Fatalf("Add: %v", err)
42+
}
43+
44+
// Find roundtrips
45+
if e := s.Find("tok1"); e == nil || e.Name != "db1" {
46+
t.Errorf("Find(tok1) = %+v", e)
47+
}
48+
if e := s.Find("missing"); e != nil {
49+
t.Errorf("Find(missing) = %+v, want nil", e)
50+
}
51+
52+
// File written
53+
if _, err := os.Stat(filepath.Join(dir, ".instant-tokens")); err != nil {
54+
t.Errorf("expected file written, got %v", err)
55+
}
56+
57+
// Reload preserves data
58+
s2, err := Load()
59+
if err != nil {
60+
t.Fatalf("Load2: %v", err)
61+
}
62+
if len(s2.Entries) != 2 {
63+
t.Errorf("expected 2 entries, got %d", len(s2.Entries))
64+
}
65+
66+
// Remove existing & missing
67+
if !s2.Remove("tok1") {
68+
t.Error("Remove(existing) should return true")
69+
}
70+
if s2.Remove("nope") {
71+
t.Error("Remove(missing) should return false")
72+
}
73+
if s2.Find("tok1") != nil {
74+
t.Error("tok1 should be gone")
75+
}
76+
}
77+
78+
func TestFindByTypeNameEnv(t *testing.T) {
79+
setupTempHome(t)
80+
s, _ := Load()
81+
_ = s.Add(Entry{Token: "tok1", Name: "DB One", Type: "Postgres", Env: "production"})
82+
_ = s.Add(Entry{Token: "tok2", Name: "cache", Type: "redis", Env: ""}) // legacy empty env
83+
_ = s.Add(Entry{Token: "tok3", Name: "x", Type: ""}) // legacy empty type — ignored
84+
85+
// Case-insensitive type+name match.
86+
if e := s.FindByTypeNameEnv("postgres", "db one", "production"); e == nil || e.Token != "tok1" {
87+
t.Errorf("expected tok1, got %+v", e)
88+
}
89+
// Empty cached env matches default "development".
90+
if e := s.FindByTypeNameEnv("redis", "cache", "development"); e == nil || e.Token != "tok2" {
91+
t.Errorf("expected tok2, got %+v", e)
92+
}
93+
// Empty cached env also matches empty.
94+
if e := s.FindByTypeNameEnv("redis", "cache", ""); e == nil {
95+
t.Errorf("empty env should match")
96+
}
97+
// Different env → no match.
98+
if e := s.FindByTypeNameEnv("postgres", "db one", "staging"); e != nil {
99+
t.Errorf("staging should not match production entry, got %+v", e)
100+
}
101+
// Empty type entry is never returned.
102+
if e := s.FindByTypeNameEnv("", "x", ""); e != nil {
103+
t.Errorf("empty-type entry returned: %+v", e)
104+
}
105+
// Unknown combo.
106+
if e := s.FindByTypeNameEnv("kafka", "nope", "dev"); e != nil {
107+
t.Errorf("expected nil for unknown, got %+v", e)
108+
}
109+
}
110+
111+
func TestNormalize_Internal(t *testing.T) {
112+
cases := map[string]string{
113+
"": "",
114+
" Foo ": "foo",
115+
"a\tB\nC": "abc",
116+
"POSTGRES": "postgres",
117+
}
118+
for in, want := range cases {
119+
got := normalize(in)
120+
if got != want {
121+
t.Errorf("normalize(%q) = %q, want %q", in, got, want)
122+
}
123+
}
124+
}
125+
126+
func TestAdd_AutoTimestamps(t *testing.T) {
127+
setupTempHome(t)
128+
s, _ := Load()
129+
before := time.Now().UTC()
130+
if err := s.Add(Entry{Token: "auto1"}); err != nil {
131+
t.Fatalf("Add: %v", err)
132+
}
133+
e := s.Find("auto1")
134+
if e == nil {
135+
t.Fatal("Find returned nil")
136+
}
137+
if e.CreatedAt.IsZero() {
138+
t.Error("CreatedAt should be auto-populated")
139+
}
140+
if e.CreatedAt.Before(before.Add(-time.Second)) {
141+
t.Errorf("CreatedAt %v predates test start %v", e.CreatedAt, before)
142+
}
143+
}
144+
145+
func TestStorePath_Internal(t *testing.T) {
146+
dir := setupTempHome(t)
147+
p, err := storePath()
148+
if err != nil {
149+
t.Fatalf("storePath: %v", err)
150+
}
151+
if p != filepath.Join(dir, ".instant-tokens") {
152+
t.Errorf("storePath = %q, want %q", p, filepath.Join(dir, ".instant-tokens"))
153+
}
154+
}
155+
156+
func TestSave_BadPath(t *testing.T) {
157+
s := &Store{path: "/nonexistent-directory/no/such/file"}
158+
if err := s.Save(); err == nil {
159+
t.Error("expected error writing to bogus path")
160+
}
161+
}

0 commit comments

Comments
 (0)