Files
zooqueenandHanzo Dev 8a53464ca7 mirror: carry all 40 repos the script did, not the 24 the port kept
The Go port took 24 entries from a Python dict that held 40. Sixteen repos were
in NEITHER sync leg afterwards -- not pull mirrors, so nothing brought them in,
and absent from this table, so nothing pushed them forward -- which is the exact
shape the port was written to eliminate, reintroduced by the port itself.

The worst of them is hanzoai/ci. Every repo in three orgs imports it as
`hanzoai/ci/...@v1`, and a stale pipeline runs GREEN, so a caller cannot tell a
current pipeline from one months old. It had already sat 18 commits behind once
for exactly this reason. The rest: console and namespace (shipping services whose
inbound leg was never written down), the five hanzo-apps surfaces, hanzo-go/sdk
and hanzo-rs/sdk (published SDKs that `go get` and `cargo` resolve against the
forge copy -- 16 and 10 commits behind when they were found), hanzo-dart,
hanzokv, hanzozt, hanzo-docs/team and luxfi/aml.

Two guards, because a table that quietly loses rows is not a thing to notice
twice. One asserts every declared org is in the allowlist -- without it an entry
in a new org is refused and the only symptom is a smaller "N of M resolved". The
other asserts the count and names five of the entries that went missing.

The allowlist gains hanzo-inc, and the live run is why. Three declared repos --
auto, blog and visor -- have been transferred there, and the run refused all
three by name rather than pushing into an org nobody had listed. That is the
guard working: it is an allowlist so that an unknown org is a question, not an
assumption. hanzo-inc is ours (created 2026-07-24, all three private and pushed
within the week), so it is listed now and the refusal goes away.

Measured against live GitHub: 40 of 40 resolve, exit 0. EIGHT only resolve
because names are RESOLVED and never matched against a listing -- three moved to
hanzo-apps, three to hanzo-inc, esign became sign, kms-v1 became kms. A listing
would have reported eight absences, and an absent name raises nothing.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 02:15:58 -07:00

121 lines
4.1 KiB
Go

package commands
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/hanzoai/cd/mirror"
)
func write(t *testing.T, body string) string {
t.Helper()
p := filepath.Join(t.TempDir(), "repos.json")
if err := os.WriteFile(p, []byte(body), 0o600); err != nil {
t.Fatal(err)
}
return p
}
// Direction decides which side may overwrite the other, so an entry that omits it
// must be refused rather than assigned a default. Defaulting to native would turn
// every typo into a push at a repository we do not own; defaulting to mirror would
// silently stop syncing one we do. Neither is a guess a sync job gets to make.
func TestDirectionIsNeverAssumed(t *testing.T) {
_, err := load(write(t, `[{"org":"hanzoai","name":"cloud"}]`))
if err == nil {
t.Fatal("an entry with no direction was accepted")
}
if !strings.Contains(err.Error(), "direction is required") {
t.Fatalf("the error should name the missing field, got %q", err)
}
}
func TestIncompleteEntryIsRefused(t *testing.T) {
for _, body := range []string{
`[{"org":"hanzoai","direction":"native"}]`,
`[{"name":"cloud","direction":"native"}]`,
} {
if _, err := load(write(t, body)); err == nil {
t.Errorf("accepted an entry with no org or no name: %s", body)
}
}
}
// An unreadable table must stop the run. Treating it as empty would report a clean
// reconcile of nothing — the exact shape of silence this program exists to remove.
func TestAbsentTableIsAnError(t *testing.T) {
if _, err := load(filepath.Join(t.TempDir(), "nope.json")); err == nil {
t.Fatal("a missing table was accepted")
}
}
// The shipped table is the one the scheduled run reads, so it has to survive the
// same validation as any other. It caught nothing for weeks while living as a Go
// literal; parsing it here is what makes it config.
func TestShippedTableIsValid(t *testing.T) {
entries, err := load("../../../mirror/repos.json")
if err != nil {
t.Fatalf("the shipped table does not load: %v", err)
}
if len(entries) == 0 {
t.Fatal("the shipped table is empty")
}
for _, e := range entries {
if e.Direction != mirror.Native && e.Direction != mirror.Mirror {
t.Errorf("%s: direction %q is neither %q nor %q", e, e.Direction, mirror.Native, mirror.Mirror)
}
}
}
// Every declared repo must sit in an org the allowlist owns, or the entry is
// dead on arrival: `owned` refuses it, the plan drops it, and the report says
// only that fewer entries resolved than were declared. Adding a repo in a new
// org is therefore TWO edits, and this is what says so — otherwise the second
// one is discovered as a silence, which is the failure mode this whole package
// exists to remove.
//
// One direction only. The allowlist is deliberately broader than the table: a
// repo can be transferred into an org that has no entry yet, and `owned` guards
// the name GitHub resolves to, not the name declared here.
func TestEveryDeclaredOrgIsOwned(t *testing.T) {
entries, err := load("../../../mirror/repos.json")
if err != nil {
t.Fatal(err)
}
for _, e := range entries {
if !mirror.Owned[e.Org] {
t.Errorf("%s: org %q is not in mirror.Owned, so this entry can never resolve", e, e.Org)
}
}
}
// The table replaced a Python dict, and the port dropped 16 of its 40 entries --
// among them hanzoai/ci, the reusable pipeline every repo in three orgs imports
// as `hanzoai/ci/...@v1`. It sat 18 commits behind with v1 pinned to a pipeline
// from months earlier, and a stale pipeline runs GREEN, so every caller believed
// it was current. A count is a blunt guard, but the thing it guards against is
// blunt too: a table that quietly loses rows.
func TestTableCoversEveryRepoTheScriptCarried(t *testing.T) {
entries, err := load("../../../mirror/repos.json")
if err != nil {
t.Fatal(err)
}
if len(entries) < 40 {
t.Errorf("table has %d entries, want at least the 40 the script carried", len(entries))
}
for _, want := range []string{"hanzoai/ci", "hanzoai/console", "hanzo-go/sdk", "hanzo-rs/sdk", "luxfi/aml"} {
found := false
for _, e := range entries {
if e.Org+"/"+e.Name == want {
found = true
break
}
}
if !found {
t.Errorf("%s is missing; it was in the table this replaced", want)
}
}
}