 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>
		
			
				
	
	
		
			409 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			409 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package ucxi
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"net/http/httptest"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	"chorus/pkg/ucxl"
 | |
| )
 | |
| 
 | |
| // Helper function to create test server for UCXL testing 
 | |
| func createUCXLTestServer() *Server {
 | |
| 	config := ServerConfig{
 | |
| 		Port:     8080,
 | |
| 		BasePath: "/test",
 | |
| 		Resolver: NewMockResolver(), // Use existing MockResolver from server_test.go
 | |
| 		Storage:  NewMockStorage(),  // Use existing MockStorage from server_test.go
 | |
| 		Logger:   SimpleLogger{},
 | |
| 	}
 | |
| 	return NewServer(config)
 | |
| }
 | |
| 
 | |
| // Test UCXL standardized response formats
 | |
| func TestUCXLResponseFormats(t *testing.T) {
 | |
| 	server := createUCXLTestServer()
 | |
| 	
 | |
| 	tests := []struct {
 | |
| 		name           string
 | |
| 		method         string
 | |
| 		endpoint       string
 | |
| 		query          string
 | |
| 		body           string
 | |
| 		expectedCode   ucxl.UCXLCode
 | |
| 		expectedStatus int
 | |
| 	}{
 | |
| 		{
 | |
| 			name:           "GET with valid address returns UCXL-200-SUCCESS",
 | |
| 			method:         "GET",
 | |
| 			endpoint:       "/test/ucxi/v1/get",
 | |
| 			query:          "address=ucxl://agent:role@project:task/*^",
 | |
| 			body:           "",
 | |
| 			expectedCode:   ucxl.CodeSuccess,
 | |
| 			expectedStatus: 200,
 | |
| 		},
 | |
| 		{
 | |
| 			name:           "GET without address returns UCXL-400-BAD_REQUEST",
 | |
| 			method:         "GET",
 | |
| 			endpoint:       "/test/ucxi/v1/get",
 | |
| 			query:          "",
 | |
| 			body:           "",
 | |
| 			expectedCode:   ucxl.CodeBadRequest,
 | |
| 			expectedStatus: 400,
 | |
| 		},
 | |
| 		{
 | |
| 			name:           "GET with invalid address returns UCXL-400-INVALID_ADDRESS",
 | |
| 			method:         "GET",
 | |
| 			endpoint:       "/test/ucxi/v1/get",
 | |
| 			query:          "address=invalid-address",
 | |
| 			body:           "",
 | |
| 			expectedCode:   ucxl.CodeInvalidAddress,
 | |
| 			expectedStatus: 400,
 | |
| 		},
 | |
| 		{
 | |
| 			name:           "PUT with valid data returns UCXL-201-CREATED",
 | |
| 			method:         "PUT",
 | |
| 			endpoint:       "/test/ucxi/v1/put",
 | |
| 			query:          "address=ucxl://agent:role@project:task/*^",
 | |
| 			body:           "test content",
 | |
| 			expectedCode:   ucxl.CodeCreated,
 | |
| 			expectedStatus: 201,
 | |
| 		},
 | |
| 		{
 | |
| 			name:           "DELETE with valid address returns UCXL-200-SUCCESS",
 | |
| 			method:         "DELETE",
 | |
| 			endpoint:       "/test/ucxi/v1/delete",
 | |
| 			query:          "address=ucxl://agent:role@project:task/*^",
 | |
| 			body:           "",
 | |
| 			expectedCode:   ucxl.CodeSuccess,
 | |
| 			expectedStatus: 200,
 | |
| 		},
 | |
| 		{
 | |
| 			name:           "POST to GET endpoint returns UCXL-405-METHOD_NOT_ALLOWED",
 | |
| 			method:         "POST",
 | |
| 			endpoint:       "/test/ucxi/v1/get",
 | |
| 			query:          "",
 | |
| 			body:           "",
 | |
| 			expectedCode:   ucxl.CodeMethodNotAllowed,
 | |
| 			expectedStatus: 405,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			// Create request
 | |
| 			var req *http.Request
 | |
| 			var err error
 | |
| 			
 | |
| 			if tt.body != "" {
 | |
| 				req, err = http.NewRequest(tt.method, tt.endpoint+"?"+tt.query, strings.NewReader(tt.body))
 | |
| 			} else {
 | |
| 				req, err = http.NewRequest(tt.method, tt.endpoint+"?"+tt.query, nil)
 | |
| 			}
 | |
| 			if err != nil {
 | |
| 				t.Fatalf("Failed to create request: %v", err)
 | |
| 			}
 | |
| 			
 | |
| 			req.Header.Set("Content-Type", "text/plain")
 | |
| 			req.Header.Set("X-Request-ID", "test-"+tt.name)
 | |
| 
 | |
| 			// Create response recorder
 | |
| 			rr := httptest.NewRecorder()
 | |
| 
 | |
| 			// Create HTTP handler
 | |
| 			mux := http.NewServeMux()
 | |
| 			server.registerRoutes(mux)
 | |
| 			handler := server.withMiddleware(mux)
 | |
| 
 | |
| 			// Execute request
 | |
| 			handler.ServeHTTP(rr, req)
 | |
| 
 | |
| 			// Check status code
 | |
| 			if rr.Code != tt.expectedStatus {
 | |
| 				t.Errorf("Expected status %d, got %d", tt.expectedStatus, rr.Code)
 | |
| 			}
 | |
| 
 | |
| 			// Parse response
 | |
| 			var response map[string]interface{}
 | |
| 			if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
 | |
| 				t.Fatalf("Failed to parse response JSON: %v", err)
 | |
| 			}
 | |
| 
 | |
| 			// Check for UCXL response structure
 | |
| 			if rr.Code >= 200 && rr.Code < 300 {
 | |
| 				// Success response should have "response" field
 | |
| 				if responseData, ok := response["response"]; ok {
 | |
| 					if responseMap, ok := responseData.(map[string]interface{}); ok {
 | |
| 						if code, ok := responseMap["code"].(string); ok {
 | |
| 							if ucxl.UCXLCode(code) != tt.expectedCode {
 | |
| 								t.Errorf("Expected UCXL code %s, got %s", tt.expectedCode, code)
 | |
| 							}
 | |
| 						} else {
 | |
| 							t.Error("Response missing 'code' field")
 | |
| 						}
 | |
| 
 | |
| 						// Check required fields
 | |
| 						if _, ok := responseMap["message"]; !ok {
 | |
| 							t.Error("Response missing 'message' field")
 | |
| 						}
 | |
| 						if _, ok := responseMap["request_id"]; !ok {
 | |
| 							t.Error("Response missing 'request_id' field")
 | |
| 						}
 | |
| 						if _, ok := responseMap["timestamp"]; !ok {
 | |
| 							t.Error("Response missing 'timestamp' field")
 | |
| 						}
 | |
| 					}
 | |
| 				} else {
 | |
| 					t.Error("Success response missing 'response' field")
 | |
| 				}
 | |
| 			} else {
 | |
| 				// Error response should have "error" field
 | |
| 				if errorData, ok := response["error"]; ok {
 | |
| 					if errorMap, ok := errorData.(map[string]interface{}); ok {
 | |
| 						if code, ok := errorMap["code"].(string); ok {
 | |
| 							if ucxl.UCXLCode(code) != tt.expectedCode {
 | |
| 								t.Errorf("Expected UCXL code %s, got %s", tt.expectedCode, code)
 | |
| 							}
 | |
| 						} else {
 | |
| 							t.Error("Error response missing 'code' field")
 | |
| 						}
 | |
| 
 | |
| 						// Check required fields
 | |
| 						if _, ok := errorMap["message"]; !ok {
 | |
| 							t.Error("Error response missing 'message' field")
 | |
| 						}
 | |
| 						if _, ok := errorMap["source"]; !ok {
 | |
| 							t.Error("Error response missing 'source' field")
 | |
| 						}
 | |
| 						if _, ok := errorMap["path"]; !ok {
 | |
| 							t.Error("Error response missing 'path' field")
 | |
| 						}
 | |
| 						if _, ok := errorMap["request_id"]; !ok {
 | |
| 							t.Error("Error response missing 'request_id' field")
 | |
| 						}
 | |
| 						if _, ok := errorMap["timestamp"]; !ok {
 | |
| 							t.Error("Error response missing 'timestamp' field")
 | |
| 						}
 | |
| 					}
 | |
| 				} else {
 | |
| 					t.Error("Error response missing 'error' field")
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Test status endpoint provides comprehensive information per Issue 010
 | |
| func TestStatusEndpoint(t *testing.T) {
 | |
| 	server := createUCXLTestServer()
 | |
| 
 | |
| 	req, err := http.NewRequest("GET", "/test/ucxi/v1/status", nil)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to create request: %v", err)
 | |
| 	}
 | |
| 	req.Header.Set("X-Request-ID", "test-status")
 | |
| 
 | |
| 	rr := httptest.NewRecorder()
 | |
| 	mux := http.NewServeMux()
 | |
| 	server.registerRoutes(mux)
 | |
| 	handler := server.withMiddleware(mux)
 | |
| 	handler.ServeHTTP(rr, req)
 | |
| 
 | |
| 	if rr.Code != 200 {
 | |
| 		t.Errorf("Expected status 200, got %d", rr.Code)
 | |
| 	}
 | |
| 
 | |
| 	var response map[string]interface{}
 | |
| 	if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
 | |
| 		t.Fatalf("Failed to parse response JSON: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Check UCXL response structure
 | |
| 	responseData, ok := response["response"].(map[string]interface{})
 | |
| 	if !ok {
 | |
| 		t.Fatal("Response missing 'response' field")
 | |
| 	}
 | |
| 
 | |
| 	data, ok := responseData["data"].(map[string]interface{})
 | |
| 	if !ok {
 | |
| 		t.Fatal("Response data missing")
 | |
| 	}
 | |
| 
 | |
| 	// Check required status fields per Issue 010
 | |
| 	requiredFields := []string{"server", "ucxi", "resolver", "storage", "navigators", "p2p", "metrics"}
 | |
| 	for _, field := range requiredFields {
 | |
| 		if _, ok := data[field]; !ok {
 | |
| 			t.Errorf("Status response missing required field: %s", field)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Check server info
 | |
| 	if serverInfo, ok := data["server"].(map[string]interface{}); ok {
 | |
| 		serverFields := []string{"port", "base_path", "running", "version"}
 | |
| 		for _, field := range serverFields {
 | |
| 			if _, ok := serverInfo[field]; !ok {
 | |
| 				t.Errorf("Server info missing field: %s", field)
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		t.Error("Status response missing server information")
 | |
| 	}
 | |
| 
 | |
| 	// Check resolver stats
 | |
| 	if resolverInfo, ok := data["resolver"].(map[string]interface{}); ok {
 | |
| 		if enabled, ok := resolverInfo["enabled"].(bool); !ok || !enabled {
 | |
| 			t.Error("Resolver should be enabled in test")
 | |
| 		}
 | |
| 	} else {
 | |
| 		t.Error("Status response missing resolver information")
 | |
| 	}
 | |
| 
 | |
| 	// Check storage metrics
 | |
| 	if storageInfo, ok := data["storage"].(map[string]interface{}); ok {
 | |
| 		if enabled, ok := storageInfo["enabled"].(bool); !ok || !enabled {
 | |
| 			t.Error("Storage should be enabled in test")
 | |
| 		}
 | |
| 	} else {
 | |
| 		t.Error("Status response missing storage information")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Test announce endpoint with JSON payload
 | |
| func TestAnnounceEndpoint(t *testing.T) {
 | |
| 	server := createUCXLTestServer()
 | |
| 
 | |
| 	payload := map[string]interface{}{
 | |
| 		"address": "ucxl://agent:role@project:task/*^",
 | |
| 		"content": map[string]interface{}{
 | |
| 			"data":         "dGVzdCBjb250ZW50", // base64 encoded "test content"
 | |
| 			"content_type": "text/plain",
 | |
| 			"metadata":     map[string]string{"author": "test"},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	payloadBytes, err := json.Marshal(payload)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to marshal payload: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	req, err := http.NewRequest("POST", "/test/ucxi/v1/announce", bytes.NewReader(payloadBytes))
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to create request: %v", err)
 | |
| 	}
 | |
| 	req.Header.Set("Content-Type", "application/json")
 | |
| 	req.Header.Set("X-Request-ID", "test-announce")
 | |
| 
 | |
| 	rr := httptest.NewRecorder()
 | |
| 	mux := http.NewServeMux()
 | |
| 	server.registerRoutes(mux)
 | |
| 	handler := server.withMiddleware(mux)
 | |
| 	handler.ServeHTTP(rr, req)
 | |
| 
 | |
| 	if rr.Code != 200 {
 | |
| 		t.Errorf("Expected status 200, got %d", rr.Code)
 | |
| 	}
 | |
| 
 | |
| 	var response map[string]interface{}
 | |
| 	if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
 | |
| 		t.Fatalf("Failed to parse response JSON: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Verify UCXL success response structure
 | |
| 	responseData, ok := response["response"].(map[string]interface{})
 | |
| 	if !ok {
 | |
| 		t.Fatal("Response missing 'response' field")
 | |
| 	}
 | |
| 
 | |
| 	if code, ok := responseData["code"].(string); !ok || ucxl.UCXLCode(code) != ucxl.CodeSuccess {
 | |
| 		t.Errorf("Expected UCXL-200-SUCCESS, got %s", code)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Test error handling with invalid UCXL addresses
 | |
| func TestInvalidAddressHandling(t *testing.T) {
 | |
| 	server := createUCXLTestServer()
 | |
| 
 | |
| 	invalidAddresses := []string{
 | |
| 		"not-a-ucxl-address",
 | |
| 		"ucxl://",
 | |
| 		"ucxl://agent",
 | |
| 		"ucxl://agent:role",
 | |
| 		"ucxl://agent:role@project",
 | |
| 		"ucxl://agent:role@project:task",
 | |
| 		"ucxl://agent:role@project:task/invalid-temporal",
 | |
| 	}
 | |
| 
 | |
| 	for i, address := range invalidAddresses {
 | |
| 		t.Run(fmt.Sprintf("InvalidAddress%d", i), func(t *testing.T) {
 | |
| 			req, err := http.NewRequest("GET", "/test/ucxi/v1/get?address="+address, nil)
 | |
| 			if err != nil {
 | |
| 				t.Fatalf("Failed to create request: %v", err)
 | |
| 			}
 | |
| 			req.Header.Set("X-Request-ID", fmt.Sprintf("test-invalid-%d", i))
 | |
| 
 | |
| 			rr := httptest.NewRecorder()
 | |
| 			mux := http.NewServeMux()
 | |
| 			server.registerRoutes(mux)
 | |
| 			handler := server.withMiddleware(mux)
 | |
| 			handler.ServeHTTP(rr, req)
 | |
| 
 | |
| 			if rr.Code != 400 {
 | |
| 				t.Errorf("Expected status 400, got %d", rr.Code)
 | |
| 			}
 | |
| 
 | |
| 			var response map[string]interface{}
 | |
| 			if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
 | |
| 				t.Fatalf("Failed to parse response JSON: %v", err)
 | |
| 			}
 | |
| 
 | |
| 			// Should be UCXL error format
 | |
| 			errorData, ok := response["error"].(map[string]interface{})
 | |
| 			if !ok {
 | |
| 				t.Fatal("Error response missing 'error' field")
 | |
| 			}
 | |
| 
 | |
| 			code, ok := errorData["code"].(string)
 | |
| 			if !ok {
 | |
| 				t.Fatal("Error missing 'code' field")
 | |
| 			}
 | |
| 
 | |
| 			// Should be either invalid address or bad request
 | |
| 			ucxlCode := ucxl.UCXLCode(code)
 | |
| 			if ucxlCode != ucxl.CodeInvalidAddress && ucxlCode != ucxl.CodeBadRequest {
 | |
| 				t.Errorf("Expected INVALID_ADDRESS or BAD_REQUEST, got %s", code)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Benchmark UCXL response building
 | |
| func BenchmarkUCXLResponseBuilding(b *testing.B) {
 | |
| 	builder := ucxl.NewResponseBuilder("test-request-id", "ucxi-server")
 | |
| 	data := map[string]interface{}{
 | |
| 		"test": "data",
 | |
| 		"count": 42,
 | |
| 	}
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		_ = builder.OK(data)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Benchmark UCXL error building
 | |
| func BenchmarkUCXLErrorBuilding(b *testing.B) {
 | |
| 	builder := ucxl.NewResponseBuilder("test-request-id", "ucxi-server")
 | |
| 	details := map[string]interface{}{
 | |
| 		"field": "address",
 | |
| 		"provided": "invalid-address",
 | |
| 	}
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		_ = builder.ErrorWithDetails(ucxl.CodeInvalidAddress, "Invalid address", "/test/path", details)
 | |
| 	}
 | |
| } |