Artifact 9a5bdd53c05ee7319782f742a335fef45c06aadecb286fe2e3a5c4e14b51f602:
- File
internal/oltp/mirror_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: 2248) [annotate] [blame] [check-ins using]
package oltp import ( "errors" "path/filepath" "testing" "time" ) func openTest(t *testing.T) *DB { t.Helper() d, err := Open(filepath.Join(t.TempDir(), "oltp.db")) if err != nil { t.Fatal(err) } t.Cleanup(func() { d.Close() }) return d } // Posture changes: a URL change forgets failures about the old peer, // an interval change keeps them; detaching keeps the URL as provenance // and re-attaching clears it; writes are refused until allowed. func TestMirrorPosture(t *testing.T) { d := openTest(t) if err := d.SetMirror("w", "https://old.example/home", time.Hour); err != nil { t.Fatal(err) } at := time.Date(2026, 8, 21, 16, 0, 0, 0, time.UTC) _ = d.RecordMirrorRun("w", at, errors.New("cannot connect")) _ = d.RecordMirrorRun("w", at, errors.New("cannot connect")) if err := d.SetMirror("w", "https://old.example/home", 2*time.Hour); err != nil { t.Fatal(err) } m, _, _ := d.MirrorFor("w") if m.Failures != 2 || m.LastErr == "" { t.Fatalf("interval change must keep the failure history: %+v", m) } if m.AcceptWrites || !m.Tracking() { t.Fatalf("a fresh mirror tracks and refuses writes: %+v", m) } if err := d.SetMirror("w", "https://new.example/home", 2*time.Hour); err != nil { t.Fatal(err) } m, _, _ = d.MirrorFor("w") if m.Failures != 0 || m.LastErr != "" { t.Fatalf("a URL change must clear failures about the old peer: %+v", m) } if err := d.SetMirrorWrites("w", true); err != nil { t.Fatal(err) } if m, _, _ = d.MirrorFor("w"); !m.AcceptWrites { t.Fatal("accept_writes not persisted") } if err := d.SetMirrorWrites("nope", true); err == nil { t.Fatal("writes flag on a non-mirror must fail") } if err := d.DetachMirror("w"); err != nil { t.Fatal(err) } m, ok, _ := d.MirrorFor("w") if !ok || m.Tracking() || m.DetachedFrom != "https://new.example/home" { t.Fatalf("detach keeps the row with provenance and stops tracking: %+v", m) } if err := d.SetMirror("w", "https://new.example/home", time.Hour); err != nil { t.Fatal(err) } if m, _, _ = d.MirrorFor("w"); !m.Tracking() || m.DetachedFrom != "" { t.Fatalf("re-attaching clears detached_from: %+v", m) } all, _ := d.Mirrors() if len(all) != 1 || all[0].Repo != "w" { t.Fatalf("Mirrors: %+v", all) } }