mesh/delivery merged: source.main moved to novox/mesh-delivery@4596fbe1b7ac
mesh/delivery-group group feat/take-in merged: every member is merged
mesh/check being carried to c78941e80b59 on 4596fbe1b7ac | from ae31b66a9e33, mesh-catalog fd4931a75261 | novox/mesh-delivery#153 in group feat/take-in
The group's check failed at7b7096f3: the group's own manifest validator refuses the indexer's stream "take-in", outside the bus's predefined forms. The lead's ruling: the indexer says take-in.pending and take-in.taken as its ordinary events (its emits, mesh.mod.<module>.event.take-in.<kind>), as manifest-accepted is said, and keeps no stream. The wait's key is in the body only: an emitted event names one event (no wildcard), and a key such as g.<group> is no event name. The watchman consumes the two events by name on the shared events stream; it needs no replay, since it seeds its waits from indexer_pending at its start. indexer_pending says the last event of each wait as take-in.<kind>. Run locally, as the check runs it: cmd/mesh-judge over every manifest of mesh-catalog, mesh-delivery, mesh-host, mesh-tools, mesh-networking and mesh-media-catalog, with this branch's and mesh-catalog's feat/take-in manifests in the change case and origin/main's in the trunk case: 171 of 171 accepted in both. The same run with the indexer's manifest at7b7096f3refuses it with the check's words. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
97 lines
4.6 KiB
Go
97 lines
4.6 KiB
Go
package takein
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// sampleTakeIns are a pending revision, a group being gathered and a hand take-in's taken (1585/T2).
|
|
func sampleTakeIns(since time.Time) []TakeIn {
|
|
m := []Merge{{Repository: "novox/mesh-delivery", Number: 9, Merge: "eeee"}}
|
|
return []TakeIn{
|
|
{Kind: TakeInPending, Key: PendingKey(12), What: TakeInRevision, Merges: m, WaitsFor: "src2:abc", Since: since},
|
|
{Kind: TakeInPending, Key: GroupTakeInKey("feat/take-in"), What: TakeInGroup, Merges: m, Group: "feat/take-in",
|
|
Members: []delivery.Head{{Repository: "novox/mesh-delivery", Number: 9, Head: "ffff"}, {Repository: "novox/mesh-sdk", Number: 37, Head: "dddd"}}, Since: since},
|
|
{Kind: TakeInTaken, Key: PendingKey(12), What: TakeInRevision, Merges: m, WaitsFor: "src2:abc", Since: since,
|
|
By: "lead-47@shanks", Why: "the named indexer can never run"},
|
|
}
|
|
}
|
|
|
|
// TestTakeInEvents holds the indexer's take-in contract, moved from mesh-sdk go/delivery with its tests (1585/T2, T12).
|
|
func TestTakeInEvents(t *testing.T) {
|
|
// The take-in events, `take-in.<pending|taken>`; a group's key with its dots and no subject wildcard, in the body.
|
|
if PendingKey(42) != "m.00000000000000000042" || GroupTakeInKey("feat/a.b *>") != "g.feat/a.b___" {
|
|
t.Errorf("take-in keys %q %q", PendingKey(42), GroupTakeInKey("feat/a.b *>"))
|
|
}
|
|
for kind, want := range map[string]string{TakeInPending: TakeInPendingEvent, TakeInTaken: TakeInTakenEvent} {
|
|
ev, err := TakeInEvent(kind)
|
|
if err != nil || ev != want || ev != "take-in."+kind {
|
|
t.Fatalf("take-in event = %q, %v", ev, err)
|
|
}
|
|
if k, err := ParseTakeInEvent(ev); err != nil || k != kind {
|
|
t.Errorf("parse take-in event %q = %q %v", ev, k, err)
|
|
}
|
|
}
|
|
for _, bad := range []string{"", "take-in", "take-in.held", "take-in.pending.m.1", "forge.taken"} {
|
|
if _, err := ParseTakeInEvent(bad); err == nil {
|
|
t.Errorf("parse take-in event %q accepted", bad)
|
|
}
|
|
}
|
|
// The bodies round-trip: a pending revision, a group being gathered, and a hand take-in's taken with its by.
|
|
since := time.Date(2026, 11, 4, 23, 0, 0, 0, time.UTC)
|
|
for _, ti := range sampleTakeIns(since) {
|
|
data, err := EncodeTakeIn(ti)
|
|
if err != nil {
|
|
t.Fatalf("take-in %s %s: %v", ti.Kind, ti.What, err)
|
|
}
|
|
ti.V = delivery.Version
|
|
if got, err := DecodeTakeIn(data); err != nil || !SameTakeIn(got, ti) {
|
|
t.Errorf("take-in %s round trip: %+v %v", ti.Key, got, err)
|
|
}
|
|
}
|
|
for _, in := range []string{`{"key":"m.1"}`, `{"v":2,"key":"m.1"}`} {
|
|
if _, err := DecodeTakeIn([]byte(in)); !errors.Is(err, delivery.ErrUnknownVersion) {
|
|
t.Errorf("decode of %s = %v, want delivery.ErrUnknownVersion", in, err)
|
|
}
|
|
}
|
|
// A body that does not hold is refused, naming why.
|
|
for _, c := range []struct {
|
|
name, want string
|
|
i int
|
|
mut func(*TakeIn)
|
|
}{
|
|
{"group take-in without its group", "names its group", 1, func(t *TakeIn) { t.Group = "" }},
|
|
{"group take-in under another group's key", "g.other", 1, func(t *TakeIn) { t.Key = GroupTakeInKey("other") }},
|
|
{"pending revision waiting for no build", "waits for", 0, func(t *TakeIn) { t.WaitsFor = "" }},
|
|
{"pending with a hand act", "only a taken event", 0, func(t *TakeIn) { t.By = "someone@node" }},
|
|
{"hand act without its by", "by", 2, func(t *TakeIn) { t.By = "" }},
|
|
{"take-in without merges", "no merge", 0, func(t *TakeIn) { t.Merges = nil }},
|
|
{"take-in without since", "since", 0, func(t *TakeIn) { t.Since = time.Time{} }},
|
|
{"a merge of no repository", "owner", 0, func(t *TakeIn) { t.Merges = []Merge{{Repository: "nope", Merge: "m"}} }},
|
|
{"a key that is neither a merge's nor a group's", "neither", 0, func(t *TakeIn) { t.Key = "x.1" }},
|
|
} {
|
|
ti := sampleTakeIns(since)[c.i]
|
|
c.mut(&ti)
|
|
if _, err := EncodeTakeIn(ti); err == nil || !strings.Contains(err.Error(), c.want) {
|
|
t.Errorf("%s: %v, want a refusal naming %q", c.name, err, c.want)
|
|
}
|
|
}
|
|
// indexer_pending's answer reads back; a wait of neither kind is refused.
|
|
pa := PendingAnswer{Waits: []PendingWait{{Key: PendingKey(12), What: TakeInRevision, Merges: sampleTakeIns(since)[0].Merges,
|
|
Keys: []string{PendingKey(12)}, WaitsFor: "src2:abc", Since: since, Holds: []string{"mesh-delivery-indexer"}, Said: "take-in.pending." + PendingKey(12), SaidAt: since}}}
|
|
if data, err := json.Marshal(pa); err != nil {
|
|
t.Fatal(err)
|
|
} else if got, err := DecodePendingAnswer(data); err != nil || !reflect.DeepEqual(got, pa) {
|
|
t.Errorf("pending answer round trip: %+v %v", got, err)
|
|
}
|
|
if _, err := DecodePendingAnswer([]byte(`{"waits":[{"key":"m.1","what":"held"}]}`)); err == nil {
|
|
t.Error("a pending answer with a wait of neither kind accepted")
|
|
}
|
|
}
|