package judge import ( "strings" "testing" "git.novox.be/novox/mesh-sdk/go/catalogue" ) // knowledge is mesh-knowledge's manifest as ADR 0592 has it: an object bucket granted by its trusted value readers (a // trusted file takes it), and // two streams that keep their history. const knowledge = `{"module":"mesh-knowledge","version":"1", "resources":[{"id":"state","type":"directory","mode":"0700","place":"."}, {"id":"readers","trusted":true,"type":"file","path":"${dir:state}/readers.json","mode":"0600", "merge":"json","takes-settings":["readers"],"content":"{\"readers\": {}}"}], "objects":[{"name":"sections","max-mib":2048,"grant":{"config":"readers"}}], "streams":[{"name":"versions","retention":"history","max-mib":4096,"events":["version.stored"]}, {"name":"reviews","retention":"history","events":["review.stored"]}]}` // ADR 0592 §5, §6, §9, §25: a module's own object bucket and a stream that keeps its history are taken as declared (the // history stream's bounds are in TestAWorkQueueStreamsValuesAreBounded); each bound and form of a bucket is refused, a // bucket's name has one owner on the bus, and a module reads another's bucket only where the owner grants it. func TestObjectBucketsAreJudgedAsADR0592Says(t *testing.T) { if _, err := ParseManifest([]byte(knowledge)); err != nil { t.Fatalf("mesh-knowledge's manifest was refused: %v", err) } for _, c := range []struct{ change, want string }{ {`"max-mib":2048,"grant"`, `"max-mib":0,"grant"`}, // no size: there is no default {`"max-mib":2048,"grant"`, `"max-mib":8193,"grant"`}, // over the bound {`"name":"sections"`, `"name":"Sections"`}, // not a plain local name {`"name":"sections"`, `"name":"sections"},{"name":"sections","max-mib":1`}, // one bucket per name {`"grant":{"config":"readers"}`, `"grant":{"config":"nobody"}`}, // a grant not by a trusted value } { raw := strings.Replace(knowledge, c.change, c.want, 1) if raw == knowledge { t.Fatalf("the change %q found nothing to change", c.change) } if _, err := ParseManifest([]byte(raw)); err == nil { t.Errorf("%s was taken", c.want) } } // A bucket whose name on the bus starts with what the mesh derives names with is refused. if _, err := ParseManifest([]byte(`{"module":"kv","version":"1","objects":[{"name":"x","max-mib":1}]}`)); err == nil || !strings.Contains(err.Error(), "KV_") { t.Errorf("a bucket kv_x was taken: %v", err) } parse := func(raw string) catalogue.Manifest { t.Helper() m, err := ParseManifest([]byte(raw)) if err != nil { t.Fatal(err) } return m } owner := parse(knowledge) reader := parse(`{"module":"records-page","version":"1","reads":["mesh-knowledge.sections"]}`) shelf := catalogue.Shelf{"mesh-knowledge": owner, "records-page": reader} if p := objectProblemsOf(CatalogueProblems(shelf)); len(p) > 0 { t.Fatalf("a granted read was refused: %v", p) } ungranted := parse(strings.Replace(knowledge, `,"grant":{"config":"readers"}`, "", 1)) shelf["mesh-knowledge"] = ungranted if p := objectProblemsOf(CatalogueProblems(shelf)); len(p) != 1 || !strings.Contains(p[0], "records-page") { t.Errorf("a read of a bucket its owner grants nobody: %v", p) } } // objectProblemsOf are the problems that name an object bucket or a history stream. func objectProblemsOf(all []string) []string { var out []string for _, p := range all { if strings.Contains(p, "object bucket") || strings.Contains(p, "history") { out = append(out, p) } } return out }