package module import ( "path/filepath" "regexp" "testing" ) // TestAllManifestsLoadAndEventRegexesCompile is the repo-wide consistency // gate for module manifests: every modules//module.yaml must LoadDir // cleanly, and every declared events regex must compile under Go's RE2 // engine (AMPTemplates uses .NET (?) syntax — these must have been // converted to (?P)) and carry at least one named capture group the // agent's tracker actually reads (see agent/internal/state/tracker.go // OnLogLine: name/player_name, id-family, msg/detail/reason). func TestAllManifestsLoadAndEventRegexesCompile(t *testing.T) { dir := filepath.Join("..", "..", "modules") manifests, err := LoadDir(dir) if err != nil { t.Fatalf("LoadDir(%s): %v", dir, err) } if len(manifests) < 20 { t.Fatalf("expected >=20 manifests, got %d — wrong dir?", len(manifests)) } // Group names the tracker extracts (tracker.go firstGroup calls). known := map[string]bool{ "name": true, "player_name": true, "platform_id": true, "owner": true, "cross_id": true, "id": true, "player_id": true, "msg": true, "detail": true, "reason": true, } validKinds := map[string]bool{"join": true, "leave": true, "chat": true, "death": true, "custom": true} for _, m := range manifests { m := m t.Run(m.ID, func(t *testing.T) { for evName, ev := range m.Events { re, err := regexp.Compile(ev.Pattern) if err != nil { t.Errorf("event %q: pattern does not compile: %v", evName, err) continue } if !validKinds[ev.Kind] { t.Errorf("event %q: unknown kind %q", evName, ev.Kind) } // Every event must expose at least one group the tracker // knows how to read, otherwise the event emits empty // PlayerName/PlayerId/Detail and is a silently-useless knob. got := false for _, g := range re.SubexpNames() { if known[g] { got = true } } if !got { t.Errorf("event %q: no tracker-known named group (want one of name/id/msg families); groups=%v", evName, re.SubexpNames()) } // join/leave should identify the player by name or id. if ev.Kind == "join" || ev.Kind == "leave" { hasIdent := false for _, g := range re.SubexpNames() { if g == "name" || g == "player_name" || g == "id" || g == "platform_id" || g == "player_id" || g == "owner" || g == "cross_id" { hasIdent = true } } if !hasIdent { t.Errorf("event %q (kind %s): no player-identifying named group", evName, ev.Kind) } } } }) } }