 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			367 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			367 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package hmmm_adapter
 | |
| 
 | |
| import (
 | |
|     "context"
 | |
|     "encoding/json"
 | |
|     "sync"
 | |
|     "testing"
 | |
|     "time"
 | |
| 
 | |
|     "chorus/p2p"
 | |
|     "chorus/pubsub"
 | |
|     "chorus/pkg/hmmm"
 | |
| )
 | |
| 
 | |
| // TestAdapterPubSubIntegration tests the complete integration between the adapter and CHORUS pubsub
 | |
| func TestAdapterPubSubIntegration(t *testing.T) {
 | |
|     ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
 | |
|     defer cancel()
 | |
| 
 | |
|     // Create P2P node
 | |
|     node, err := p2p.NewNode(ctx)
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create P2P node: %v", err)
 | |
|     }
 | |
|     defer node.Close()
 | |
| 
 | |
|     // Create PubSub system
 | |
|     ps, err := pubsub.NewPubSub(ctx, node.Host(), "CHORUS/test/coordination", "hmmm/test/meta-discussion")
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create PubSub: %v", err)
 | |
|     }
 | |
|     defer ps.Close()
 | |
| 
 | |
|     // Create adapter using actual CHORUS pubsub methods
 | |
|     adapter := NewAdapter(
 | |
|         ps.JoinDynamicTopic,
 | |
|         ps.PublishRaw,
 | |
|     )
 | |
| 
 | |
|     // Test publishing to a per-issue topic
 | |
|     topic := "CHORUS/meta/issue/integration-test-42"
 | |
|     testPayload := []byte(`{"version": 1, "type": "meta_msg", "issue_id": 42, "message": "Integration test message"}`)
 | |
| 
 | |
|     err = adapter.Publish(ctx, topic, testPayload)
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to publish message: %v", err)
 | |
|     }
 | |
| 
 | |
|     // Verify metrics
 | |
|     metrics := adapter.GetMetrics()
 | |
|     if metrics.PublishCount != 1 {
 | |
|         t.Errorf("Expected publish count 1, got %d", metrics.PublishCount)
 | |
|     }
 | |
|     if metrics.JoinCount != 1 {
 | |
|         t.Errorf("Expected join count 1, got %d", metrics.JoinCount)
 | |
|     }
 | |
|     if metrics.ErrorCount != 0 {
 | |
|         t.Errorf("Expected error count 0, got %d", metrics.ErrorCount)
 | |
|     }
 | |
| 
 | |
|     // Verify topic is cached
 | |
|     joinedTopics := adapter.GetJoinedTopics()
 | |
|     if len(joinedTopics) != 1 || joinedTopics[0] != topic {
 | |
|         t.Errorf("Expected topic to be cached: got %v", joinedTopics)
 | |
|     }
 | |
| 
 | |
|     // Test repeated publishing to same topic (should use cache)
 | |
|     err = adapter.Publish(ctx, topic, []byte(`{"version": 1, "type": "meta_msg", "issue_id": 42, "message": "Second message"}`))
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to publish second message: %v", err)
 | |
|     }
 | |
| 
 | |
|     // Verify join count didn't increase (cached)
 | |
|     metrics = adapter.GetMetrics()
 | |
|     if metrics.JoinCount != 1 {
 | |
|         t.Errorf("Expected join count to remain 1 (cached), got %d", metrics.JoinCount)
 | |
|     }
 | |
|     if metrics.PublishCount != 2 {
 | |
|         t.Errorf("Expected publish count 2, got %d", metrics.PublishCount)
 | |
|     }
 | |
| }
 | |
| 
 | |
| // TestHMMMRouterIntegration tests the adapter working with the HMMM Router
 | |
| func TestHMMMRouterIntegration(t *testing.T) {
 | |
|     ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
 | |
|     defer cancel()
 | |
| 
 | |
|     // Create P2P node
 | |
|     node, err := p2p.NewNode(ctx)
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create P2P node: %v", err)
 | |
|     }
 | |
