Complete BZZZ functionality port to CHORUS
🎭 CHORUS now contains full BZZZ functionality adapted for containers Core systems ported: - P2P networking (libp2p with DHT and PubSub) - Task coordination (COOEE protocol) - HMMM collaborative reasoning - SHHH encryption and security - SLURP admin election system - UCXL content addressing - UCXI server integration - Hypercore logging system - Health monitoring and graceful shutdown - License validation with KACHING Container adaptations: - Environment variable configuration (no YAML files) - Container-optimized logging to stdout/stderr - Auto-generated agent IDs for container deployments - Docker-first architecture All proven BZZZ P2P protocols, AI integration, and collaboration features are now available in containerized form. Next: Build and test container deployment. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
409
pkg/ucxi/ucxl_integration_test.go
Normal file
409
pkg/ucxi/ucxl_integration_test.go
Normal file
@@ -0,0 +1,409 @@
|
||||
package ucxi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"chorus.services/bzzz/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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user