package storage import ( "context" "encoding/json" "testing" "github.com/metazla/meta-core/internal/claim" "github.com/metazla/meta-core/internal/schema" ) func schemaJSON(t *testing.T, c *Client) string { t.Helper() snap, err := schema.NewIndexer(c.Store()).Snapshot(context.Background()) if err != nil { t.Fatal(err) } snap.GeneratedAt = snap.GeneratedAt.Truncate(0).UTC() b, _ := json.Marshal(snap.Fields) return string(b) } // The counters behind /api/schema and search's `total` follow the merged view. // Whatever sequence of claims, retractions and edits runs, they must equal what // a full rebuild from the records computes. func TestCountersStayExactAcrossWrites(t *testing.T) { c := newTestClient(t) a, b := signer(t, 1), signer(t, 2) mustAccept(t, c, vote(t, a, "bafyR1", "title", "One", 10), vote(t, a, "bafyR1", "sizeByte", "100", 10), vote(t, a, "bafyR1", "title/eng", "One", 10), vote(t, a, "bafyR2", "title", "Two", 10), vote(t, a, "bafyR2", "sizeByte", "x", 10), vote(t, a, "bafyR2", "genres/Action", "true", 10), vote(t, a, "bafyR3", "title", "Three", 10), vote(t, a, "bafyR3", "sizeByte", "300", 10), ) mustAccept(t, c, vote(t, a, "bafyR2", "sizeByte", "200", 20), // label string → int vote(t, b, "bafyR1", "title", "One", 20), // agreeing vote: merged value unchanged vote(t, a, "bafyR1", "title/eng", nil, 20), // retraction ) if n, err := c.CountRoots(); err != nil || n != 3 { t.Fatalf("CountRoots = %d (err %v), want 3", n, err) } incremental := schemaJSON(t, c) snap, _ := schema.NewIndexer(c.Store()).Snapshot(context.Background()) if f := snap.Fields["sizeByte"]; f == nil || f.Type != schema.PrimInt || f.Breakdown["int"] != 3 { t.Fatalf("sizeByte = %+v, want 3 ints", f) } if _, ok := snap.Fields["title/eng"]; ok { t.Fatalf("retracted title/eng still counted: %v", snap.Fields) } if err := c.RebuildCounters(); err != nil { t.Fatal(err) } if rebuilt := schemaJSON(t, c); rebuilt != incremental { t.Fatalf("incremental counters drifted from a rebuild:\n incremental %s\n rebuilt %s", incremental, rebuilt) } if n, _ := c.CountRoots(); n != 3 { t.Fatalf("CountRoots after rebuild = %d", n) } } func TestEnsureCountersBuildsOnce(t *testing.T) { c := newTestClient(t) seed(t, c, map[string]map[string]string{"R1": {"title": "One"}}) built, err := c.EnsureCounters() if err != nil || !built { t.Fatalf("first EnsureCounters built=%v err=%v", built, err) } if built, _ := c.EnsureCounters(); built { t.Fatal("second EnsureCounters rebuilt again") } } // Re-persisting the same signed claims writes nothing and publishes nothing — // the gateway's constant repeats. func TestUnchangedClaimWriteIsANoOp(t *testing.T) { c := newTestClient(t) a := signer(t, 1) batch := []claim.Claim{ vote(t, a, "bafkreiabc", "title", "Naruto", 5), vote(t, a, "bafkreiabc", "fileType", "video", 5), } mustAccept(t, c, batch...) before, _ := c.GetStreamLength(MetaEventsStream) for _, st := range write(t, c, batch...) { if st != StatusUnchanged { t.Fatalf("re-sent claim status %s, want unchanged", st) } } if after, _ := c.GetStreamLength(MetaEventsStream); after != before { t.Fatalf("re-persisting unchanged claims published %d events", after-before) } }