Restructured project for V2 refactor: **Structure Changes:** - Moved all V1 code to orig/ folder (preserved with git mv) - Created docs/planning/ directory - Added orig/README_V1.md explaining V1 preservation **Planning Documents:** - 00_V2_MASTER_PLAN.md: Complete architecture overview - Executive summary of critical V1 issues - High-level component architecture diagrams - 5-phase implementation roadmap - Success metrics and risk mitigation - 07_TASK_BREAKDOWN.md: Atomic task breakdown - 99+ hours of detailed tasks - Every task < 2 hours (atomic) - Clear dependencies and success criteria - Organized by implementation phase **V2 Key Improvements:** - Per-exchange parsers (factory pattern) - Multi-layer strict validation - Multi-index pool cache - Background validation pipeline - Comprehensive observability **Critical Issues Addressed:** - Zero address tokens (strict validation + cache enrichment) - Parsing accuracy (protocol-specific parsers) - No audit trail (background validation channel) - Inefficient lookups (multi-index cache) - Stats disconnection (event-driven metrics) Next Steps: 1. Review planning documents 2. Begin Phase 1: Foundation (P1-001 through P1-010) 3. Implement parsers in Phase 2 4. Build cache system in Phase 3 5. Add validation pipeline in Phase 4 6. Migrate and test in Phase 5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
97 lines
2.9 KiB
Go
97 lines
2.9 KiB
Go
package lifecycle
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type stubEventBus struct {
|
|
failUntil int
|
|
attempts int
|
|
txHash string
|
|
}
|
|
|
|
func (s *stubEventBus) Publish(event ModuleEvent) error {
|
|
s.attempts++
|
|
if s.attempts <= s.failUntil {
|
|
return fmt.Errorf("publish failure %d for tx %s", s.attempts, s.txHash)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *stubEventBus) Subscribe(eventType EventType, handler EventHandler) error {
|
|
return nil
|
|
}
|
|
|
|
func TestModuleRegistryPublishEventWithRetrySuccess(t *testing.T) {
|
|
registry := NewModuleRegistry(RegistryConfig{
|
|
EventPublishRetries: 3,
|
|
EventPublishDelay: time.Nanosecond,
|
|
})
|
|
registry.logger = nil
|
|
|
|
bus := &stubEventBus{failUntil: 2, txHash: "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}
|
|
registry.eventBus = bus
|
|
|
|
err := registry.publishEventWithRetry(ModuleEvent{ModuleID: "module-A", Type: EventModuleStarted}, "publish failed")
|
|
if err != nil {
|
|
t.Fatalf("expected publish to eventually succeed, got %v", err)
|
|
}
|
|
if bus.attempts != 3 {
|
|
t.Fatalf("expected 3 attempts, got %d", bus.attempts)
|
|
}
|
|
if err := registry.aggregatedErrors(); err != nil {
|
|
t.Fatalf("expected no aggregated errors, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestModuleRegistryPublishEventWithRetryFailure(t *testing.T) {
|
|
registry := NewModuleRegistry(RegistryConfig{
|
|
EventPublishRetries: 2,
|
|
EventPublishDelay: time.Nanosecond,
|
|
})
|
|
registry.logger = nil
|
|
|
|
txHash := "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
|
bus := &stubEventBus{failUntil: 3, txHash: txHash}
|
|
registry.eventBus = bus
|
|
|
|
err := registry.publishEventWithRetry(ModuleEvent{ModuleID: "module-B", Type: EventModuleStopped}, "publish failed")
|
|
if err == nil {
|
|
t.Fatal("expected publish to fail after retries")
|
|
}
|
|
if bus.attempts != 2 {
|
|
t.Fatalf("expected 2 attempts, got %d", bus.attempts)
|
|
}
|
|
|
|
exported := registry.RegistryErrors()
|
|
if len(exported) != 1 {
|
|
t.Fatalf("expected 1 recorded registry error, got %d", len(exported))
|
|
}
|
|
if exported[0] == nil {
|
|
t.Fatal("expected recorded error to be non-nil")
|
|
}
|
|
if copyErrs := registry.RegistryErrors(); copyErrs[0] == nil {
|
|
t.Fatal("copy of registry errors should preserve values")
|
|
}
|
|
if got := exported[0].Error(); !strings.Contains(got, txHash) {
|
|
t.Fatalf("recorded registry error should include tx hash, got %q", got)
|
|
}
|
|
details := registry.RegistryErrorDetails()
|
|
if len(details) != 1 {
|
|
t.Fatalf("expected registry error details to include entry, got %d", len(details))
|
|
}
|
|
if details[0].TxHash != txHash {
|
|
t.Fatalf("expected registry error detail to track tx hash %s, got %s", txHash, details[0].TxHash)
|
|
}
|
|
agg := registry.aggregatedErrors()
|
|
if agg == nil {
|
|
t.Fatal("expected aggregated error to be returned")
|
|
}
|
|
if got := agg.Error(); !strings.Contains(got, "publish failed") || !strings.Contains(got, "publish failure 1") || !strings.Contains(got, txHash) {
|
|
t.Fatalf("aggregated error should include failure details and tx hash, got %q", got)
|
|
}
|
|
}
|