Files
mesh-catalog/modules/claude-code/cmd/claude-code/claude_code_test.go
T
jochen b91b4c427d Apply a binding that differs, and never repeat a generation a node passed
A licence store rebuilt after issue 241 counted generations from one again,
and nodes that apply only a higher number discarded the login move and the
rotations unseen. Nodes now apply any binding other than the one applied;
the manager moves its sequence past every generation a node reports. hq
issue 243.
2026-10-05 09:59:19 +02:00

464 lines
19 KiB
Go

package main
import (
"encoding/json"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"time"
)
var now = time.Now().UnixMilli()
func node(t *testing.T, name string) (Paths, map[string]string) {
t.Helper()
root := t.TempDir()
p := Paths{State: filepath.Join(root, "state"), Facts: filepath.Join(root, "state", "facts.json"),
Settings: filepath.Join(root, "state", "settings.json"), Home: filepath.Join(root, "home"), Node: name}
_ = os.MkdirAll(p.State, 0o700)
_ = os.MkdirAll(filepath.Join(p.Home, ".claude"), 0o700)
_ = os.WriteFile(p.Facts, []byte(`{"node":"`+name+`","console":"http://127.0.0.1:4270/mcp"}`), 0o600)
_ = os.WriteFile(p.Settings, []byte(`{"role":"","mcp_servers":{}}`), 0o600)
return p, map[string]string{}
}
func writer(w map[string]string) WriteManaged {
return func(name, content string) (string, error) { w[name] = content; return name + ": written", nil }
}
func writeFile(t *testing.T, path, content string) {
t.Helper()
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
t.Fatal(err)
}
}
func creds(t *testing.T, p Paths) map[string]any {
t.Helper()
var c map[string]any
raw, _ := os.ReadFile(p.credentials())
if err := json.Unmarshal(raw, &c); err != nil {
t.Fatal(err)
}
return c["claudeAiOauth"].(map[string]any)
}
// ---- the renderer, held to the TypeScript it replaced -------------------------------------------------
func TestTheRendererWritesWhatTheTypeScriptOneWrote(t *testing.T) {
raw, err := os.ReadFile("testdata/rendered-by-typescript.json")
if err != nil {
t.Fatal(err)
}
var f struct {
Facts Facts `json:"facts"`
Settings Settings `json:"settings"`
Registered Servers `json:"registered"`
WithKey map[string]string `json:"withKey"`
Plain map[string]string `json:"plain"`
}
if err := json.Unmarshal(raw, &f); err != nil {
t.Fatal(err)
}
same := func(label string, got, want map[string]string) {
if got["CLAUDE.md"] != want["CLAUDE.md"] {
t.Errorf("%s: CLAUDE.md differs from the TypeScript's:\n--- go\n%s\n--- typescript\n%s", label, got["CLAUDE.md"], want["CLAUDE.md"])
}
for _, file := range []string{"managed-mcp.json", "managed-settings.json"} {
var a, b any
_ = json.Unmarshal([]byte(got[file]), &a)
_ = json.Unmarshal([]byte(want[file]), &b)
if !reflect.DeepEqual(a, b) {
t.Errorf("%s: %s means something else:\n--- go\n%s\n--- typescript\n%s", label, file, got[file], want[file])
}
}
}
same("with an API key", Render(f.Facts, f.Settings, &Binding{Licence: "api", Kind: "api-key"}, "/state/api-key-helper", f.Registered), f.WithKey)
same("plain", Render(f.Facts, Settings{}, nil, "/h", Servers{}), f.Plain)
}
func TestASettingCannotReplaceTheMeshsOwnEntryAndABadNameIsLeftOut(t *testing.T) {
out := Render(Facts{Node: "w", Console: "http://127.0.0.1:4270/mcp"},
Settings{MCPServers: map[string]map[string]any{"mesh": {"type": "http", "url": "http://evil"}, "bad name": {}}}, nil, "/h", nil)
var mcp struct {
MCPServers map[string]map[string]any `json:"mcpServers"`
}
_ = json.Unmarshal([]byte(out["managed-mcp.json"]), &mcp)
if mcp.MCPServers["mesh"]["url"] != "http://127.0.0.1:4270/mcp" || mcp.MCPServers["bad name"] != nil {
t.Fatalf("%v", mcp.MCPServers)
}
if !reflect.DeepEqual(Render(Facts{Console: "x"}, Settings{}, nil, "/h", nil), Render(Facts{Console: "x"}, Settings{}, nil, "/h", nil)) {
t.Fatal("rendering is not deterministic")
}
}
func TestTheOperatorsManagedSettingsAreLaidUnderTheMeshsOwnKeys(t *testing.T) {
settings := Settings{ManagedSettings: map[string]any{
"autoMode": map[string]any{"allow": []any{"merging an approved pull request"}},
"permissions": map[string]any{"deny": []any{"Bash(rm -rf /)"}},
"attribution": map[string]any{"commit": "made by a machine"},
"allowAllClaudeAiMcps": false,
"apiKeyHelper": "/somewhere/else",
}}
read := func(binding *Binding) map[string]any {
var m map[string]any
out := Render(Facts{Console: "x"}, settings, binding, "/h", nil)
if err := json.Unmarshal([]byte(out["managed-settings.json"]), &m); err != nil {
t.Fatal(err)
}
return m
}
m := read(nil)
if allow := m["autoMode"].(map[string]any)["allow"].([]any); len(allow) != 1 || allow[0] != "merging an approved pull request" {
t.Errorf("the operator's auto mode was not carried: %v", m["autoMode"])
}
if m["permissions"] == nil {
t.Errorf("the operator's permissions were not carried: %v", m)
}
if !reflect.DeepEqual(m["attribution"], map[string]any{"commit": "", "pr": ""}) || m["allowAllClaudeAiMcps"] != true {
t.Errorf("a setting replaced the mesh's own keys: %v", m)
}
if _, ok := m["apiKeyHelper"]; ok {
t.Errorf("a setting named a key-helper the licence did not: %v", m)
}
if helper := read(&Binding{Licence: "api", Kind: "api-key"})["apiKeyHelper"]; helper != "/h" {
t.Errorf("an API-key licence's key-helper was replaced: %v", helper)
}
}
// ---- the credentials file -----------------------------------------------------------------------------
func i64(v int64) *int64 { return &v }
func TestTheLineageRules(t *testing.T) {
const hour = 3_600_000
g := func(at string, exp int64, rtExp int64) Grant {
return Grant{AccessToken: at, ExpiresAt: exp, RefreshTokenExpiresAt: i64(rtExp)}
}
month := now + 30*24*hour
if d := DecideApply(&Grant{AccessToken: "A", ExpiresAt: now + hour, RefreshTokenExpiresAt: i64(month)}, g("B", now+2*hour, month), false); !d.Apply {
t.Fatal("a newer rotation was refused")
}
if d := DecideApply(&Grant{AccessToken: "new", ExpiresAt: now + 2*hour, RefreshTokenExpiresAt: i64(month)}, g("old", now+hour, month), false); d.Apply || d.Reason != "not-newer" {
t.Fatalf("a late older rotation: %+v", d)
}
if d := DecideApply(&Grant{AccessToken: "A", ExpiresAt: now + 8*hour, RefreshTokenExpiresAt: i64(month)}, g("re", now+hour, now+5*24*hour), false); !d.Apply || !d.Reissued {
t.Fatalf("a re-issued grant: %+v", d)
}
if d := DecideApply(&Grant{AccessToken: "A", ExpiresAt: now + 8*hour}, g("other", now+hour, month), true); !d.Apply {
t.Fatal("a switch was refused")
}
if d := DecideApply(&Grant{AccessToken: "A"}, Grant{AccessToken: "A"}, true); d.Apply || d.Reason != "already-current" {
t.Fatalf("the same token: %+v", d)
}
}
func TestALoginIsSeenAndStrippedWhenTheNodesOwnGrantIsWritten(t *testing.T) {
p, _ := node(t, "laptop")
writeFile(t, p.credentials(), `{"claudeAiOauth":{"accessToken":"at-login","refreshToken":"rt-login","expiresAt":1700000000000},"other":1}`)
login := ReadCredentials(p.credentials())
if !HoldsLogin(login) {
t.Fatal("a login was not seen")
}
if err := WriteCredentials(p.credentials(), WithGrant(login, Grant{AccessToken: "at-mesh", ExpiresAt: 1, Scopes: []string{"user:inference"}})); err != nil {
t.Fatal(err)
}
back := ReadCredentials(p.credentials())
raw, _ := os.ReadFile(p.credentials())
info, _ := os.Stat(p.credentials())
if HoldsLogin(back) || GrantOf(back).AccessToken != "at-mesh" || back["other"] == nil || strings.Contains(string(raw), "rt-login") || info.Mode().Perm() != 0o600 {
t.Fatalf("written %s (mode %v)", raw, info.Mode())
}
}
func TestTheAccountIsReadFromTheAgentsStateFileAndNeverGuessed(t *testing.T) {
p, _ := node(t, "laptop")
writeFile(t, p.account(), `{"oauthAccount":{"accountUuid":"u-1","emailAddress":"a@example.org"},"other":2}`)
if id := ReadIdentity(p.account()); id == nil || id.AccountUUID != "u-1" || id.EmailAddress != "a@example.org" {
t.Fatalf("%+v", id)
}
if ReadIdentity("/nonexistent/.claude.json") != nil {
t.Fatal("an identity from nothing")
}
writeFile(t, p.account(), `{}`)
if ReadIdentity(p.account()) != nil {
t.Fatal("an identity from an empty file")
}
}
// ---- the licence, ADR 0206 ----------------------------------------------------------------------------
func TestWhatANodeHoldsIsReportedWithFingerprintsAndItsAccountNeverAToken(t *testing.T) {
p, _ := node(t, "laptop")
writeFile(t, p.credentials(), `{"claudeAiOauth":{"accessToken":"at-secret","refreshToken":"rt-secret","expiresAt":2000,"refreshTokenExpiresAt":9000}}`)
writeFile(t, p.account(), `{"oauthAccount":{"accountUuid":"u-1","emailAddress":"a@example.org"}}`)
h := HoldingsOf(p)
if h.Node != "laptop" || h.Identity.AccountUUID != "u-1" || *h.Kind != "subscription" || !h.Refresh.Present ||
!strings.HasPrefix(*h.Refresh.Fingerprint, "sha256:") || h.Access.ExpiresAt != 2000 || h.ChangedAt == nil {
t.Fatalf("%+v", h)
}
raw, _ := json.Marshal(h)
if strings.Contains(string(raw), "at-secret") || strings.Contains(string(raw), "rt-secret") {
t.Fatalf("a token is in the report: %s", raw)
}
var keys map[string]any
_ = json.Unmarshal(raw, &keys)
for k := range keys {
if strings.Contains(strings.ToLower(k), "token") || strings.Contains(strings.ToLower(k), "secret") {
t.Fatalf("a field the runtime would refuse: %s", k)
}
}
// The manager reads exactly this shape.
if _, err := time.Parse(time.RFC3339Nano, *h.ChangedAt); err != nil {
t.Fatalf("the manager cannot read the report's time: %v", err)
}
}
func TestTheGrantAnswersOnlyAWaitingLoginSealedToTheManagersKey(t *testing.T) {
p, _ := node(t, "laptop")
manager, _ := GenerateKeyPair()
if a, _ := GrantFor(p, manager.PublicKey); a.Sealed != nil || a.Waiting == nil || *a.Waiting {
t.Fatalf("%+v", a)
}
writeFile(t, p.credentials(), `{"claudeAiOauth":{"accessToken":"at","refreshToken":"rt-login","expiresAt":1}}`)
writeFile(t, p.account(), `{"oauthAccount":{"accountUuid":"u-9"}}`)
a, err := GrantFor(p, manager.PublicKey)
if err != nil || a.Identity.AccountUUID != "u-9" {
t.Fatalf("%+v %v", a, err)
}
plain, _ := Open(*a.Sealed, manager.PrivateKey)
if !strings.Contains(plain, `"refreshToken":"rt-login"`) {
t.Fatalf("opened %s", plain)
}
raw, _ := json.Marshal(a)
if strings.Contains(string(raw), "rt-login") {
t.Fatal("the refresh token crossed in the clear")
}
}
// seat answers `current` as the manager does: the grant sealed to the key the node sent.
func seat(t *testing.T, licence, token string, gen int64, asked *[]string) Ask {
return func(address string, args any) (json.RawMessage, error) {
*asked = append(*asked, address)
key := args.(map[string]any)["public_key"].(string)
g, _ := json.Marshal(Grant{AccessToken: token, ExpiresAt: now + 3_600_000})
box, err := Seal(string(g), key)
if err != nil {
t.Fatal(err)
}
return json.Marshal(Current{Licence: licence, Kind: "subscription", Generation: gen, Sealed: &box})
}
}
func TestANewerGenerationFetchesTheTokenOnceByTheSeatsVerb(t *testing.T) {
p, w := node(t, "laptop")
var asked []string
ask := seat(t, "personal", "at-1", 3, &asked)
if _, err := OnBinding(p, &BindingState{Licence: "personal", Kind: "subscription", Generation: 3}, ask, writer(w)); err != nil {
t.Fatal(err)
}
if len(asked) != 1 || asked[0] != "seat:anthropic-licence-manager.current" || creds(t, p)["accessToken"] != "at-1" {
t.Fatalf("asked %v, credentials %v", asked, creds(t, p))
}
if done, _ := OnBinding(p, &BindingState{Licence: "personal", Kind: "subscription", Generation: 3}, ask, writer(w)); done != "" || len(asked) != 1 {
t.Fatal("an equal generation asked again")
}
if HoldingsOf(p).Generation != 3 || w["managed-mcp.json"] == "" {
t.Fatal("the generation or the managed files were not written")
}
}
func TestTheTokenANodeIsHandedReplacesALoginsGrantAndLeavesNoRefreshToken(t *testing.T) {
p, w := node(t, "laptop")
writeFile(t, p.credentials(), `{"claudeAiOauth":{"accessToken":"at-old","refreshToken":"rt-spent","expiresAt":`+
strings.TrimSpace(string(mustJSON(now+7_200_000)))+`}}`)
var asked []string
out, err := Pull(p, seat(t, "personal", "at-new", 1, &asked), writer(w))
if err != nil || out["applied"] != true {
t.Fatalf("%v %v", out, err)
}
c := creds(t, p)
if c["accessToken"] != "at-new" || c["refreshToken"] != nil || HoldingsOf(p).Refresh.Present {
t.Fatalf("%v", c)
}
}
func mustJSON(v any) []byte { b, _ := json.Marshal(v); return b }
// ---- MCP servers in state, ADR 0201 -------------------------------------------------------------------
// bus is the `servers` state as every node in a test shares it, with each node's watch.
type bus struct {
kept map[string]map[string]any
watchers []func(ServerChange)
}
func (b *bus) Put(key string, value any) error {
v := value.(map[string]any)
b.kept[key] = v
for _, w := range b.watchers {
w(ServerChange{Key: key, Op: "put", Value: v})
}
return nil
}
func (b *bus) Delete(key string) error {
delete(b.kept, key)
for _, w := range b.watchers {
w(ServerChange{Key: key, Op: "delete"})
}
return nil
}
func (b *bus) Keys() ([]string, error) {
var out []string
for k := range b.kept {
out = append(out, k)
}
return out, nil
}
// join is a node joining: its view takes the current state, then every change.
func (b *bus) join(p Paths, w map[string]string) *ServerView {
v := NewServerView(p)
for k, val := range b.kept {
_, _ = OnServerChange(v, ServerChange{Key: k, Op: "put", Value: val}, p, writer(w))
}
b.watchers = append(b.watchers, func(c ServerChange) { _, _ = OnServerChange(v, c, p, writer(w)) })
return v
}
func noOthers() ([]string, error) { return nil, nil }
func TestRegisteringHerePutsItUnderThisNodesKeyAndAsksAboutTheOthers(t *testing.T) {
p, w := node(t, "laptop")
b := &bus{kept: map[string]map[string]any{}}
v := b.join(p, w)
r, err := RegisterServer(p, Registration{Name: "search", Entry: map[string]any{"type": "http", "url": "https://s.example/mcp"}}, b, v, writer(w),
func() ([]string, error) { return []string{"laptop", "server", "desktop"}, nil })
if err != nil || r["here"] != "changed" || !strings.Contains(r["also"].(string), "server, desktop") || b.kept["laptop.search"] == nil {
t.Fatalf("%v %v %v", r, err, b.kept)
}
if !strings.Contains(w["managed-mcp.json"], `"search"`) {
t.Fatal("not rendered")
}
}
func TestEveryNodeRegistrationReachesTheOthersAndALateNodeReadsIt(t *testing.T) {
a, wa := node(t, "laptop")
s, ws := node(t, "server")
b := &bus{kept: map[string]map[string]any{}}
va := b.join(a, wa)
b.join(s, ws)
_, _ = RegisterServer(a, Registration{Name: "docs", Entry: map[string]any{"type": "stdio", "command": "docs-mcp"}, Nodes: []string{"all"}}, b, va, writer(wa), noOthers)
if Registered(s)["docs"] == nil || !strings.Contains(ws["managed-mcp.json"], "docs-mcp") {
t.Fatalf("the other node did not take it: %v", Registered(s))
}
late, wl := node(t, "desktop")
b.join(late, wl)
if Registered(late)["docs"] == nil {
t.Fatal("a node joining later did not read the current set")
}
_, _ = RegisterServer(a, Registration{Name: "docs", Nodes: []string{"all"}}, b, va, writer(wa), noOthers)
if Registered(s)["docs"] != nil || Registered(late)["docs"] != nil {
t.Fatal("an unregistration did not reach every node")
}
}
func TestANodesOwnRegistrationOverridesTheOneForEveryNode(t *testing.T) {
a, wa := node(t, "laptop")
s, ws := node(t, "server")
b := &bus{kept: map[string]map[string]any{}}
va := b.join(a, wa)
b.join(s, ws)
_, _ = RegisterServer(a, Registration{Name: "x", Entry: map[string]any{"type": "http", "url": "https://all"}, Nodes: []string{"all"}}, b, va, writer(wa), noOthers)
_, _ = RegisterServer(a, Registration{Name: "x", Entry: map[string]any{"type": "http", "url": "https://laptop"}}, b, va, writer(wa), noOthers)
if Registered(a)["x"]["url"] != "https://laptop" || Registered(s)["x"]["url"] != "https://all" {
t.Fatalf("%v %v", Registered(a), Registered(s))
}
r, _ := RegisterServer(a, Registration{Name: "x"}, b, va, writer(wa), noOthers)
if !strings.Contains(r["still"].(string), "still applies here") || Registered(a)["x"]["url"] != "https://all" {
t.Fatalf("%v", r)
}
}
func TestABadEntryIsRefusedBeforeAnythingIsPut(t *testing.T) {
p, w := node(t, "laptop")
b := &bus{kept: map[string]map[string]any{}}
v := b.join(p, w)
r, _ := RegisterServer(p, Registration{Name: "mesh", Entry: map[string]any{"type": "http", "url": "https://x"}}, b, v, writer(w), noOthers)
if r["registered"] != false || len(b.kept) != 0 {
t.Fatalf("%v %v", r, b.kept)
}
if done, _ := OnServerChange(v, ServerChange{Key: "server.b", Op: "put", Value: map[string]any{"type": "http", "url": "https://b"}}, p, writer(w)); done != "" {
t.Fatal("another node's key changed this one")
}
}
// An API key goes to the manager sealed to its key, never in the clear, and its file is gone afterwards.
func TestAnAPIKeyIsHandedOverSealedAndItsFileRemoved(t *testing.T) {
p, _ := node(t, "laptop")
manager, _ := GenerateKeyPair()
file := filepath.Join(p.Home, "api-key")
writeFile(t, file, "sk-ant-api03-secret\n")
var sent []string
ask := func(address string, args any) (json.RawMessage, error) {
raw, _ := json.Marshal(args)
sent = append(sent, address+" "+string(raw))
switch address {
case "seat:anthropic-licence-manager.public-key":
return json.Marshal(map[string]any{"public_key": manager.PublicKey})
case "seat:anthropic-licence-manager.adopt":
box := args.(map[string]any)["sealed"].(SealedBox)
if key, err := Open(box, manager.PrivateKey); err != nil || key != "sk-ant-api03-secret" {
t.Fatalf("the manager opened %q, %v", key, err)
}
return json.Marshal(map[string]any{"adopted": true})
case "seat:anthropic-licence-manager.switch":
return json.Marshal(map[string]any{"licence": "api"})
}
t.Fatalf("asked %s", address)
return nil, nil
}
out, err := AddAPIKey(p, "api", file, true, ask)
if err != nil || out["file"] != "removed" || out["switched"] == nil {
t.Fatalf("%v %v", out, err)
}
if _, err := os.Stat(file); !os.IsNotExist(err) {
t.Fatal("the key file is still there")
}
if strings.Contains(strings.Join(sent, "\n"), "sk-ant") {
t.Fatalf("the key crossed in the clear: %v", sent)
}
}
// A manager whose store was rebuilt counts generations from one again (novox/hq issue 243): a binding with a
// lower generation than the one applied is still a binding to apply, and only the one applied is skipped.
func TestALowerGenerationAfterTheManagerWasRebuiltIsStillApplied(t *testing.T) {
p, w := node(t, "laptop")
var asked []string
if _, err := OnBinding(p, &BindingState{Licence: "personal", Kind: "subscription", Generation: 16},
seat(t, "personal", "at-old", 16, &asked), writer(w)); err != nil {
t.Fatal(err)
}
// The rotation that came with it: a later token, as a refresh hands one over.
later := func(address string, args any) (json.RawMessage, error) {
asked = append(asked, address)
g, _ := json.Marshal(Grant{AccessToken: "at-new", ExpiresAt: now + 7_200_000})
box, err := Seal(string(g), args.(map[string]any)["public_key"].(string))
if err != nil {
t.Fatal(err)
}
return json.Marshal(Current{Licence: "personal", Kind: "subscription", Generation: 3, Sealed: &box})
}
if _, err := OnBinding(p, &BindingState{Licence: "personal", Kind: "subscription", Generation: 3},
later, writer(w)); err != nil {
t.Fatal(err)
}
if len(asked) != 2 || creds(t, p)["accessToken"] != "at-new" || HoldingsOf(p).Generation != 3 {
t.Fatalf("asked %v, credentials %v: a lower generation was ignored", asked, creds(t, p))
}
}