|     defer node.Close()
 | |
| 
 | |
|     // Create PubSub system
 | |
|     ps, err := pubsub.NewPubSub(ctx, node.Host(), "CHORUS/test/coordination", "hmmm/test/meta-discussion")
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create PubSub: %v", err)
 | |
|     }
 | |
|     defer ps.Close()
 | |
| 
 | |
|     // Create adapter
 | |
|     adapter := NewAdapter(
 | |
|         ps.JoinDynamicTopic,
 | |
|         ps.PublishRaw,
 | |
|     )
 | |
| 
 | |
|     // Create HMMM Router using our adapter
 | |
|     hmmmRouter := hmmm.NewRouter(adapter, hmmm.DefaultConfig())
 | |
| 
 | |
|     // Create a valid HMMM message
 | |
|     msg := hmmm.Message{
 | |
|         Version:   1,
 | |
|         Type:      "meta_msg",
 | |
|         IssueID:   42,
 | |
|         ThreadID:  "test-thread-1",
 | |
|         MsgID:     "test-msg-1",
 | |
|         NodeID:    node.ID().String(),
 | |
|         Author:    "test-author",
 | |
|         HopCount:  0,
 | |
|         Timestamp: time.Now(),
 | |
|         Message:   "Test message from HMMM Router integration test",
 | |
|     }
 | |
| 
 | |
|     // Publish through HMMM Router
 | |
|     err = hmmmRouter.Publish(ctx, msg)
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to publish via HMMM Router: %v", err)
 | |
|     }
 | |
| 
 | |
|     // Verify adapter metrics were updated
 | |
|     metrics := adapter.GetMetrics()
 | |
|     if metrics.PublishCount != 1 {
 | |
|         t.Errorf("Expected publish count 1, got %d", metrics.PublishCount)
 | |
|     }
 | |
|     if metrics.JoinCount != 1 {
 | |
|         t.Errorf("Expected join count 1, got %d", metrics.JoinCount)
 | |
|     }
 | |
| 
 | |
|     // Verify the expected topic was joined
 | |
|     expectedTopic := hmmm.TopicForIssue(42)
 | |
|     joinedTopics := adapter.GetJoinedTopics()
 | |
|     if len(joinedTopics) != 1 || joinedTopics[0] != expectedTopic {
 | |
|         t.Errorf("Expected topic %s to be joined, got %v", expectedTopic, joinedTopics)
 | |
|     }
 | |
| }
 | |
| 
 | |
| // TestPerIssueTopicPublishing tests publishing to multiple per-issue topics
 | |
| func TestPerIssueTopicPublishing(t *testing.T) {
 | |
|     ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
 | |
|     defer cancel()
 | |
| 
 | |
|     // Create P2P node
 | |
|     node, err := p2p.NewNode(ctx)
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create P2P node: %v", err)
 | |
|     }
 | |
|     defer node.Close()
 | |
| 
 | |
|     // Create PubSub system
 | |
|     ps, err := pubsub.NewPubSub(ctx, node.Host(), "CHORUS/test/coordination", "hmmm/test/meta-discussion")
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create PubSub: %v", err)
 | |
|     }
 | |
|     defer ps.Close()
 | |
| 
 | |
|     // Create adapter
 | |
|     adapter := NewAdapter(
 | |
|         ps.JoinDynamicTopic,
 | |
|         ps.PublishRaw,
 | |
|     )
 | |
| 
 | |
|     // Test publishing to multiple per-issue topics
 | |
|     issueIDs := []int64{100, 101, 102, 103, 104}
 | |
|     
 | |
