Skip to content

Commit ca0dbf0

Browse files
committed
test: prove composed profile isolation
1 parent a938d06 commit ca0dbf0

1 file changed

Lines changed: 133 additions & 0 deletions

File tree

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package server
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
"testing"
10+
"time"
11+
12+
"github.com/agensfield/scriba/internal/agentcontext"
13+
"github.com/agensfield/scriba/internal/model"
14+
"github.com/agensfield/scriba/internal/remote"
15+
"github.com/agensfield/scriba/internal/resetwatch"
16+
"github.com/agensfield/scriba/internal/server/store"
17+
)
18+
19+
type composedAuthFixture struct {
20+
Token string `json:"token"`
21+
AccountID string `json:"accountId"`
22+
Used float64 `json:"used"`
23+
}
24+
25+
type composedAuthFetcher struct{ paths map[string]string }
26+
27+
func (f *composedAuthFetcher) FetchLimits(context.Context) (remote.ProbeResult, error) {
28+
return remote.ProbeResult{}, ErrProfileAuthPaths
29+
}
30+
31+
func (f *composedAuthFetcher) FetchProfileLimits(_ context.Context, profile Profile) (remote.ProbeResult, error) {
32+
if len(profile.AuthPaths) != 1 {
33+
return remote.ProbeResult{}, ErrProfileAuthPaths
34+
}
35+
path := profile.AuthPaths[0]
36+
f.paths[profile.Ref] = path
37+
raw, err := os.ReadFile(path) // #nosec G304 -- test reads the explicit fixture path under t.TempDir.
38+
if err != nil {
39+
return remote.ProbeResult{}, err
40+
}
41+
var fixture composedAuthFixture
42+
if err := json.Unmarshal(raw, &fixture); err != nil {
43+
return remote.ProbeResult{}, err
44+
}
45+
reset := time.Date(2026, 7, 20, 3, 0, 0, 0, time.UTC).Format(time.RFC3339)
46+
return remote.ProbeResult{
47+
ProviderID: "codex",
48+
Lines: []model.MetricLine{{Type: "progress", Label: resetwatch.LabelWeeklyLimit, Used: &fixture.Used, ResetsAt: reset}},
49+
AuthState: remote.AuthState{OK: true, Source: path, Error: "PRIVATE_DIAGNOSTIC", AccessToken: fixture.Token, AccountID: fixture.AccountID},
50+
}, nil
51+
}
52+
53+
func TestComposedTwoAuthProfilesRemainIsolatedAndPrivate(t *testing.T) {
54+
ctx := context.Background()
55+
dir := t.TempDir()
56+
writeFixture := func(name, token, account string, used float64) string {
57+
t.Helper()
58+
path := filepath.Join(dir, name+".json")
59+
raw, _ := json.Marshal(composedAuthFixture{Token: token, AccountID: account, Used: used})
60+
if err := os.WriteFile(path, raw, 0o600); err != nil {
61+
t.Fatal(err)
62+
}
63+
return path
64+
}
65+
personalPath := writeFixture("personal-auth", "PRIVATE_TOKEN_PERSONAL", "acct-personal", 21)
66+
workPath := writeFixture("work-auth", "PRIVATE_TOKEN_WORK", "acct-work", 82)
67+
profiles := []Profile{
68+
{Ref: "personal", Label: "Personal", AuthPaths: []string{personalPath}, Default: true},
69+
{Ref: "work", Label: "Work", AuthPaths: []string{workPath}},
70+
}
71+
statePath := filepath.Join(dir, "state.sqlite")
72+
st := openStoreAt(t, statePath)
73+
syncRuntimeProfiles(t, st, profiles)
74+
fetcher := &composedAuthFetcher{paths: map[string]string{}}
75+
srv := New(st, fetcher, nil, Config{Profiles: profiles})
76+
result, err := srv.RefreshProfilesNow(ctx)
77+
if err != nil {
78+
t.Fatal(err)
79+
}
80+
if len(result.Profiles) != 2 || result.Profiles[0].Observation.Account.Ref != "acct-personal" || result.Profiles[1].Observation.Account.Ref != "acct-work" {
81+
t.Fatalf("results=%+v", result.Profiles)
82+
}
83+
if fetcher.paths["personal"] != personalPath || fetcher.paths["work"] != workPath {
84+
t.Fatalf("paths=%+v", fetcher.paths)
85+
}
86+
87+
svc := agentcontext.New(agentcontext.Config{CacheDir: filepath.Join(dir, "missing-cache"), StorePath: statePath, DefaultProfileID: "personal", ProfileIDs: []string{"personal", "work"}})
88+
for profile, wantUsed := range map[string]float64{"personal": 21, "work": 82} {
89+
got, err := svc.ContextForProfile(ctx, profile)
90+
if err != nil {
91+
t.Fatal(err)
92+
}
93+
raw, err := json.Marshal(got)
94+
if err != nil {
95+
t.Fatal(err)
96+
}
97+
if !strings.Contains(string(raw), `"profileId":"`+profile+`"`) || !strings.Contains(string(raw), `"usedPercent":`+formatUsed(wantUsed)) {
98+
t.Fatalf("%s context=%s", profile, raw)
99+
}
100+
for _, private := range []string{"acct-personal", "acct-work", "PRIVATE_TOKEN", personalPath, workPath, "PRIVATE_DIAGNOSTIC"} {
101+
if strings.Contains(string(raw), private) {
102+
t.Fatalf("%s leaked %q in %s", profile, private, raw)
103+
}
104+
}
105+
}
106+
107+
if err := st.Close(); err != nil {
108+
t.Fatal(err)
109+
}
110+
database, err := os.ReadFile(statePath)
111+
if err != nil {
112+
t.Fatal(err)
113+
}
114+
for _, private := range []string{"PRIVATE_TOKEN_PERSONAL", "PRIVATE_TOKEN_WORK", personalPath, workPath, "PRIVATE_DIAGNOSTIC"} {
115+
if strings.Contains(string(database), private) {
116+
t.Fatalf("database contains private fixture %q", private)
117+
}
118+
}
119+
}
120+
121+
func formatUsed(value float64) string {
122+
raw, _ := json.Marshal(value)
123+
return string(raw)
124+
}
125+
126+
func openStoreAt(t *testing.T, path string) *store.Store {
127+
t.Helper()
128+
st, err := store.Open(path)
129+
if err != nil {
130+
t.Fatal(err)
131+
}
132+
return st
133+
}

0 commit comments

Comments
 (0)