Artifact 0526a1ba2ae8a4d4a284c49cf57ca367f721dd5712883a80e4a79b035e626681:
- File
internal/api/mirror_posture_test.go
— part of check-in
[d980bf4d71]
at
2026-08-21 18:16:58
on branch mirror-posture
— Repository posture: mirrors carried on the repo model, refuse local writes by default
repo.get returns mirror{tracking,url,state,acceptWrites,...} and detachedFrom. mirror.detach replaces mirror.delete and keeps the upstream as provenance; mirror.writes opts a mirror into local writes. Pushes, tickets, wiki, forum and technotes on a tracking mirror are refused (403) naming the upstream. A URL change clears failures recorded against the old peer. (user: dmestas, size: 4214) [annotate] [blame] [check-ins using] [more...]
package api import ( "context" "errors" "strings" "testing" "time" "craftdesign.group/quarry/internal/ops" ) // memMirrors is a MirrorStore in memory, enough to drive posture. type memMirrors map[string]Mirror func (m memMirrors) Mirrors() ([]Mirror, error) { var out []Mirror for _, v := range m { out = append(out, v) } return out, nil } func (m memMirrors) MirrorFor(repo string) (Mirror, bool, error) { v, ok := m[repo]; return v, ok, nil } func (m memMirrors) SetMirror(repo, url string, every int) error { m[repo] = Mirror{Repo: repo, URL: url, Every: every, Enabled: true} return nil } func (m memMirrors) DetachMirror(repo string) error { v := m[repo] v.Enabled, v.DetachedFrom = false, v.URL m[repo] = v return nil } func (m memMirrors) SetMirrorWrites(repo string, accept bool) error { v := m[repo] v.AcceptWrites = accept m[repo] = v return nil } func (m memMirrors) RecordMirrorRun(string, error) error { return nil } func TestMirrorState(t *testing.T) { now := time.Date(2026, 8, 21, 12, 0, 0, 0, time.UTC) stamp := func(ago time.Duration) string { return now.Add(-ago).UTC().Format(time.RFC3339) } cases := []struct { name string m Mirror want string }{ {"fresh", Mirror{Enabled: true, Every: 3600, LastOK: stamp(10 * time.Minute)}, MirrorCurrent}, {"edge", Mirror{Enabled: true, Every: 3600, LastOK: stamp(119 * time.Minute)}, MirrorCurrent}, {"quiet too long", Mirror{Enabled: true, Every: 3600, LastOK: stamp(3 * time.Hour)}, MirrorLagging}, {"never ran", Mirror{Enabled: true, Every: 3600}, MirrorLagging}, {"failing", Mirror{Enabled: true, Every: 3600, LastOK: stamp(time.Minute), Failures: 1}, MirrorFailing}, {"detached", Mirror{Enabled: false, DetachedFrom: "https://x/home"}, MirrorDetached}, } for _, c := range cases { if got := mirrorState(c.m, now); got != c.want { t.Errorf("%s: got %s, want %s", c.name, got, c.want) } } } // A mirror refuses local writes until the operator allows them, and a // detached store is native again. repo.get carries the posture. func TestMirrorRefusesWrites(t *testing.T) { ix := wikiIndex(t) ms := memMirrors{} _ = ms.SetMirror("test", "https://upstream.example/home", 3600) r := ops.NewRegistry() if err := RegisterAll(r, Deps{Version: "v", Repos: testRepos(ix), Mirrors: ms}); err != nil { t.Fatal(err) } ctx := context.Background() newTicket := func() error { _, err := r.Invoke(ctx, "ticket.new", ticketNewIn{Repo: "test", Title: "t"}) return err } err := newTicket() if !errors.Is(err, ops.ErrForbidden) || !strings.Contains(err.Error(), "upstream.example") { t.Fatalf("ticket on a mirror: want forbidden naming the upstream, got %v", err) } for _, op := range []struct { name string in any }{ {"wiki.save", wikiSaveIn{Repo: "test", Name: "p", Body: "b"}}, {"forum.post", forumPostIn{Repo: "test", Title: "t", Body: "b"}}, {"technote.add", technoteAddIn{Repo: "test", Body: "b"}}, } { if _, err := r.Invoke(ctx, op.name, op.in); !errors.Is(err, ops.ErrForbidden) { t.Errorf("%s on a mirror: want forbidden, got %v", op.name, err) } } got, err := r.Invoke(ctx, "repo.get", repoGetIn{Repo: "test"}) if err != nil { t.Fatal(err) } info := got.(repoInfo) if !info.Mirror.Tracking || info.Mirror.URL != "https://upstream.example/home" || info.Mirror.State != MirrorLagging { t.Fatalf("repo.get posture: %+v", info.Mirror) } if _, err := r.Invoke(ctx, "mirror.writes", mirrorWritesIn{Repo: "test", Accept: true}); err != nil { t.Fatal(err) } // (This harness has no store to write through, so the gate is // asserted directly once writes are allowed.) if ok, _ := (Deps{Mirrors: ms}).Writable("test"); !ok { t.Fatal("writes allowed, still refused") } if _, err := r.Invoke(ctx, "mirror.detach", mirrorDetachIn{Repo: "test"}); err != nil { t.Fatal(err) } got, _ = r.Invoke(ctx, "repo.get", repoGetIn{Repo: "test"}) info = got.(repoInfo) if info.Mirror.Tracking || info.DetachedFrom != "https://upstream.example/home" { t.Fatalf("detached posture: %+v detachedFrom=%q", info.Mirror, info.DetachedFrom) } if ok, _ := (Deps{Mirrors: ms}).Writable("test"); !ok { t.Fatal("a detached store is native and writable") } }