package main import ( "strings" "testing" "time" "sourcecraft.dev/bigbes/sr-ht-spec/db" ) func TestParseTokenIDRejectsWhatIsNotAnID(t *testing.T) { for _, in := range []string{"", "0", "-1", "3.0", "abc", " 3", "3 "} { if _, err := parseTokenID(in); err == nil { t.Errorf("parseTokenID(%q) was accepted", in) } } id, err := parseTokenID("42") if err != nil { t.Fatalf("parseTokenID(\"42\"): %v", err) } if id != 42 { t.Errorf("parseTokenID(\"42\") = %d want 42", id) } } // TestParseTokenIDPointsAtTheListing keeps the failure actionable: an operator // who mistyped an id needs to be told where the ids come from, not just that // this one was wrong. func TestParseTokenIDPointsAtTheListing(t *testing.T) { _, err := parseTokenID("nope") if err == nil { t.Fatal("parseTokenID accepted a non-numeric id") } if !strings.Contains(err.Error(), "specsrht token list") { t.Errorf("the error does not say how to find the ids:\n%v", err) } } func TestFormatTokenRow(t *testing.T) { created := time.Date(2026, 8, 5, 9, 30, 0, 0, time.UTC) revoked := created.Add(24 * time.Hour) active := formatTokenRow(&db.AgentToken{ID: 1, Name: "claude", Created: created}) if want := "1\tclaude\t2026-08-05T09:30:00Z\tactive"; active != want { t.Errorf("active row = %q want %q", active, want) } dead := formatTokenRow(&db.AgentToken{ID: 2, Name: "old", Created: created, Revoked: &revoked}) if want := "2\told\t2026-08-05T09:30:00Z\trevoked 2026-08-06T09:30:00Z"; dead != want { t.Errorf("revoked row = %q want %q", dead, want) } } // TestFormatTokenRowKeepsTheHashOut guards the one thing this listing must not // leak into an operator's terminal or scrollback: the stored hash is not a // credential and printing it invites treating it as one. func TestFormatTokenRowKeepsTheHashOut(t *testing.T) { row := formatTokenRow(&db.AgentToken{ ID: 3, Name: "agent", Hash: db.HashToken("s3cret"), Created: time.Date(2026, 8, 5, 9, 30, 0, 0, time.UTC), }) if strings.Contains(row, "[") || strings.ContainsAny(row, "%") { t.Errorf("the row appears to render the hash bytes: %q", row) } if strings.Contains(row, string(db.HashToken("s3cret"))) { t.Errorf("the row carries the stored hash: %q", row) } } // TestRunTokenRejectsBadInvocationsBeforeTheDatabase guards the ordering that // makes this command usable on a workstation: a usage error must be reported // without a config file or a Postgres connection, which is what an operator // typing it blind has. func TestRunTokenRejectsBadInvocationsBeforeTheDatabase(t *testing.T) { err := runToken(nil) if err == nil { t.Fatal("runToken accepted an empty argument list") } if err.Error() != tokenUsage { t.Errorf("empty invocation did not print the usage line:\n%v", err) } }