panel public release
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package module
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestAllManifestsLoadAndEventRegexesCompile is the repo-wide consistency
|
||||
// gate for module manifests: every modules/<id>/module.yaml must LoadDir
|
||||
// cleanly, and every declared events regex must compile under Go's RE2
|
||||
// engine (AMPTemplates uses .NET (?<n>) syntax — these must have been
|
||||
// converted to (?P<n>)) 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user