backbeat: add module sources

This commit is contained in:
anthonyrawlins
2025-10-17 08:56:25 +11:00
parent 627d15b3f7
commit 4b4eb16efb
48 changed files with 11636 additions and 0 deletions

View File

@@ -0,0 +1,533 @@
package tests
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/xeipuuv/gojsonschema"
)
// MessageTypes defines the three core BACKBEAT interfaces
const (
BeatFrameType = "backbeat.beatframe.v1"
StatusClaimType = "backbeat.statusclaim.v1"
BarReportType = "backbeat.barreport.v1"
)
// BeatFrame represents INT-A: Pulse → All Services
type BeatFrame struct {
Type string `json:"type"`
ClusterID string `json:"cluster_id"`
BeatIndex int64 `json:"beat_index"`
Downbeat bool `json:"downbeat"`
Phase string `json:"phase"`
HLC string `json:"hlc"`
DeadlineAt time.Time `json:"deadline_at"`
TempoBPM float64 `json:"tempo_bpm"`
WindowID string `json:"window_id"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// StatusClaim represents INT-B: Agents → Reverb
type StatusClaim struct {
Type string `json:"type"`
AgentID string `json:"agent_id"`
TaskID string `json:"task_id,omitempty"`
BeatIndex int64 `json:"beat_index"`
State string `json:"state"`
BeatsLeft int `json:"beats_left,omitempty"`
Progress float64 `json:"progress,omitempty"`
Notes string `json:"notes,omitempty"`
HLC string `json:"hlc"`
Resources map[string]interface{} `json:"resources,omitempty"`
Dependencies []string `json:"dependencies,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// BarReport represents INT-C: Reverb → All Services
type BarReport struct {
Type string `json:"type"`
WindowID string `json:"window_id"`
FromBeat int64 `json:"from_beat"`
ToBeat int64 `json:"to_beat"`
AgentsReporting int `json:"agents_reporting"`
OnTimeReviews int `json:"on_time_reviews"`
HelpPromisesFulfilled int `json:"help_promises_fulfilled"`
SecretRotationsOK bool `json:"secret_rotations_ok"`
TempoDriftMS float64 `json:"tempo_drift_ms"`
Issues []map[string]interface{} `json:"issues,omitempty"`
Performance map[string]interface{} `json:"performance,omitempty"`
HealthIndicators map[string]interface{} `json:"health_indicators,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// TestSchemaValidation tests that all JSON schemas are valid and messages conform
func TestSchemaValidation(t *testing.T) {
schemaDir := "../schemas"
tests := []struct {
name string
schemaFile string
validMsgs []interface{}
invalidMsgs []map[string]interface{}
}{
{
name: "BeatFrame Schema Validation",
schemaFile: "beatframe-v1.schema.json",
validMsgs: []interface{}{
BeatFrame{
Type: BeatFrameType,
ClusterID: "test-cluster",
BeatIndex: 100,
Downbeat: false,
Phase: "execute",
HLC: "7ffd:0001:abcd",
DeadlineAt: time.Now().Add(30 * time.Second),
TempoBPM: 2.0,
WindowID: "7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5",
},
BeatFrame{
Type: BeatFrameType,
ClusterID: "prod",
BeatIndex: 0,
Downbeat: true,
Phase: "plan",
HLC: "0001:0000:cafe",
DeadlineAt: time.Now().Add(15 * time.Second),
TempoBPM: 4.0,
WindowID: "a1b2c3d4e5f6789012345678901234ab",
Metadata: map[string]interface{}{
"pulse_version": "1.0.0",
"cluster_health": "healthy",
},
},
},
invalidMsgs: []map[string]interface{}{
// Missing required fields
{
"type": BeatFrameType,
"cluster_id": "test",
// missing beat_index, downbeat, phase, etc.
},
// Invalid phase
{
"type": BeatFrameType,
"cluster_id": "test",
"beat_index": 0,
"downbeat": false,
"phase": "invalid_phase",
"hlc": "7ffd:0001:abcd",
"deadline_at": "2025-09-05T12:00:00Z",
"tempo_bpm": 2.0,
"window_id": "7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5",
},
// Invalid HLC format
{
"type": BeatFrameType,
"cluster_id": "test",
"beat_index": 0,
"downbeat": false,
"phase": "plan",
"hlc": "invalid-hlc-format",
"deadline_at": "2025-09-05T12:00:00Z",
"tempo_bpm": 2.0,
"window_id": "7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5",
},
},
},
{
name: "StatusClaim Schema Validation",
schemaFile: "statusclaim-v1.schema.json",
validMsgs: []interface{}{
StatusClaim{
Type: StatusClaimType,
AgentID: "worker:test-01",
TaskID: "task:123",
BeatIndex: 100,
State: "executing",
BeatsLeft: 3,
Progress: 0.5,
Notes: "processing batch",
HLC: "7ffd:0001:beef",
},
StatusClaim{
Type: StatusClaimType,
AgentID: "agent:backup",
BeatIndex: 101,
State: "idle",
HLC: "7ffe:0002:dead",
Resources: map[string]interface{}{
"cpu_percent": 25.0,
"memory_mb": 512,
},
},
},
invalidMsgs: []map[string]interface{}{
// Missing required fields
{
"type": StatusClaimType,
"agent_id": "test",
// missing beat_index, state, hlc
},
// Invalid state
{
"type": StatusClaimType,
"agent_id": "test",
"beat_index": 0,
"state": "invalid_state",
"hlc": "7ffd:0001:abcd",
},
// Negative progress
{
"type": StatusClaimType,
"agent_id": "test",
"beat_index": 0,
"state": "executing",
"progress": -0.1,
"hlc": "7ffd:0001:abcd",
},
},
},
{
name: "BarReport Schema Validation",
schemaFile: "barreport-v1.schema.json",
validMsgs: []interface{}{
BarReport{
Type: BarReportType,
WindowID: "7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5",
FromBeat: 0,
ToBeat: 119,
AgentsReporting: 150,
OnTimeReviews: 147,
HelpPromisesFulfilled: 12,
SecretRotationsOK: true,
TempoDriftMS: -2.1,
},
BarReport{
Type: BarReportType,
WindowID: "a1b2c3d4e5f6789012345678901234ab",
FromBeat: 120,
ToBeat: 239,
AgentsReporting: 200,
OnTimeReviews: 195,
HelpPromisesFulfilled: 25,
SecretRotationsOK: false,
TempoDriftMS: 15.7,
Issues: []map[string]interface{}{
{
"severity": "warning",
"category": "timing",
"count": 5,
"description": "Some agents running late",
},
},
},
},
invalidMsgs: []map[string]interface{}{
// Missing required fields
{
"type": BarReportType,
"window_id": "7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5",
// missing from_beat, to_beat, etc.
},
// Invalid window_id format
{
"type": BarReportType,
"window_id": "invalid-window-id",
"from_beat": 0,
"to_beat": 119,
"agents_reporting": 150,
"on_time_reviews": 147,
"help_promises_fulfilled": 12,
"secret_rotations_ok": true,
"tempo_drift_ms": 0.0,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Load schema
schemaPath := filepath.Join(schemaDir, tt.schemaFile)
schemaLoader := gojsonschema.NewReferenceLoader("file://" + schemaPath)
// Test valid messages
for i, validMsg := range tt.validMsgs {
t.Run(fmt.Sprintf("Valid_%d", i), func(t *testing.T) {
msgBytes, err := json.Marshal(validMsg)
if err != nil {
t.Fatalf("Failed to marshal valid message: %v", err)
}
docLoader := gojsonschema.NewBytesLoader(msgBytes)
result, err := gojsonschema.Validate(schemaLoader, docLoader)
if err != nil {
t.Fatalf("Schema validation failed: %v", err)
}
if !result.Valid() {
t.Errorf("Valid message failed validation: %v", result.Errors())
}
})
}
// Test invalid messages
for i, invalidMsg := range tt.invalidMsgs {
t.Run(fmt.Sprintf("Invalid_%d", i), func(t *testing.T) {
msgBytes, err := json.Marshal(invalidMsg)
if err != nil {
t.Fatalf("Failed to marshal invalid message: %v", err)
}
docLoader := gojsonschema.NewBytesLoader(msgBytes)
result, err := gojsonschema.Validate(schemaLoader, docLoader)
if err != nil {
t.Fatalf("Schema validation failed: %v", err)
}
if result.Valid() {
t.Errorf("Invalid message passed validation when it should have failed")
}
})
}
})
}
}
// TestMessageParsing tests that messages can be correctly parsed from JSON
func TestMessageParsing(t *testing.T) {
tests := []struct {
name string
jsonStr string
expected interface{}
}{
{
name: "Parse BeatFrame",
jsonStr: `{
"type": "backbeat.beatframe.v1",
"cluster_id": "test",
"beat_index": 123,
"downbeat": true,
"phase": "review",
"hlc": "7ffd:0001:abcd",
"deadline_at": "2025-09-05T12:00:00Z",
"tempo_bpm": 2.5,
"window_id": "7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5"
}`,
expected: BeatFrame{
Type: BeatFrameType,
ClusterID: "test",
BeatIndex: 123,
Downbeat: true,
Phase: "review",
HLC: "7ffd:0001:abcd",
TempoBPM: 2.5,
WindowID: "7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5",
},
},
{
name: "Parse StatusClaim",
jsonStr: `{
"type": "backbeat.statusclaim.v1",
"agent_id": "worker:01",
"beat_index": 456,
"state": "completed",
"progress": 1.0,
"hlc": "7ffe:0002:beef"
}`,
expected: StatusClaim{
Type: StatusClaimType,
AgentID: "worker:01",
BeatIndex: 456,
State: "completed",
Progress: 1.0,
HLC: "7ffe:0002:beef",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
switch expected := tt.expected.(type) {
case BeatFrame:
var parsed BeatFrame
err := json.Unmarshal([]byte(tt.jsonStr), &parsed)
if err != nil {
t.Fatalf("Failed to parse BeatFrame: %v", err)
}
if parsed.Type != expected.Type ||
parsed.ClusterID != expected.ClusterID ||
parsed.BeatIndex != expected.BeatIndex {
t.Errorf("Parsed BeatFrame doesn't match expected")
}
case StatusClaim:
var parsed StatusClaim
err := json.Unmarshal([]byte(tt.jsonStr), &parsed)
if err != nil {
t.Fatalf("Failed to parse StatusClaim: %v", err)
}
if parsed.Type != expected.Type ||
parsed.AgentID != expected.AgentID ||
parsed.State != expected.State {
t.Errorf("Parsed StatusClaim doesn't match expected")
}
}
})
}
}
// TestHLCValidation tests Hybrid Logical Clock format validation
func TestHLCValidation(t *testing.T) {
validHLCs := []string{
"0000:0000:0000",
"7ffd:0001:abcd",
"FFFF:FFFF:FFFF",
"1234:5678:90ab",
}
invalidHLCs := []string{
"invalid",
"7ffd:0001", // too short
"7ffd:0001:abcd:ef", // too long
"gggg:0001:abcd", // invalid hex
"7ffd:0001:abcdz", // invalid hex
}
for _, hlc := range validHLCs {
t.Run(fmt.Sprintf("Valid_%s", hlc), func(t *testing.T) {
if !isValidHLC(hlc) {
t.Errorf("Valid HLC %s was rejected", hlc)
}
})
}
for _, hlc := range invalidHLCs {
t.Run(fmt.Sprintf("Invalid_%s", hlc), func(t *testing.T) {
if isValidHLC(hlc) {
t.Errorf("Invalid HLC %s was accepted", hlc)
}
})
}
}
// TestWindowIDValidation tests window ID format validation
func TestWindowIDValidation(t *testing.T) {
validWindowIDs := []string{
"7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5",
"a1b2c3d4e5f6789012345678901234ab",
"00000000000000000000000000000000",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
}
invalidWindowIDs := []string{
"invalid",
"7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d", // too short
"7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d55", // too long
"7e9b0e6c4c9a4e59b7f2d9a3c1b2e4g5", // invalid hex
}
for _, windowID := range validWindowIDs {
t.Run(fmt.Sprintf("Valid_%s", windowID), func(t *testing.T) {
if !isValidWindowID(windowID) {
t.Errorf("Valid window ID %s was rejected", windowID)
}
})
}
for _, windowID := range invalidWindowIDs {
t.Run(fmt.Sprintf("Invalid_%s", windowID), func(t *testing.T) {
if isValidWindowID(windowID) {
t.Errorf("Invalid window ID %s was accepted", windowID)
}
})
}
}
// Helper functions for validation
func isValidHLC(hlc string) bool {
parts := strings.Split(hlc, ":")
if len(parts) != 3 {
return false
}
for _, part := range parts {
if len(part) != 4 {
return false
}
for _, char := range part {
if !((char >= '0' && char <= '9') || (char >= 'a' && char <= 'f') || (char >= 'A' && char <= 'F')) {
return false
}
}
}
return true
}
func isValidWindowID(windowID string) bool {
if len(windowID) != 32 {
return false
}
for _, char := range windowID {
if !((char >= '0' && char <= '9') || (char >= 'a' && char <= 'f') || (char >= 'A' && char <= 'F')) {
return false
}
}
return true
}
// BenchmarkSchemaValidation benchmarks schema validation performance
func BenchmarkSchemaValidation(b *testing.B) {
schemaDir := "../schemas"
schemaPath := filepath.Join(schemaDir, "beatframe-v1.schema.json")
schemaLoader := gojsonschema.NewReferenceLoader("file://" + schemaPath)
beatFrame := BeatFrame{
Type: BeatFrameType,
ClusterID: "benchmark",
BeatIndex: 1000,
Downbeat: false,
Phase: "execute",
HLC: "7ffd:0001:abcd",
DeadlineAt: time.Now().Add(30 * time.Second),
TempoBPM: 2.0,
WindowID: "7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5",
}
msgBytes, _ := json.Marshal(beatFrame)
docLoader := gojsonschema.NewBytesLoader(msgBytes)
b.ResetTimer()
for i := 0; i < b.N; i++ {
result, err := gojsonschema.Validate(schemaLoader, docLoader)
if err != nil || !result.Valid() {
b.Fatal("Validation failed")
}
}
}
// Helper function to check if schema files exist
func TestSchemaFilesExist(t *testing.T) {
schemaDir := "../schemas"
requiredSchemas := []string{
"beatframe-v1.schema.json",
"statusclaim-v1.schema.json",
"barreport-v1.schema.json",
}
for _, schema := range requiredSchemas {
schemaPath := filepath.Join(schemaDir, schema)
if _, err := os.Stat(schemaPath); os.IsNotExist(err) {
t.Errorf("Required schema file %s does not exist", schemaPath)
}
}
}