The operator, 2026-11-04: logic one module owns lives inside that module as a package of its own, so the take-in step changes no mesh-sdk. TakeIn, its keys and event names, PendingWait and PendingAnswer move to modules/mesh-delivery-indexer/takein (its merges a takein.Merge of its own); Forge whole, with its group fields, ForgeGroupEnd and GroupMerge, moves to modules/mesh-delivery-forge-bridge/forgemsg. Both are nested Go modules that their readers take by replace, as buildrun is. Every importer moves in this commit: the indexer, the bridge, the watchman and the view. Forge's private checks (the repository name, the version) are copied small; mesh-sdk main still holds an unused Forge until its own fold removes it. The indexer, bridge, watchman, view and checker pin mesh-sdk 5a38a12 (origin/main), no feat/take-in commit. The bridge's integration bus now grants the state end-asks that bridge_group_end_again added: without it every test of the package failed on a permissions violation. Test budget: two new test functions (TestForge, TestTakeInEvents) and the two the join added are paid by merging four pairs in internal/bridge; the repository stays at 806 unit tests. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
194 lines
11 KiB
Go
194 lines
11 KiB
Go
package forgemsg
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
const oddManifest = "{\n\t\"module\": \"a\",\n \"z\": 1, \"a\": [ \"x\",\"y\" ],\n \"why\": \"é — <b> & \\n\"\n}\n\n"
|
|
|
|
func sampleForges() []Forge {
|
|
return []Forge{
|
|
{Kind: ForgePull, Repository: "novox/mesh-sdk", Branch: "feat/x", Number: 36, Head: "aaaa", Action: "opened"},
|
|
{Kind: ForgeMerged, Repository: "novox/mesh-sdk", Number: 36, Head: "aaaa", Merge: "cccc",
|
|
Trees: map[string]string{"modules/a": "t1"}, Root: "t0"},
|
|
{Kind: ForgeRefused, Repository: "novox/mesh-sdk", Act: "novox/mesh-sdk.36.aaaa.view", Words: "403 forbidden"},
|
|
{Kind: ForgeOpen, Open: []OpenPull{{Repository: "novox/mesh-sdk", Number: 36}, {Repository: "novox/mesh-control", Number: 7}}},
|
|
// 1585/T1: a group member's merged message names its group and every approved member (ADR 0633 §4).
|
|
{Kind: ForgeMerged, Repository: "novox/mesh-sdk", Number: 37, Head: "dddd", Merge: "eeee", Root: "t0",
|
|
Group: "feat/take-in", Members: []delivery.Head{{Repository: "novox/mesh-sdk", Number: 37, Head: "dddd"}, {Repository: "novox/mesh-delivery", Number: 9, Head: "ffff"}}},
|
|
}
|
|
}
|
|
|
|
func mergedWithInputs() Forge {
|
|
return Forge{Kind: ForgeMerged, Repository: "novox/mesh-sdk", Number: 36, Head: "aaaa", Merge: "cccc", Root: "t0",
|
|
Trees: map[string]string{"a": "t1", "b": "t2"},
|
|
Manifests: map[string]string{"a": oddManifest},
|
|
Locks: map[string]map[string]string{"a": {"go.sum": strings.Repeat("ab", 32)}}}
|
|
}
|
|
|
|
// TestForge holds the forge bridge's contract, moved whole from mesh-sdk go/delivery with its tests (1200/AC1, AC3,
|
|
// AC4; 1279/AC5; 1442/AC1-AC4; 1585/T1).
|
|
func TestForge(t *testing.T) {
|
|
t.Run("events", func(t *testing.T) {
|
|
ev, err := ForgeEvent("novox/mesh-sdk", ForgeMerged)
|
|
if err != nil || ev != "forge.novox.mesh-sdk.merged" {
|
|
t.Fatalf("forge event = %q, %v", ev, err)
|
|
}
|
|
if repo, kind, err := ParseForgeEvent(ev); err != nil || repo != "novox/mesh-sdk" || kind != ForgeMerged {
|
|
t.Fatalf("parse forge event = %q %q %v", repo, kind, err)
|
|
}
|
|
for _, bad := range []struct{ repo, kind string }{
|
|
{"", "pull"}, {"novox", "pull"}, {"a/b/c", "pull"}, {"/b", "pull"}, {"a/", "pull"},
|
|
{"a.b/c", "pull"}, {"a/b c", "pull"}, {"a/b", "closed"}, {"a/b", ""}, {"a/b", ForgeOpen},
|
|
} {
|
|
if _, err := ForgeEvent(bad.repo, bad.kind); err == nil {
|
|
t.Errorf("forge event %q %q accepted", bad.repo, bad.kind)
|
|
}
|
|
}
|
|
for _, bad := range []string{"", "forge.a.b", "forge.a.b.closed", "x.a.b.pull", "forge.a.b.pull.x", "forge..b.pull", ForgeGroupEndEvent} {
|
|
if _, _, err := ParseForgeEvent(bad); err == nil {
|
|
t.Errorf("parse forge event %q accepted", bad)
|
|
}
|
|
}
|
|
if repo, kind, err := ParseForgeEvent(OpenEvent()); OpenEvent() != "forge.open" || err != nil || repo != "" || kind != ForgeOpen {
|
|
t.Errorf("open event %q = %q %q %v", OpenEvent(), repo, kind, err)
|
|
}
|
|
})
|
|
t.Run("round trip", func(t *testing.T) {
|
|
fs := append(sampleForges(), mergedWithInputs())
|
|
for _, f := range fs {
|
|
data, err := EncodeForge(f)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", f.Kind, err)
|
|
}
|
|
var probe map[string]any
|
|
if err := json.Unmarshal(data, &probe); err != nil || probe["v"] != float64(1) {
|
|
t.Fatalf("%s: json %s does not carry v:1", f.Kind, data)
|
|
}
|
|
f.V = Version
|
|
if got, err := DecodeForge(data); err != nil || !SameForge(got, f) {
|
|
t.Errorf("forge %s round trip: %+v %v", f.Kind, got, err)
|
|
}
|
|
}
|
|
// An empty list is an open message too: nothing is open.
|
|
if data, err := EncodeForge(Forge{Kind: ForgeOpen, Open: []OpenPull{}}); err != nil {
|
|
t.Errorf("empty open: %v", err)
|
|
} else if f, err := DecodeForge(data); err != nil || f.Kind != ForgeOpen || len(f.Open) != 0 {
|
|
t.Errorf("empty open round trip: %+v %v", f, err)
|
|
}
|
|
ge := ForgeGroupEnd{Group: "feat/take-in", Members: []GroupMerge{{Repository: "novox/mesh-sdk", Number: 37, Merge: "eeee"}}}
|
|
data, err := EncodeForgeGroupEnd(ge)
|
|
ge.V = Version
|
|
if got, err2 := DecodeForgeGroupEnd(data); err != nil || err2 != nil || !SameForgeGroupEnd(got, ge) {
|
|
t.Errorf("group end round trip: %+v %v %v", got, err, err2)
|
|
}
|
|
// An older bridge's message reads, and is written back byte for byte.
|
|
old := `{"v":1,"kind":"merged","repository":"novox/mesh-sdk","number":36,"head":"aaaa","merge":"cccc","trees":{"a":"t1"},"root":"t0"}`
|
|
if f, err := DecodeForge([]byte(old)); err != nil || f.Manifests != nil {
|
|
t.Fatalf("%+v, %v", f, err)
|
|
} else if data, err := EncodeForge(f); err != nil || string(data) != old {
|
|
t.Errorf("%s, %v, want exactly %s", data, err, old)
|
|
}
|
|
// A merge leaving the repository with no module.json is a message too (issue 1126).
|
|
f := sampleForges()[1]
|
|
f.Trees = nil
|
|
if data, err := EncodeForge(f); err != nil {
|
|
t.Errorf("a merged message with no trees is refused: %v", err)
|
|
} else if got, err := DecodeForge(data); err != nil || len(got.Trees) != 0 {
|
|
t.Errorf("a merged message with no trees comes back as %+v, %v", got, err)
|
|
}
|
|
for name, dec := range map[string]func([]byte) error{
|
|
"forge": func(b []byte) error { _, err := DecodeForge(b); return err },
|
|
"group end": func(b []byte) error { _, err := DecodeForgeGroupEnd(b); return err },
|
|
} {
|
|
for _, in := range []string{`{"group":"g"}`, `{"v":2,"group":"g"}`} {
|
|
if err := dec([]byte(in)); !errors.Is(err, delivery.ErrUnknownVersion) {
|
|
t.Errorf("%s decode of %s = %v, want delivery.ErrUnknownVersion", name, in, err)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
t.Run("refused", func(t *testing.T) {
|
|
at := func(i int, mut func(*Forge)) Forge { f := sampleForges()[i]; mut(&f); return f }
|
|
in := func(mut func(*Forge)) Forge { f := mergedWithInputs(); mut(&f); return f }
|
|
pullIn := func(mut func(*Forge)) Forge {
|
|
f := Forge{Kind: ForgePull, Repository: "novox/mesh-sdk", Number: 1, Head: "h", Action: PullOpened,
|
|
Dirs: []string{"a"}, DirsKnown: true, Manifests: map[string]string{"a": oddManifest},
|
|
Locks: map[string]map[string]string{"a": {"go.sum": strings.Repeat("0", 64)}}}
|
|
mut(&f)
|
|
return f
|
|
}
|
|
for _, c := range []struct {
|
|
name, want string
|
|
f Forge
|
|
}{
|
|
{"merged without merge commit", "merge", at(1, func(f *Forge) { f.Merge = "" })},
|
|
{"merged without root", "root", at(1, func(f *Forge) { f.Root = "" })},
|
|
{"merged with a manifest and no trees", "modules/a", at(1, func(f *Forge) { f.Trees = nil; f.Manifests = map[string]string{"modules/a": "{}"} })},
|
|
{"merged with an empty tree", "modules/a", at(1, func(f *Forge) { f.Trees = map[string]string{"modules/a": ""} })},
|
|
{"pull with an unknown action", "reopened", at(0, func(f *Forge) { f.Action = "reopened" })},
|
|
{"pull without head", "head", at(0, func(f *Forge) { f.Head = "" })},
|
|
{"refused without words", "words", at(2, func(f *Forge) { f.Words = "" })},
|
|
{"refused without act", "act", at(2, func(f *Forge) { f.Act = "" })},
|
|
{"unknown kind", "closed", Forge{Kind: "closed", Repository: "a/b"}},
|
|
{"bad repository", "nope", Forge{Kind: ForgePull, Repository: "nope", Number: 1, Head: "h", Action: "opened"}},
|
|
{"open with a repository", "only the open", Forge{Kind: ForgeOpen, Repository: "a/b", Open: []OpenPull{{Repository: "a/b", Number: 1}}}},
|
|
{"open with a bad entry", "", Forge{Kind: ForgeOpen, Open: []OpenPull{{Repository: "a", Number: 1}}}},
|
|
{"open with a zero number", "no number", Forge{Kind: ForgeOpen, Open: []OpenPull{{Repository: "a/b"}}}},
|
|
{"pull naming open pull requests", "only an open message", Forge{Kind: ForgePull, Repository: "a/b", Number: 1, Head: "h", Action: "updated", Open: []OpenPull{{Repository: "a/b", Number: 1}}}},
|
|
{"manifest outside trees", `"zz"`, in(func(f *Forge) { f.Manifests["zz"] = "{}" })},
|
|
{"locks outside trees", `"yy"`, in(func(f *Forge) { f.Locks["yy"] = map[string]string{"go.sum": "h"} })},
|
|
{"empty manifest", `"b"`, in(func(f *Forge) { f.Manifests["b"] = "" })},
|
|
{"empty lock hash", `"a"`, in(func(f *Forge) { f.Locks["a"]["go.sum"] = "" })},
|
|
{"named paths outside trees", `"other"`, in(func(f *Forge) { f.Paths = map[string]map[string]string{"other": {"src": "o"}} })},
|
|
{"a named path with no object", `"src"`, in(func(f *Forge) { f.Paths = map[string]map[string]string{"a": {"src": ""}} })},
|
|
{"placement files", "mesh-placement", in(func(f *Forge) { f.RetiredPlacements = []byte(`{"anchor":"{}"}`) })},
|
|
{"placements read", "mesh-placement", in(func(f *Forge) { f.RetiredRead = true })},
|
|
{"predefined not an object", "one JSON object", in(func(f *Forge) { f.Predefined = []byte(`["domain"]`) })},
|
|
{"predefined both", "says the merge holds none", in(func(f *Forge) { f.Predefined = []byte(`{}`); f.PredefinedRead = true })},
|
|
{"pull with the predefined list", "only a merged message", Forge{Kind: ForgePull, Repository: "novox/mesh-sdk", Number: 1, Head: "h", Action: PullOpened, PredefinedRead: true}},
|
|
{"pull manifest outside dirs", `"b"`, pullIn(func(f *Forge) { f.Manifests["b"] = "{}" })},
|
|
{"pull locks outside dirs", `"c"`, pullIn(func(f *Forge) { f.Locks["c"] = map[string]string{"go.sum": "h"} })},
|
|
{"refused with manifests", "no manifests", Forge{Kind: ForgeRefused, Repository: "a/b", Act: "x", Words: "no", Manifests: map[string]string{"a": "{}"}}},
|
|
{"open with locks", "only the open", Forge{Kind: ForgeOpen, Locks: map[string]map[string]string{"a": {"go.sum": "h"}}}},
|
|
// 1585/T1: a group only on a merged message, with its members, the message's own pull request among them.
|
|
{"pull with a group", "only a merged message names its delivery group", at(0, func(f *Forge) {
|
|
f.Group, f.Members = "g", []delivery.Head{{Repository: "novox/mesh-sdk", Number: 36, Head: "aaaa"}}
|
|
})},
|
|
{"merged with a group and no members", "together", at(4, func(f *Forge) { f.Members = nil })},
|
|
{"merged with members and no group", "together", at(4, func(f *Forge) { f.Group = "" })},
|
|
{"merged with members leaving itself out", "leave out", at(4, func(f *Forge) { f.Members = f.Members[1:] })},
|
|
{"merged with a member twice", "twice", at(4, func(f *Forge) { f.Members = append(f.Members, f.Members[0]) })},
|
|
{"open with a group", "only the open pull requests", Forge{Kind: ForgeOpen, Group: "g"}},
|
|
} {
|
|
if _, err := EncodeForge(c.f); err == nil || !strings.Contains(err.Error(), c.want) {
|
|
t.Errorf("%s: %v, want a refusal naming %q", c.name, err, c.want)
|
|
}
|
|
}
|
|
// A member's repository in another case still names the merged pull request (10c91fba).
|
|
f := sampleForges()[4]
|
|
f.Members[0].Repository = "Novox/Mesh-SDK"
|
|
if _, err := EncodeForge(f); err != nil {
|
|
t.Errorf("a member in another case: %v", err)
|
|
}
|
|
for _, c := range []struct {
|
|
name, want string
|
|
e ForgeGroupEnd
|
|
}{
|
|
{"group end without members", "no member", ForgeGroupEnd{Group: "g"}},
|
|
{"group end without a group", "names its group", ForgeGroupEnd{Members: []GroupMerge{{Repository: "a/b", Number: 1, Merge: "m"}}}},
|
|
{"group end with a member without its merge", "merge commit", ForgeGroupEnd{Group: "g", Members: []GroupMerge{{Repository: "a/b", Number: 1}}}},
|
|
{"group end with a member twice", "twice", ForgeGroupEnd{Group: "g", Members: []GroupMerge{{Repository: "a/b", Number: 1, Merge: "m"}, {Repository: "a/b", Number: 1, Merge: "m"}}}},
|
|
} {
|
|
if _, err := EncodeForgeGroupEnd(c.e); err == nil || !strings.Contains(err.Error(), c.want) {
|
|
t.Errorf("%s: %v, want a refusal naming %q", c.name, err, c.want)
|
|
}
|
|
}
|
|
})
|
|
}
|