package api
import (
"context"
"errors"
"testing"
"craftdesign.group/quarry/internal/oltp"
"craftdesign.group/quarry/internal/ops"
)
// The settings log is readable without a browser.
//
// Before this op the durable record had exactly one reader: a section
// of an HTML page, reachable only by logging into the web UI as an
// admin. An audit record you can only read by hand, in a browser, is
// not one an operator reaches for at the moment they need it -- which
// is the moment they are least able to.
func TestRepoLogReadsTheSettingsLog(t *testing.T) {
d := Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"widgets": {}}),
SettingsLog: func(repo string, limit int) ([]oltp.SettingsChange, error) {
return []oltp.SettingsChange{{At: "2026-09-18T10:00:00Z", Actor: "jva",
Field: "mirrorable", Was: "true", Now: "false"}}, nil
}}
r := ops.NewRegistry()
if err := RegisterAll(r, d); err != nil {
t.Fatal(err)
}
got, err := r.Invoke(context.Background(), "repo.log", repoLogIn{Repo: "widgets"})
if err != nil {
t.Fatal(err)
}
out := got.(repoLogOut)
if out.Count != 1 || len(out.Changes) != 1 {
t.Fatalf("count = %d, changes = %+v", out.Count, out.Changes)
}
c := out.Changes[0]
if c.Actor != "jva" || c.Field != "mirrorable" || c.Was != "true" || c.Now != "false" {
t.Errorf("change = %+v, want jva turning mirrorable off", c)
}
if c.At == "" {
t.Error("the entry has no timestamp")
}
}
// A forge with no registry database says so, rather than answering with
// an empty history that reads as "nothing has ever changed here".
func TestRepoLogWithoutAStoreIsUnavailable(t *testing.T) {
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v",
Repos: NewRepos(map[string]*Repo{"widgets": {}})}); err != nil {
t.Fatal(err)
}
_, err := r.Invoke(context.Background(), "repo.log", repoLogIn{Repo: "widgets"})
if !errors.Is(err, ops.ErrUnavailable) {
t.Errorf("err = %v, want ErrUnavailable: an absent log must not read as an "+
"empty one", err)
}
}
// Reading who changed a repository's settings takes the repository
// admin letter, the same one it takes to change them. The log names
// accounts and what they did; it is not fleet-public.
func TestRepoLogTakesTheAdminLetter(t *testing.T) {
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v",
Repos: NewRepos(map[string]*Repo{"widgets": {}}),
SettingsLog: func(string, int) ([]oltp.SettingsChange, error) {
return nil, nil
}}); err != nil {
t.Fatal(err)
}
op, ok := r.Lookup("repo.log")
if !ok {
t.Fatal("repo.log is not registered")
}
if op.Caps != "a" {
t.Errorf("repo.log takes %q, want \"a\": it reports who did what inside a "+
"repository", op.Caps)
}
if op.Scope != ops.ScopeRepo {
t.Errorf("repo.log scope = %v, want ScopeRepo: a fleet-scoped read would answer "+
"for a repository the caller never named", op.Scope)
}
}