- Add MQ event types and logging in mrqueue/events.go - Have refinery emit merge_started, merged, merge_failed, merge_skipped events - Create MQEventSource to read from mq_events.jsonl - Add MultiSource to combine events from bd activity and MQ events - Add color coding: green for merged, red for failed - Update feed help with MQ event symbols Events are stored in .beads/mq_events.jsonl and displayed in the feed TUI with appropriate symbols and colors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
145 lines
3.2 KiB
Go
145 lines
3.2 KiB
Go
package feed
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/steveyegge/gastown/internal/mrqueue"
|
|
)
|
|
|
|
func TestParseMQEventLine(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
event mrqueue.Event
|
|
wantType string
|
|
wantTarget string
|
|
wantContains string // Substring in message
|
|
}{
|
|
{
|
|
name: "merge_started",
|
|
event: mrqueue.Event{
|
|
Timestamp: time.Now(),
|
|
Type: mrqueue.EventMergeStarted,
|
|
MRID: "mr-123",
|
|
Branch: "polecat/nux",
|
|
Target: "main",
|
|
Worker: "nux",
|
|
Rig: "gastown",
|
|
},
|
|
wantType: "merge_started",
|
|
wantTarget: "mr-123",
|
|
wantContains: "Merge started",
|
|
},
|
|
{
|
|
name: "merged",
|
|
event: mrqueue.Event{
|
|
Timestamp: time.Now(),
|
|
Type: mrqueue.EventMerged,
|
|
MRID: "mr-456",
|
|
Branch: "polecat/toast",
|
|
Target: "main",
|
|
Worker: "toast",
|
|
Rig: "gastown",
|
|
MergeCommit: "abc123def456789",
|
|
},
|
|
wantType: "merged",
|
|
wantTarget: "mr-456",
|
|
wantContains: "abc123de", // Short SHA
|
|
},
|
|
{
|
|
name: "merge_failed",
|
|
event: mrqueue.Event{
|
|
Timestamp: time.Now(),
|
|
Type: mrqueue.EventMergeFailed,
|
|
MRID: "mr-789",
|
|
Branch: "polecat/capable",
|
|
Target: "main",
|
|
Worker: "capable",
|
|
Rig: "gastown",
|
|
Reason: "conflict in main.go",
|
|
},
|
|
wantType: "merge_failed",
|
|
wantTarget: "mr-789",
|
|
wantContains: "conflict in main.go",
|
|
},
|
|
{
|
|
name: "merge_skipped",
|
|
event: mrqueue.Event{
|
|
Timestamp: time.Now(),
|
|
Type: mrqueue.EventMergeSkipped,
|
|
MRID: "mr-999",
|
|
Branch: "polecat/skip",
|
|
Target: "main",
|
|
Reason: "already merged",
|
|
},
|
|
wantType: "merge_skipped",
|
|
wantTarget: "mr-999",
|
|
wantContains: "already merged",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Marshal to JSON line
|
|
data, err := json.Marshal(tt.event)
|
|
if err != nil {
|
|
t.Fatalf("Failed to marshal event: %v", err)
|
|
}
|
|
|
|
// Parse the line
|
|
result := parseMQEventLine(string(data))
|
|
if result == nil {
|
|
t.Fatal("parseMQEventLine returned nil")
|
|
}
|
|
|
|
if result.Type != tt.wantType {
|
|
t.Errorf("Type = %q, want %q", result.Type, tt.wantType)
|
|
}
|
|
|
|
if result.Target != tt.wantTarget {
|
|
t.Errorf("Target = %q, want %q", result.Target, tt.wantTarget)
|
|
}
|
|
|
|
if tt.wantContains != "" && !contains(result.Message, tt.wantContains) {
|
|
t.Errorf("Message = %q, want to contain %q", result.Message, tt.wantContains)
|
|
}
|
|
|
|
// Actor should be refinery
|
|
if result.Actor != "refinery" {
|
|
t.Errorf("Actor = %q, want %q", result.Actor, "refinery")
|
|
}
|
|
|
|
if result.Role != "refinery" {
|
|
t.Errorf("Role = %q, want %q", result.Role, "refinery")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseMQEventLineEmpty(t *testing.T) {
|
|
result := parseMQEventLine("")
|
|
if result != nil {
|
|
t.Error("Expected nil for empty line")
|
|
}
|
|
|
|
result = parseMQEventLine(" ")
|
|
if result != nil {
|
|
t.Error("Expected nil for whitespace-only line")
|
|
}
|
|
|
|
result = parseMQEventLine("not valid json")
|
|
if result != nil {
|
|
t.Error("Expected nil for invalid JSON")
|
|
}
|
|
}
|
|
|
|
func contains(s, substr string) bool {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|