v2.0.0: claims model - signed MMUR2 votes in a truth DB, merged view in a rebuildable cache DB; POST /api/claims, claims and merge routes, unsigned writes answer 410
Build and Push Docker Image / build (push) Successful in 5m1s
Build and Push Docker Image / build (push) Successful in 5m1s
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/metazla/meta-core/internal/claim"
|
||||
"github.com/metazla/meta-core/internal/schema"
|
||||
)
|
||||
|
||||
@@ -19,48 +20,42 @@ func schemaJSON(t *testing.T, c *Client) string {
|
||||
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.
|
||||
// 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)
|
||||
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)
|
||||
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"] != 2 {
|
||||
t.Fatalf("sizeByte = %+v, want 2 ints", f)
|
||||
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"]; !ok {
|
||||
t.Fatalf("title missing: %v", snap.Fields)
|
||||
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 != 2 {
|
||||
if n, _ := c.CountRoots(); n != 3 {
|
||||
t.Fatalf("CountRoots after rebuild = %d", n)
|
||||
}
|
||||
}
|
||||
@@ -77,16 +72,23 @@ func TestEnsureCountersBuildsOnce(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A re-persisted record with nothing new writes nothing and publishes nothing.
|
||||
func TestUnchangedRecordWriteIsANoOp(t *testing.T) {
|
||||
// Re-persisting the same signed claims writes nothing and publishes nothing —
|
||||
// the gateway's constant repeats.
|
||||
func TestUnchangedClaimWriteIsANoOp(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})
|
||||
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)
|
||||
if err := c.SetMetadataFlat("R1", rec); err != nil {
|
||||
t.Fatal(err)
|
||||
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 an unchanged record published %d events", after-before)
|
||||
t.Fatalf("re-persisting unchanged claims published %d events", after-before)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user