|     for _, issueID := range issueIDs {
 | |
|         topic := hmmm.TopicForIssue(issueID)
 | |
|         testMessage := map[string]interface{}{
 | |
|             "version":    1,
 | |
|             "type":       "meta_msg",
 | |
|             "issue_id":   issueID,
 | |
|             "thread_id":  "test-thread",
 | |
|             "msg_id":     "test-msg-" + string(rune(issueID)),
 | |
|             "node_id":    node.ID().String(),
 | |
|             "hop_count":  0,
 | |
|             "timestamp":  time.Now().UTC(),
 | |
|             "message":    "Test message for issue " + string(rune(issueID)),
 | |
|         }
 | |
|         
 | |
|         payload, err := json.Marshal(testMessage)
 | |
|         if err != nil {
 | |
|             t.Fatalf("Failed to marshal test message: %v", err)
 | |
|         }
 | |
|         
 | |
|         err = adapter.Publish(ctx, topic, payload)
 | |
|         if err != nil {
 | |
|             t.Fatalf("Failed to publish to topic %s: %v", topic, err)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Verify all topics were joined
 | |
|     metrics := adapter.GetMetrics()
 | |
|     if metrics.JoinCount != int64(len(issueIDs)) {
 | |
|         t.Errorf("Expected join count %d, got %d", len(issueIDs), metrics.JoinCount)
 | |
|     }
 | |
|     if metrics.PublishCount != int64(len(issueIDs)) {
 | |
|         t.Errorf("Expected publish count %d, got %d", len(issueIDs), metrics.PublishCount)
 | |
|     }
 | |
| 
 | |
|     joinedTopics := adapter.GetJoinedTopics()
 | |
|     if len(joinedTopics) != len(issueIDs) {
 | |
|         t.Errorf("Expected %d joined topics, got %d", len(issueIDs), len(joinedTopics))
 | |
|     }
 | |
| 
 | |
|     // Verify all expected topics are present
 | |
|     expectedTopics := make(map[string]bool)
 | |
|     for _, issueID := range issueIDs {
 | |
|         expectedTopics[hmmm.TopicForIssue(issueID)] = true
 | |
|     }
 | |
| 
 | |
|     for _, topic := range joinedTopics {
 | |
|         if !expectedTopics[topic] {
 | |
|             t.Errorf("Unexpected topic joined: %s", topic)
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // TestConcurrentPerIssuePublishing tests concurrent publishing to multiple per-issue topics
 | |
| func TestConcurrentPerIssuePublishing(t *testing.T) {
 | |
|     ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
 | |
|     defer cancel()
 | |
| 
 | |
|     // Create P2P node
 | |
|     node, err := p2p.NewNode(ctx)
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create P2P node: %v", err)
 | |
|     }
 | |
|     defer node.Close()
 | |
| 
 | |
|     // Create PubSub system
 | |
|     ps, err := pubsub.NewPubSub(ctx, node.Host(), "CHORUS/test/coordination", "hmmm/test/meta-discussion")
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create PubSub: %v", err)
 | |
|     }
 | |
|     defer ps.Close()
 | |
| 
 | |
|     // Create adapter
 | |
|     adapter := NewAdapter(
 | |
|         ps.JoinDynamicTopic,
 | |
|         ps.PublishRaw,
 | |
|     )
 | |
| 
 | |
|     // Test concurrent publishing
 | |
|     const numGoroutines = 20
 | |
|     const numIssues = 5
 | |
|     
 | |
|     var wg sync.WaitGroup
 | |
|     wg.Add(numGoroutines)
 | |
| 
 | |
|     for i := 0; i < numGoroutines; i++ {
 | |
|         go func(id int) {
 | |
|             defer wg.Done()
 | |
|             
 | |
|             issueID := int64(200 + (id % numIssues)) // Distribute across 5 issues
 | |
|             topic := hmmm.TopicForIssue(issueID)
 | |
|             
 | |
|             testMessage := map[string]interface{}{
 | |
|                 "version":    1,
 | |
|                 "type":       "meta_msg", 
 | |
|                 "issue_id":   issueID,
 | |
|                 "thread_id":  "concurrent-test",
 | |
|                 "msg_id":     string(rune(id)),
 | |
|                 "node_id":    node.ID().String(),
 | |
|                 "hop_count":  0,
 | |
|                 "timestamp":  time.Now().UTC(),
 | |
|                 "message":    "Concurrent test message",
 | |
|             }
 | |
|             
 | |
|             payload, err := json.Marshal(testMessage)
 | |
|             if err != nil {
 | |
|                 t.Errorf("Failed to marshal message in goroutine %d: %v", id, err)
 | |
|                 return
 | |
|             }
 | |
|             
 | |
|             err = adapter.Publish(ctx, topic, payload)
 | |
|             if err != nil {
 | |
|                 t.Errorf("Failed to publish in goroutine %d: %v", id, err)
 | |
|             }
 | |
|         }(i)
 | |
|     }
 | |
| 
 | |
|     wg.Wait()
 | |
| 
 | |
|     // Verify results
 | |
|     metrics := adapter.GetMetrics()
 | |
|     if metrics.PublishCount != numGoroutines {
 | |
|         t.Errorf("Expected publish count %d, got %d", numGoroutines, metrics.PublishCount)
 | |
|     }
 | |
|     if metrics.JoinCount != numIssues {
 | |
|         t.Errorf("Expected join count %d, got %d", numIssues, metrics.JoinCount)  
 | |
|     }
 | |
|     if metrics.ErrorCount != 0 {
 | |
|         t.Errorf("Expected error count 0, got %d", metrics.ErrorCount)
 | |
|     }
 | |
| 
 | |
|     joinedTopics := adapter.GetJoinedTopics()
 | |
|     if len(joinedTopics) != numIssues {
 | |
|         t.Errorf("Expected %d unique topics joined, got %d", numIssues, len(joinedTopics))
 | |
|     }
 | |
| }
 | |
| 
 | |
| // TestAdapterValidation tests input validation in integration scenario
 | |
| func TestAdapterValidation(t *testing.T) {
 | |
|     ctx := context.Background()
 | |
| 
 | |
|     // Create P2P node
 | |
|     node, err := p2p.NewNode(ctx)
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create P2P node: %v", err)
 | |
|     }
 | |
|     defer node.Close()
 | |
| 
 | |
|     // Create PubSub system
 | |
|     ps, err := pubsub.NewPubSub(ctx, node.Host(), "CHORUS/test/coordination", "hmmm/test/meta-discussion")
 | |
|     if err != nil {
 | |
|         t.Fatalf("Failed to create PubSub: %v", err)
 | |
|     }
 | |
|     defer ps.Close()
 | |
| 
 | |
|     // Create adapter with small payload limit for testing
 | |
|     config := DefaultAdapterConfig()
 | |
|     config.MaxPayloadSize = 100 // Small limit
 | |
|     
 | |
|     adapter := NewAdapterWithConfig(
 | |
|         ps.JoinDynamicTopic,
 | |
|         ps.PublishRaw,
 | |
|         config,
 | |
|     )
 | |
| 
 | |
|     // Test empty topic
 | |
|     err = adapter.Publish(ctx, "", []byte(`{"test": true}`))
 | |
|     if err == nil {
 | |
|         t.Error("Expected error for empty topic")
 | |
|     }
 | |
| 
 | |
|     // Test empty payload
 | |
|     err = adapter.Publish(ctx, "test-topic", []byte{})
 | |
|     if err == nil {
 | |
|         t.Error("Expected error for empty payload")
 | |
|     }
 | |
| 
 | |
|     // Test payload too large
 | |
|     largePayload := make([]byte, 200) // Larger than limit
 | |
|     err = adapter.Publish(ctx, "test-topic", largePayload)
 | |
|     if err == nil {
 | |
|         t.Error("Expected error for payload too large")
 | |
|     }
 | |
| 
 | |
|     // Verify all errors were tracked
 | |
|     metrics := adapter.GetMetrics()
 | |
|     if metrics.ErrorCount != 3 {
 | |
|         t.Errorf("Expected error count 3, got %d", metrics.ErrorCount)
 | |
|     }
 | |
|     if metrics.PublishCount != 0 {
 | |
|         t.Errorf("Expected publish count 0, got %d", metrics.PublishCount)
 | |
|     }
 | |
| } |