package main import ( "context" "encoding/json" "fmt" "strings" "sync" "testing" "time" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/clients/meshcatalogclient" catalog "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog" delivery "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshdelivery" "git.novox.be/novox/mesh-sdk/go/views" ) // wiringOf is a part's reader in miniature: it keeps the followers adopted, so a test starts them as the reader's Start does. type wiringOf struct { mu sync.Mutex fs []*views.Follower } func (w *wiringOf) Adopt(f *views.Follower, _, _ string, _ func(context.Context, []byte) error) error { w.mu.Lock() defer w.mu.Unlock() w.fs = append(w.fs, f) return nil } func (w *wiringOf) Drop(string, string) error { return nil } // Design 47 G §196, §594: which modules exist, and the manifest of each, are the catalog half's; a branch with no first // view yet is an error naming it, never no module, and an entry's manifest is the one it carries whole. func TestCatalogHalfIsTheWatchedBranchesNewestRevision(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() answered := false var mu sync.Mutex call := func(key string, _ any) (json.RawMessage, error) { mu.Lock() defer mu.Unlock() switch key { case meshcatalogclient.RegistrationsVerb: return json.Marshal(catalog.RegistrationsAnswer{Registrations: []catalog.Registration{{Module: "m", Version: 1, Branches: []string{"main"}}}}) case meshcatalogclient.SourceVerb: return json.Marshal(delivery.SourceAnswer{}) case meshcatalogclient.RevisionVerb: if !answered { return nil, fmt.Errorf("the holder is away") } return json.Marshal(catalog.RevisionAnswer{Revision: &catalog.CatalogRevision{Revision: 1, Kind: meshcatalogclient.KindTrunk}, Entries: []catalog.CatalogEntry{{Module: "pg", Manifest: map[string]any{"module": "pg"}}}}) } return nil, fmt.Errorf("asked %s", key) } w := &wiringOf{} b, err := meshcatalogclient.NewBranches(w, call) if err != nil { t.Fatal(err) } h := b if _, _, err := h.Entry("pg"); err == nil || !strings.Contains(err.Error(), "no watched branch") { t.Fatalf("before the registrations: %v, want an error naming the missing branch", err) } go func() { _ = w.fs[0].Start(ctx) }() // the registrations for len(b.Followed()) == 0 { select { case <-ctx.Done(): t.Fatal("the registrations' branch is never followed") case <-time.After(10 * time.Millisecond): } } if _, _, err := h.Entry("pg"); err == nil || !strings.Contains(err.Error(), "no first view of main") { t.Fatalf("with no first view: %v, want an error naming the branch, never no module", err) } mu.Lock() answered = true mu.Unlock() c, _ := b.Catalog("main") if err := c.Start(ctx); err != nil { t.Fatal(err) } got, ok, err := h.Entry("pg") if err != nil || !ok || got.Manifest["module"] != "pg" || h.Ready() != nil { t.Fatalf("with the first view: %v, %v, %v", got, ok, err) } if _, ok, err := h.Entry("absent"); ok || err != nil { t.Fatalf("a module the half does not list: %v, %v", ok, err) } }