package main
import (
"context"
"errors"
"strings"
"testing"
"time"
"craftdesign.group/quarry/internal/api"
"craftdesign.group/quarry/internal/errlog"
"craftdesign.group/quarry/internal/oltp"
)
type fakePlane struct {
subs []string
topics map[string]string
added map[string][]string
removed map[string][]string
failSub string
}
func newFakePlane() *fakePlane {
return &fakePlane{topics: map[string]string{},
added: map[string][]string{}, removed: map[string][]string{}}
}
func (f *fakePlane) EnsureSubscriber(_ context.Context, account string) error {
if account == f.failSub {
return errors.New("notify is not holding this one")
}
f.subs = append(f.subs, account)
return nil
}
func (f *fakePlane) EnsureTopic(_ context.Context, key, name string) error {
f.topics[key] = name
return nil
}
func (f *fakePlane) AddTopicMember(_ context.Context, key, account string) error {
f.added[key] = append(f.added[key], account)
return nil
}
func (f *fakePlane) RemoveTopicMember(_ context.Context, key, account string) error {
f.removed[key] = append(f.removed[key], account)
return nil
}
type fakeBook struct {
accounts []oltp.Account
grants map[string]map[string]oltp.Role
repos []string
}
func (b fakeBook) Repos() ([]oltp.Repo, error) {
out := make([]oltp.Repo, 0, len(b.repos))
for _, n := range b.repos {
out = append(out, oltp.Repo{Name: n})
}
return out, nil
}
func (b fakeBook) Accounts() ([]oltp.Account, error) { return b.accounts, nil }
func (b fakeBook) GrantsFor(account string) (map[string]oltp.Role, error) {
return b.grants[account], nil
}
func has(list []string, want string) bool {
for _, s := range list {
if s == want {
return true
}
}
return false
}
// The sweep makes notify match the forge: every account is a subscriber,
// the admin group holds today's admins and nobody else, and a grant is a
// membership of that repository's topic — whether or not the write that
// created it ever provisioned anything.
func TestReconcileMatchesAccountsAndGrants(t *testing.T) {
np := newFakePlane()
book := fakeBook{
accounts: []oltp.Account{
{Name: "dmestas", Admin: true},
{Name: "lou"},
{Name: "ghost", Admin: true, Disabled: "2026-09-01T00:00:00"},
},
grants: map[string]map[string]oltp.Role{
"lou": {"quarry": oltp.Read, "Synthetic": oltp.Read},
"ghost": {"quarry": oltp.Read},
},
// quarry itself is granted to nobody: its only members are site
// admins, which is exactly the shape that had no topic.
repos: []string{"quarry", "Synthetic", "fossil"},
}
sync, err := reconcileNotify(context.Background(), np, book)
if err != nil {
t.Fatalf("FAIL: %v", err)
}
for _, name := range []string{"dmestas", "lou", "ghost"} {
if !has(np.subs, name) {
t.Errorf("FAIL: %s was not ensured as a subscriber", name)
}
}
if !has(np.added[adminsTopic], "dmestas") {
t.Error("FAIL: the admin did not join the admin group")
}
if !has(np.removed[adminsTopic], "lou") {
t.Error("FAIL: a non-admin was left in the admin group")
}
if !has(np.removed[adminsTopic], "ghost") || has(np.added[adminsTopic], "ghost") {
t.Error("FAIL: a disabled admin still receives fleet alerts")
}
if _, ok := np.topics[adminsTopic]; !ok {
t.Error("FAIL: the admin group topic was never created")
}
// A grant is a membership, and the key is the repository's, never
// the raw name: notify's keys are lowercase and punctuation-free,
// so "Synthetic" and "synthetic" must not become two topics.
if _, ok := np.topics[repoTopicKey("quarry")]; !ok {
t.Error("FAIL: the repository's activity topic was never created")
}
if !has(np.added[repoTopicKey("quarry")], "lou") {
t.Error("FAIL: a granted account did not join the repository topic")
}
if !has(np.added[repoTopicKey("Synthetic")], "lou") {
t.Error("FAIL: the second grant was skipped")
}
// A shut account keeps its rows in the forge; it must not keep its
// notifications.
if has(np.added[repoTopicKey("quarry")], "ghost") {
t.Error("FAIL: a disabled account was subscribed to repository activity")
}
// A repository nobody was granted still has somewhere to deliver.
if _, ok := np.topics[repoTopicKey("fossil")]; !ok {
t.Error("FAIL: a repository with no grants got no topic")
}
// The site admin holds every repository, so they are in every topic.
for _, repo := range []string{"quarry", "Synthetic", "fossil"} {
if !has(np.added[repoTopicKey(repo)], "dmestas") {
t.Errorf("FAIL: the site admin is not in %s's topic", repo)
}
}
if sync.Subscribers != 3 || sync.Members != 6 {
t.Errorf("FAIL: counted %d subscribers and %d memberships, want 3 and 6",
sync.Subscribers, sync.Members)
}
t.Log("ok: subscribers, the admin group and every grant's topic are current")
}
// One account notify cannot hold must not cost the others their alerts.
func TestReconcileKeepsGoingPastOneFailure(t *testing.T) {
np := newFakePlane()
np.failSub = "lou"
book := fakeBook{
accounts: []oltp.Account{{Name: "lou"}, {Name: "dmestas", Admin: true}},
repos: []string{"quarry"},
}
sync, err := reconcileNotify(context.Background(), np, book)
if err == nil {
t.Error("FAIL: the failure was swallowed")
}
if !has(np.added[adminsTopic], "dmestas") || sync.Subscribers != 1 {
t.Error("FAIL: the sweep stopped at the first failure")
}
if has(np.added[adminsTopic], "lou") || has(np.removed[adminsTopic], "lou") {
t.Error("FAIL: an account notify does not hold was added to a topic anyway")
}
t.Log("ok: a failed account is reported and the rest are still provisioned")
}
type fakeAttention struct {
sent map[string][]api.AttentionEvent
}
func (f *fakeAttention) Attend(account string, e api.AttentionEvent) (uint64, error) {
if f.sent == nil {
f.sent = map[string][]api.AttentionEvent{}
}
f.sent[account] = append(f.sent[account], e)
return uint64(len(f.sent[account])), nil
}
func (f *fakeAttention) TailAttention(string, uint64) (<-chan api.AttentionEvent, func()) {
return nil, func() {}
}
func (f *fakeAttention) AttentionPending(string, uint64) (int, error) { return 0, nil }
func (f *fakeAttention) AttentionTip(string) (uint64, error) { return 0, nil }
func (f *fakeAttention) MarkAttentionRead(string, uint64) error { return nil }
func (f *fakeAttention) AttentionReadSeq(string) uint64 { return 0 }
func (f *fakeAttention) RecentAttention(string, int) ([]api.AttentionEvent, error) {
return nil, nil
}
// A fault reaches the admins through the forge's own inbox, which does
// not depend on notify being present or able to deliver.
func TestAdminAlertReachesEveryAdminsInbox(t *testing.T) {
att := &fakeAttention{}
book := fakeBook{accounts: []oltp.Account{
{Name: "dmestas", Admin: true},
{Name: "lou"},
{Name: "ghost", Admin: true, Disabled: "2026-09-01T00:00:00"},
}}
first := time.Date(2026, 9, 8, 1, 30, 0, 0, time.UTC)
alertAdmins(book, att, errlog.Entry{Surface: "ingest", Repo: "quarry",
Message: "ingest lane is full", First: first})
got := att.sent["dmestas"]
if len(got) != 1 {
t.Fatalf("FAIL: the admin got %d rows, want 1", len(got))
}
if got[0].Kind != "error" || got[0].Repo != "quarry" {
t.Errorf("FAIL: the row does not describe the fault: %+v", got[0])
}
if !strings.Contains(got[0].Summary, "ingest lane is full") {
t.Error("FAIL: the row does not carry what went wrong")
}
if got[0].Link == "" {
t.Error("FAIL: the row does not lead anywhere")
}
// The idempotency key is what pairs this row with notify's twin, so
// one fault is one line in the inbox rather than two.
if got[0].TxnID != "err:"+first.Format(time.RFC3339Nano) {
t.Errorf("FAIL: the row does not pair with its notify twin: %q", got[0].TxnID)
}
if len(att.sent["lou"]) != 0 {
t.Error("FAIL: a fleet alert reached a non-admin")
}
if len(att.sent["ghost"]) != 0 {
t.Error("FAIL: a disabled admin is still alerted")
}
t.Log("ok: every serving admin gets the fault in their forge inbox")
}