package storage import ( "context" "encoding/json" "testing" "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` are maintained by the // write path. Whatever sequence of writes, modifications and deletions runs, // they must equal what a full rebuild from the records computes. func TestCountersStayExactAcrossWrites(t *testing.T) { c := newTestClient(t) seed(t, c, map[string]map[string]string{ "R1": {"title": "One", "sizeByte": "100", "title/eng": "One"}, "R2": {"title": "Two", "sizeByte": "x", "genres/Action": "true"}, "R3": {"title": "Three", "sizeByte": "300"}, }) if err := c.SetProperty("R2", "sizeByte", "200"); err != nil { // label string → int t.Fatal(err) } if err := c.SetProperty("R1", "title", "One"); err != nil { // unchanged: no delta t.Fatal(err) } if err := c.DeleteProperty("R1", "title/eng"); err != nil { t.Fatal(err) } if _, err := c.DeleteMetadata("R3"); err != nil { t.Fatal(err) } if n, err := c.CountRoots(); err != nil || n != 2 { t.Fatalf("CountRoots = %d (err %v), want 2", 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"] != 2 { t.Fatalf("sizeByte = %+v, want 2 ints", f) } if _, ok := snap.Fields["title"]; !ok { t.Fatalf("title missing: %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 != 2 { 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") } } // A re-persisted record with nothing new writes nothing and publishes nothing. func TestUnchangedRecordWriteIsANoOp(t *testing.T) { c := newTestClient(t) rec := map[string]string{"title": "Naruto", "fileType": "video", "cids/bafkreiabc": "true"} seed(t, c, map[string]map[string]string{"R1": rec}) before, _ := c.GetStreamLength(MetaEventsStream) if err := c.SetMetadataFlat("R1", rec); err != nil { t.Fatal(err) } if after, _ := c.GetStreamLength(MetaEventsStream); after != before { t.Fatalf("re-persisting an unchanged record published %d events", after-before) } }