mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
Major changes: - Merge pkg/wire Python types into pkg/python/lux_consensus - Add MCP mesh consensus module (from hanzo-consensus) - Add types.py with Candidate, Vote, Certificate for wire protocol - Bridge functions for AI consensus -> blockchain interop - Keep pkg/wire as Go-only wire protocol types Python SDK now provides: - lux_consensus: Core chain/block/vote types - lux_consensus.types: Wire protocol (Candidate, WireVote, WireCertificate) - lux_consensus.mcp: MCP mesh for agent-to-agent consensus Also includes engine improvements: - Chain syncer with persistence - PQ crypto enhancements - External sequencer finality example - Protocol field/horizon/prism updates All tests passing.
1528 lines
40 KiB
Go
1528 lines
40 KiB
Go
// Copyright (C) 2025, Lux Industries Inc All rights reserved.
|
|
// Additional tests to achieve 100% coverage for ai package
|
|
|
|
package ai
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// === SPECIALIZED AGENTS TESTS ===
|
|
|
|
// --- UpgradeAgent Tests ---
|
|
|
|
func TestNewUpgradeAgent(t *testing.T) {
|
|
extractor := NewUpgradeFeatures()
|
|
model := NewSimpleModel[UpgradeData]("upgrade-node", extractor)
|
|
agent := NewUpgradeAgent("upgrade-node", model)
|
|
|
|
if agent == nil {
|
|
t.Fatal("NewUpgradeAgent returned nil")
|
|
}
|
|
|
|
if agent.Agent == nil {
|
|
t.Fatal("Agent embedded in UpgradeAgent is nil")
|
|
}
|
|
|
|
if agent.nodeID != "upgrade-node" {
|
|
t.Errorf("Expected nodeID 'upgrade-node', got '%s'", agent.nodeID)
|
|
}
|
|
}
|
|
|
|
func TestUpgradeAgent_AutonomousUpgrade_Success(t *testing.T) {
|
|
extractor := NewUpgradeFeatures()
|
|
model := NewSimpleModel[UpgradeData]("upgrade-node", extractor)
|
|
|
|
// Set weights to produce high confidence
|
|
model.weights["change_count"] = 0.1
|
|
model.weights["risk_score"] = -2.0 // Negative weight for risk (lower risk = better)
|
|
model.weights["test_count"] = 0.5
|
|
model.bias = 5.0 // Strong positive bias
|
|
|
|
agent := NewUpgradeAgent("upgrade-node", model)
|
|
|
|
// Pre-populate usage for network consensus
|
|
agent.usage["upgrade_v2.0.0"] = 150 // High usage gives consensus boost
|
|
|
|
upgrades := []UpgradeData{
|
|
{
|
|
Version: "v2.0.0",
|
|
Changes: []string{"improvement1", "improvement2"},
|
|
Risk: "low",
|
|
TestResults: []string{"pass", "pass", "pass"},
|
|
Timestamp: time.Now(),
|
|
},
|
|
}
|
|
|
|
ctx := context.Background()
|
|
decision, err := agent.AutonomousUpgrade(ctx, "v1.0.0", upgrades)
|
|
|
|
if err != nil {
|
|
t.Fatalf("AutonomousUpgrade failed: %v", err)
|
|
}
|
|
|
|
if decision == nil {
|
|
t.Fatal("Decision is nil")
|
|
}
|
|
|
|
if decision.Action != "approve_upgrade" {
|
|
t.Errorf("Expected action 'approve_upgrade', got '%s'", decision.Action)
|
|
}
|
|
|
|
if decision.Data.Version != "v2.0.0" {
|
|
t.Errorf("Expected version 'v2.0.0', got '%s'", decision.Data.Version)
|
|
}
|
|
}
|
|
|
|
func TestUpgradeAgent_AutonomousUpgrade_InsufficientConfidence(t *testing.T) {
|
|
extractor := NewUpgradeFeatures()
|
|
model := NewSimpleModel[UpgradeData]("upgrade-node", extractor)
|
|
|
|
// Set weights to produce low confidence
|
|
model.weights["risk_score"] = 2.0 // Positive weight for risk (higher risk = worse)
|
|
model.bias = -10.0 // Strong negative bias
|
|
|
|
agent := NewUpgradeAgent("upgrade-node", model)
|
|
|
|
upgrades := []UpgradeData{
|
|
{
|
|
Version: "v2.0.0",
|
|
Changes: []string{"risky change"},
|
|
Risk: "critical",
|
|
TestResults: []string{},
|
|
Timestamp: time.Now(),
|
|
},
|
|
}
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.AutonomousUpgrade(ctx, "v1.0.0", upgrades)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error for insufficient confidence, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "insufficient confidence") {
|
|
t.Errorf("Expected insufficient confidence error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestUpgradeAgent_AutonomousUpgrade_EmptyUpgrades(t *testing.T) {
|
|
extractor := NewUpgradeFeatures()
|
|
model := NewSimpleModel[UpgradeData]("upgrade-node", extractor)
|
|
agent := NewUpgradeAgent("upgrade-node", model)
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.AutonomousUpgrade(ctx, "v1.0.0", []UpgradeData{})
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error for empty upgrades, got nil")
|
|
}
|
|
}
|
|
|
|
func TestUpgradeAgent_getNetworkConsensus(t *testing.T) {
|
|
extractor := NewUpgradeFeatures()
|
|
model := NewSimpleModel[UpgradeData]("upgrade-node", extractor)
|
|
agent := NewUpgradeAgent("upgrade-node", model)
|
|
|
|
// Test with no usage
|
|
upgrade := UpgradeData{Version: "v1.0.0"}
|
|
consensus := agent.getNetworkConsensus(upgrade)
|
|
if consensus != 0.0 {
|
|
t.Errorf("Expected 0 consensus with no usage, got %f", consensus)
|
|
}
|
|
|
|
// Test with partial usage
|
|
agent.usage["upgrade_v1.0.0"] = 50
|
|
consensus = agent.getNetworkConsensus(upgrade)
|
|
if consensus != 0.5 {
|
|
t.Errorf("Expected 0.5 consensus with 50 usage, got %f", consensus)
|
|
}
|
|
|
|
// Test with max usage (capped at 1.0)
|
|
agent.usage["upgrade_v1.0.0"] = 200
|
|
consensus = agent.getNetworkConsensus(upgrade)
|
|
if consensus != 1.0 {
|
|
t.Errorf("Expected 1.0 consensus with 200 usage, got %f", consensus)
|
|
}
|
|
}
|
|
|
|
// --- BlockAgent Tests ---
|
|
|
|
func TestNewBlockAgent(t *testing.T) {
|
|
extractor := NewBlockFeatures()
|
|
model := NewSimpleModel[BlockData]("block-node", extractor)
|
|
agent := NewBlockAgent("block-node", model)
|
|
|
|
if agent == nil {
|
|
t.Fatal("NewBlockAgent returned nil")
|
|
}
|
|
|
|
if agent.Agent == nil {
|
|
t.Fatal("Agent embedded in BlockAgent is nil")
|
|
}
|
|
}
|
|
|
|
func TestBlockAgent_ArbitrateFork_Success(t *testing.T) {
|
|
extractor := NewBlockFeatures()
|
|
model := NewSimpleModel[BlockData]("block-node", extractor)
|
|
|
|
// Set weights for high confidence
|
|
model.weights["tx_count"] = 0.5
|
|
model.weights["height"] = 0.001
|
|
model.bias = 3.0
|
|
|
|
agent := NewBlockAgent("block-node", model)
|
|
|
|
forks := []BlockData{
|
|
{
|
|
Height: 1000,
|
|
Hash: "0xabc",
|
|
Transactions: []string{"tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", "tx8", "tx9", "tx10"},
|
|
Timestamp: time.Now(),
|
|
},
|
|
{
|
|
Height: 1001,
|
|
Hash: "0xdef",
|
|
Transactions: []string{"tx1", "tx2"},
|
|
Timestamp: time.Now().Add(-48 * time.Hour), // Old fork
|
|
},
|
|
}
|
|
|
|
ctx := context.Background()
|
|
decision, err := agent.ArbitrateFork(ctx, forks)
|
|
|
|
if err != nil {
|
|
t.Fatalf("ArbitrateFork failed: %v", err)
|
|
}
|
|
|
|
if decision == nil {
|
|
t.Fatal("Decision is nil")
|
|
}
|
|
|
|
if decision.Action != "choose_fork" {
|
|
t.Errorf("Expected action 'choose_fork', got '%s'", decision.Action)
|
|
}
|
|
}
|
|
|
|
func TestBlockAgent_ArbitrateFork_LowConfidence(t *testing.T) {
|
|
extractor := NewBlockFeatures()
|
|
model := NewSimpleModel[BlockData]("block-node", extractor)
|
|
|
|
// Set weights for low confidence
|
|
model.bias = -10.0
|
|
|
|
agent := NewBlockAgent("block-node", model)
|
|
|
|
forks := []BlockData{
|
|
{
|
|
Height: 1000,
|
|
Hash: "0xabc",
|
|
Transactions: []string{},
|
|
Timestamp: time.Now().Add(-100 * time.Hour), // Very old
|
|
},
|
|
}
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.ArbitrateFork(ctx, forks)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error for low confidence, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "cannot determine fork winner") {
|
|
t.Errorf("Expected fork winner confidence error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestBlockAgent_ArbitrateFork_EmptyForks(t *testing.T) {
|
|
extractor := NewBlockFeatures()
|
|
model := NewSimpleModel[BlockData]("block-node", extractor)
|
|
agent := NewBlockAgent("block-node", model)
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.ArbitrateFork(ctx, []BlockData{})
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error for empty forks, got nil")
|
|
}
|
|
}
|
|
|
|
func TestBlockAgent_evaluateForkSupport(t *testing.T) {
|
|
extractor := NewBlockFeatures()
|
|
model := NewSimpleModel[BlockData]("block-node", extractor)
|
|
agent := NewBlockAgent("block-node", model)
|
|
|
|
// Recent fork with many transactions
|
|
recentFork := BlockData{
|
|
Height: 1000,
|
|
Transactions: []string{"tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", "tx8", "tx9", "tx10"},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
support := agent.evaluateForkSupport(recentFork)
|
|
if support <= 0 {
|
|
t.Errorf("Recent fork with many txs should have positive support, got %f", support)
|
|
}
|
|
|
|
// Old fork with few transactions
|
|
oldFork := BlockData{
|
|
Height: 1000,
|
|
Transactions: []string{},
|
|
Timestamp: time.Now().Add(-48 * time.Hour),
|
|
}
|
|
|
|
oldSupport := agent.evaluateForkSupport(oldFork)
|
|
if oldSupport >= support {
|
|
t.Error("Old fork should have less support than recent fork")
|
|
}
|
|
}
|
|
|
|
// --- SecurityAgent Tests ---
|
|
|
|
func TestNewSecurityAgent(t *testing.T) {
|
|
model := NewSimpleModel[SecurityData]("security-node", &securityFeatureExtractor{})
|
|
agent := NewSecurityAgent("security-node", model)
|
|
|
|
if agent == nil {
|
|
t.Fatal("NewSecurityAgent returned nil")
|
|
}
|
|
|
|
if agent.Agent == nil {
|
|
t.Fatal("Agent embedded in SecurityAgent is nil")
|
|
}
|
|
}
|
|
|
|
func TestSecurityAgent_AutomaticSecurityResponse_Success(t *testing.T) {
|
|
model := NewSimpleModel[SecurityData]("security-node", &securityFeatureExtractor{})
|
|
model.bias = 3.0 // Positive bias for approve action
|
|
|
|
agent := NewSecurityAgent("security-node", model)
|
|
|
|
threat := SecurityData{
|
|
ThreatLevel: "medium",
|
|
Threats: []string{"suspicious activity"},
|
|
NodeID: "malicious-node",
|
|
Evidence: []string{"log entry 1", "log entry 2"},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
decision, err := agent.AutomaticSecurityResponse(ctx, threat)
|
|
|
|
if err != nil {
|
|
t.Fatalf("AutomaticSecurityResponse failed: %v", err)
|
|
}
|
|
|
|
if decision == nil {
|
|
t.Fatal("Decision is nil")
|
|
}
|
|
|
|
if decision.Data.ThreatLevel != "medium" {
|
|
t.Errorf("Expected threat level 'medium', got '%s'", decision.Data.ThreatLevel)
|
|
}
|
|
}
|
|
|
|
func TestSecurityAgent_AutomaticSecurityResponse_CriticalThreat(t *testing.T) {
|
|
model := NewSimpleModel[SecurityData]("security-node", &securityFeatureExtractor{})
|
|
// Set weights for very high confidence (>0.9 after multiplier)
|
|
// But make it reject so auto-execution is not triggered with "approve"
|
|
model.bias = -10.0 // Negative bias will result in "reject" action
|
|
|
|
agent := NewSecurityAgent("security-node", model)
|
|
|
|
threat := SecurityData{
|
|
ThreatLevel: "critical",
|
|
Threats: []string{"51% attack detected"},
|
|
NodeID: "malicious-node",
|
|
Evidence: []string{"block reorg evidence"},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
decision, err := agent.AutomaticSecurityResponse(ctx, threat)
|
|
|
|
if err != nil {
|
|
t.Fatalf("AutomaticSecurityResponse failed: %v", err)
|
|
}
|
|
|
|
if decision == nil {
|
|
t.Fatal("Decision is nil")
|
|
}
|
|
|
|
// Verify the critical urgency multiplier was applied
|
|
if decision.Confidence <= 0 {
|
|
t.Error("Expected positive confidence after urgency multiplier")
|
|
}
|
|
}
|
|
|
|
func TestSecurityAgent_AutomaticSecurityResponse_CriticalAutoExecute(t *testing.T) {
|
|
// Create a mock model that returns a valid security action
|
|
model := &mockSecurityModel{
|
|
action: "block_node",
|
|
confidence: 0.95,
|
|
}
|
|
|
|
agent := &SecurityAgent{
|
|
Agent: New[SecurityData]("security-node", model, nil, nil),
|
|
}
|
|
|
|
threat := SecurityData{
|
|
ThreatLevel: "critical",
|
|
Threats: []string{"51% attack"},
|
|
NodeID: "malicious-node",
|
|
Evidence: []string{"evidence"},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
decision, err := agent.AutomaticSecurityResponse(ctx, threat)
|
|
|
|
if err != nil {
|
|
t.Fatalf("AutomaticSecurityResponse failed: %v", err)
|
|
}
|
|
|
|
if decision == nil {
|
|
t.Fatal("Decision is nil")
|
|
}
|
|
|
|
if decision.Action != "block_node" {
|
|
t.Errorf("Expected action 'block_node', got '%s'", decision.Action)
|
|
}
|
|
}
|
|
|
|
func TestSecurityAgent_getThreatUrgency(t *testing.T) {
|
|
model := NewSimpleModel[SecurityData]("security-node", &securityFeatureExtractor{})
|
|
agent := NewSecurityAgent("security-node", model)
|
|
|
|
tests := []struct {
|
|
level string
|
|
expected float64
|
|
}{
|
|
{"critical", 1.5},
|
|
{"high", 1.2},
|
|
{"medium", 1.0},
|
|
{"low", 0.8},
|
|
{"unknown", 1.0}, // default
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.level, func(t *testing.T) {
|
|
urgency := agent.getThreatUrgency(tt.level)
|
|
if urgency != tt.expected {
|
|
t.Errorf("Expected urgency %f for level %s, got %f", tt.expected, tt.level, urgency)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSecurityAgent_executeSecurityResponse(t *testing.T) {
|
|
model := NewSimpleModel[SecurityData]("security-node", &securityFeatureExtractor{})
|
|
agent := NewSecurityAgent("security-node", model)
|
|
|
|
threat := SecurityData{
|
|
ThreatLevel: "high",
|
|
Threats: []string{"test threat"},
|
|
NodeID: "test-node",
|
|
Evidence: []string{},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
// Test known actions
|
|
actions := []string{"block_node", "quarantine", "emergency_halt"}
|
|
for _, action := range actions {
|
|
t.Run(action, func(t *testing.T) {
|
|
err := agent.executeSecurityResponse(action, threat)
|
|
if err != nil {
|
|
t.Errorf("executeSecurityResponse(%s) failed: %v", action, err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Test unknown action
|
|
err := agent.executeSecurityResponse("unknown_action", threat)
|
|
if err == nil {
|
|
t.Fatal("Expected error for unknown action, got nil")
|
|
}
|
|
if !containsString(err.Error(), "unknown security action") {
|
|
t.Errorf("Expected unknown security action error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
// --- DisputeAgent Tests ---
|
|
|
|
func TestNewDisputeAgent(t *testing.T) {
|
|
model := NewSimpleModel[DisputeData]("dispute-node", &disputeFeatureExtractor{})
|
|
agent := NewDisputeAgent("dispute-node", model)
|
|
|
|
if agent == nil {
|
|
t.Fatal("NewDisputeAgent returned nil")
|
|
}
|
|
|
|
if agent.Agent == nil {
|
|
t.Fatal("Agent embedded in DisputeAgent is nil")
|
|
}
|
|
}
|
|
|
|
func TestDisputeAgent_ResolveDispute_Success(t *testing.T) {
|
|
model := NewSimpleModel[DisputeData]("dispute-node", &disputeFeatureExtractor{})
|
|
// Set weights for high confidence
|
|
model.bias = 5.0
|
|
|
|
agent := NewDisputeAgent("dispute-node", model)
|
|
|
|
// Set usage for network agreement
|
|
agent.usage["dispute_governance_approve"] = 100
|
|
|
|
dispute := DisputeData{
|
|
Type: "governance",
|
|
Parties: []string{"party-a", "party-b"},
|
|
Evidence: []string{"evidence1", "evidence2"},
|
|
ChainID: "chain-1",
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
decision, err := agent.ResolveDispute(ctx, dispute)
|
|
|
|
if err != nil {
|
|
t.Fatalf("ResolveDispute failed: %v", err)
|
|
}
|
|
|
|
if decision == nil {
|
|
t.Fatal("Decision is nil")
|
|
}
|
|
|
|
if decision.Data.Type != "governance" {
|
|
t.Errorf("Expected dispute type 'governance', got '%s'", decision.Data.Type)
|
|
}
|
|
}
|
|
|
|
func TestDisputeAgent_ResolveDispute_InsufficientConfidence(t *testing.T) {
|
|
model := NewSimpleModel[DisputeData]("dispute-node", &disputeFeatureExtractor{})
|
|
// Set weights for low confidence
|
|
model.bias = -10.0
|
|
|
|
agent := NewDisputeAgent("dispute-node", model)
|
|
|
|
dispute := DisputeData{
|
|
Type: "governance",
|
|
Parties: []string{"party-a", "party-b"},
|
|
Evidence: []string{},
|
|
ChainID: "chain-1",
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.ResolveDispute(ctx, dispute)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error for insufficient confidence, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "insufficient confidence") {
|
|
t.Errorf("Expected insufficient confidence error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDisputeAgent_validateDisputeResolution(t *testing.T) {
|
|
model := NewSimpleModel[DisputeData]("dispute-node", &disputeFeatureExtractor{})
|
|
agent := NewDisputeAgent("dispute-node", model)
|
|
|
|
dispute := DisputeData{
|
|
Type: "governance",
|
|
Parties: []string{"a", "b"},
|
|
Evidence: []string{},
|
|
ChainID: "chain-1",
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
// No agreement initially
|
|
agreement := agent.validateDisputeResolution(dispute, "approve")
|
|
if agreement != 0.0 {
|
|
t.Errorf("Expected 0 agreement with no usage, got %f", agreement)
|
|
}
|
|
|
|
// Add some agreement
|
|
agent.usage["dispute_governance_approve"] = 25
|
|
agreement = agent.validateDisputeResolution(dispute, "approve")
|
|
if agreement != 0.5 {
|
|
t.Errorf("Expected 0.5 agreement with 25 usage, got %f", agreement)
|
|
}
|
|
|
|
// Max agreement
|
|
agent.usage["dispute_governance_approve"] = 100
|
|
agreement = agent.validateDisputeResolution(dispute, "approve")
|
|
if agreement != 1.0 {
|
|
t.Errorf("Expected 1.0 agreement with 100 usage, got %f", agreement)
|
|
}
|
|
}
|
|
|
|
// === ENGINE ADDITIONAL TESTS ===
|
|
|
|
func TestEngineProcess_DecisionModuleError(t *testing.T) {
|
|
eng := NewEngine()
|
|
|
|
// Add inference module that succeeds
|
|
infModule := &testModule{
|
|
id: "inference-1",
|
|
typ: ModuleInference,
|
|
}
|
|
_ = eng.AddModule(infModule)
|
|
|
|
// Add decision module that fails
|
|
decModule := &testModule{
|
|
id: "decision-1",
|
|
typ: ModuleDecision,
|
|
processErr: errors.New("decision processing failed"),
|
|
}
|
|
_ = eng.AddModule(decModule)
|
|
|
|
input := Input{
|
|
Type: InputBlock,
|
|
Data: map[string]interface{}{"height": 100},
|
|
}
|
|
|
|
_, err := eng.Process(context.Background(), input)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error from decision module, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "decision module") {
|
|
t.Errorf("Expected decision module error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestEngineConfigure_ModuleInitError(t *testing.T) {
|
|
eng := NewEngine()
|
|
|
|
module := &testModuleWithInitError{
|
|
id: "test-module",
|
|
typ: ModuleInference,
|
|
initErr: errors.New("initialization failed"),
|
|
}
|
|
|
|
_ = eng.AddModule(module)
|
|
|
|
config := Config{
|
|
Global: map[string]interface{}{},
|
|
Modules: map[string]interface{}{
|
|
"test-module": map[string]interface{}{"enabled": true},
|
|
},
|
|
}
|
|
|
|
err := eng.Configure(config)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error from module initialization, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "failed to configure module") {
|
|
t.Errorf("Expected configure module error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestBuilderBuild_DuplicateModuleID(t *testing.T) {
|
|
builder := NewBuilder()
|
|
|
|
// Add two modules with same ID
|
|
_, err := builder.
|
|
WithInference("same-id", nil).
|
|
WithDecision("same-id", nil).
|
|
Build()
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error for duplicate module ID, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "already exists") {
|
|
t.Errorf("Expected already exists error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCreateModule_UnknownType(t *testing.T) {
|
|
_, err := createModule("test", ModuleType("unknown"), nil)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error for unknown module type, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "unknown module type") {
|
|
t.Errorf("Expected unknown module type error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestBuilderBuild_ConfigureError(t *testing.T) {
|
|
// This tests when Configure fails after modules are added
|
|
// Currently, Configure only fails if module.Initialize fails
|
|
// which is already tested in TestEngineConfigure_ModuleInitError
|
|
// So we just verify the flow works correctly
|
|
builder := NewBuilder()
|
|
|
|
engine, err := builder.
|
|
WithInference("inf-1", nil).
|
|
Build()
|
|
|
|
if err != nil {
|
|
t.Fatalf("Build failed: %v", err)
|
|
}
|
|
|
|
if engine == nil {
|
|
t.Fatal("Engine is nil")
|
|
}
|
|
}
|
|
|
|
// === MODELS ADDITIONAL TESTS ===
|
|
|
|
func TestSimpleModel_ProposeDecision_DecideError(t *testing.T) {
|
|
// Use a model that returns error from Decide
|
|
extractor := &errorFeatureExtractor{}
|
|
model := NewSimpleModel[BlockData]("node-1", extractor)
|
|
|
|
ctx := context.Background()
|
|
_, err := model.ProposeDecision(ctx, BlockData{})
|
|
|
|
// ProposeDecision calls Decide, which should not error with this extractor
|
|
// However, we can test the flow works correctly
|
|
if err != nil {
|
|
t.Logf("ProposeDecision error (expected for coverage): %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSimpleModel_ValidateProposal_DifferentActions(t *testing.T) {
|
|
extractor := &mockBlockFeatureExtractor{}
|
|
model := NewSimpleModel[BlockData]("node-1", extractor)
|
|
|
|
// Set weights so our model will approve (positive score)
|
|
model.bias = 10.0
|
|
|
|
// Create a proposal with reject action (different from what our model would decide)
|
|
proposal := &Proposal[BlockData]{
|
|
ID: "test-proposal",
|
|
NodeID: "other-node",
|
|
Decision: &Decision[BlockData]{
|
|
ID: "decision-1",
|
|
Action: "reject", // Our model would approve, so this disagrees
|
|
Confidence: 0.5,
|
|
Data: BlockData{
|
|
Height: 100,
|
|
Timestamp: time.Now(),
|
|
},
|
|
},
|
|
Weight: 1.0,
|
|
Confidence: 0.5,
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
confidence, err := model.ValidateProposal(proposal)
|
|
|
|
if err != nil {
|
|
t.Fatalf("ValidateProposal failed: %v", err)
|
|
}
|
|
|
|
// Since actions differ, agreement should be 0
|
|
// confidence = 0 * confidenceWeight * ourConfidence = 0
|
|
if confidence != 0.0 {
|
|
t.Errorf("Expected 0 confidence for disagreeing actions, got %f", confidence)
|
|
}
|
|
}
|
|
|
|
func TestSimpleModel_Learn_ReturnError(t *testing.T) {
|
|
// Test that Learn returns error from learnExample
|
|
// Currently learnExample always returns nil, so we test the success case
|
|
extractor := &mockBlockFeatureExtractor{}
|
|
model := NewSimpleModel[BlockData]("node-1", extractor)
|
|
|
|
examples := []TrainingExample[BlockData]{
|
|
{
|
|
Input: BlockData{
|
|
Height: 100,
|
|
Timestamp: time.Now(),
|
|
},
|
|
Output: Decision[BlockData]{
|
|
ID: "decision-1",
|
|
Action: "approve",
|
|
Timestamp: time.Now(),
|
|
},
|
|
Feedback: 0.5, // Neutral feedback
|
|
NodeID: "node-1",
|
|
Weight: 0.5, // Lower weight
|
|
},
|
|
}
|
|
|
|
err := model.Learn(examples)
|
|
if err != nil {
|
|
t.Fatalf("Learn failed: %v", err)
|
|
}
|
|
|
|
if len(model.history) != 1 {
|
|
t.Errorf("Expected 1 history entry, got %d", len(model.history))
|
|
}
|
|
}
|
|
|
|
func TestSecurityAgent_AutomaticSecurityResponse_AutoExecuteError(t *testing.T) {
|
|
// Test the auto-execution error path
|
|
model := &mockSecurityModel{
|
|
action: "unknown_action", // Will fail executeSecurityResponse
|
|
confidence: 0.95, // High confidence to trigger auto-execute
|
|
}
|
|
|
|
agent := &SecurityAgent{
|
|
Agent: New[SecurityData]("security-node", model, nil, nil),
|
|
}
|
|
|
|
threat := SecurityData{
|
|
ThreatLevel: "critical", // Critical triggers auto-execute
|
|
Threats: []string{"attack"},
|
|
NodeID: "malicious-node",
|
|
Evidence: []string{"evidence"},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.AutomaticSecurityResponse(ctx, threat)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error from auto-execution, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "auto-execution failed") {
|
|
t.Errorf("Expected auto-execution failed error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestUpgradeAgent_AutonomousUpgrade_DecideError(t *testing.T) {
|
|
// Test when model.Decide returns error for all upgrades
|
|
model := &mockUpgradeModel{
|
|
decideErr: errors.New("decide failed"),
|
|
}
|
|
|
|
agent := &UpgradeAgent{
|
|
Agent: New[UpgradeData]("upgrade-node", model, nil, nil),
|
|
}
|
|
|
|
upgrades := []UpgradeData{
|
|
{
|
|
Version: "v2.0.0",
|
|
Changes: []string{"change"},
|
|
Risk: "low",
|
|
Timestamp: time.Now(),
|
|
},
|
|
}
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.AutonomousUpgrade(ctx, "v1.0.0", upgrades)
|
|
|
|
// When all Decide calls fail, bestScore remains 0, insufficient confidence
|
|
if err == nil {
|
|
t.Fatal("Expected error for insufficient confidence, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "insufficient confidence") {
|
|
t.Errorf("Expected insufficient confidence error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestBlockAgent_ArbitrateFork_DecideError(t *testing.T) {
|
|
// Test when model.Decide returns error for all forks
|
|
model := &mockBlockModel{
|
|
decideErr: errors.New("decide failed"),
|
|
}
|
|
|
|
agent := &BlockAgent{
|
|
Agent: New[BlockData]("block-node", model, nil, nil),
|
|
}
|
|
|
|
forks := []BlockData{
|
|
{
|
|
Height: 1000,
|
|
Hash: "0xabc",
|
|
Timestamp: time.Now(),
|
|
},
|
|
}
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.ArbitrateFork(ctx, forks)
|
|
|
|
// When all Decide calls fail, bestScore remains 0, cannot determine winner
|
|
if err == nil {
|
|
t.Fatal("Expected error for cannot determine fork winner, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "cannot determine fork winner") {
|
|
t.Errorf("Expected cannot determine fork winner error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDisputeAgent_ResolveDispute_DecideError(t *testing.T) {
|
|
// Test when model.Decide returns error
|
|
model := &mockDisputeModel{
|
|
decideErr: errors.New("decide failed"),
|
|
}
|
|
|
|
agent := &DisputeAgent{
|
|
Agent: New[DisputeData]("dispute-node", model, nil, nil),
|
|
}
|
|
|
|
dispute := DisputeData{
|
|
Type: "governance",
|
|
Parties: []string{"a", "b"},
|
|
Evidence: []string{"e1"},
|
|
ChainID: "chain-1",
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.ResolveDispute(ctx, dispute)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error from dispute resolution, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "dispute resolution failed") {
|
|
t.Errorf("Expected dispute resolution failed error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSecurityAgent_AutomaticSecurityResponse_DecideError(t *testing.T) {
|
|
// Test when model.Decide returns error
|
|
model := &mockSecurityModelWithError{
|
|
decideErr: errors.New("decide failed"),
|
|
}
|
|
|
|
agent := &SecurityAgent{
|
|
Agent: New[SecurityData]("security-node", model, nil, nil),
|
|
}
|
|
|
|
threat := SecurityData{
|
|
ThreatLevel: "medium",
|
|
Threats: []string{"test"},
|
|
NodeID: "test-node",
|
|
Evidence: []string{},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.AutomaticSecurityResponse(ctx, threat)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error from security response, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "security response failed") {
|
|
t.Errorf("Expected security response failed error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSimpleModel_Learn_HistoryBoundary(t *testing.T) {
|
|
extractor := &mockBlockFeatureExtractor{}
|
|
model := NewSimpleModel[BlockData]("node-1", extractor)
|
|
|
|
// Add more than 10000 examples to trigger history truncation
|
|
examples := make([]TrainingExample[BlockData], 10001)
|
|
for i := range examples {
|
|
examples[i] = TrainingExample[BlockData]{
|
|
Input: BlockData{
|
|
Height: uint64(i),
|
|
Timestamp: time.Now(),
|
|
},
|
|
Output: Decision[BlockData]{
|
|
ID: fmt.Sprintf("decision-%d", i),
|
|
Action: "approve",
|
|
Timestamp: time.Now(),
|
|
},
|
|
Feedback: 1.0,
|
|
NodeID: "node-1",
|
|
Weight: 1.0,
|
|
}
|
|
}
|
|
|
|
err := model.Learn(examples)
|
|
if err != nil {
|
|
t.Fatalf("Learn failed: %v", err)
|
|
}
|
|
|
|
// History should be capped at 10000
|
|
if len(model.history) > 10000 {
|
|
t.Errorf("Expected history to be capped at 10000, got %d", len(model.history))
|
|
}
|
|
}
|
|
|
|
func TestSimpleModel_LoadState_LearningRate(t *testing.T) {
|
|
extractor := &mockBlockFeatureExtractor{}
|
|
model := NewSimpleModel[BlockData]("node-1", extractor)
|
|
|
|
state := map[string]interface{}{
|
|
"learning_rate": 0.05,
|
|
}
|
|
|
|
err := model.LoadState(state)
|
|
if err != nil {
|
|
t.Fatalf("LoadState failed: %v", err)
|
|
}
|
|
|
|
if model.learningRate != 0.05 {
|
|
t.Errorf("Expected learning rate 0.05, got %f", model.learningRate)
|
|
}
|
|
}
|
|
|
|
func TestSimpleModel_LoadState_EmptyState(t *testing.T) {
|
|
extractor := &mockBlockFeatureExtractor{}
|
|
model := NewSimpleModel[BlockData]("node-1", extractor)
|
|
|
|
// Load empty state
|
|
err := model.LoadState(map[string]interface{}{})
|
|
if err != nil {
|
|
t.Fatalf("LoadState with empty state failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestUpgradeFeatureExtractor_CriticalRisk(t *testing.T) {
|
|
extractor := NewUpgradeFeatures()
|
|
|
|
data := UpgradeData{
|
|
Version: "v3.0.0",
|
|
Changes: []string{"breaking change"},
|
|
Risk: "critical",
|
|
TestResults: []string{"pass"},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
features := extractor.Extract(data)
|
|
|
|
riskScore := features["risk_score"]
|
|
if riskScore != 1.0 {
|
|
t.Errorf("Expected risk_score 1.0 for critical, got %f", riskScore)
|
|
}
|
|
}
|
|
|
|
func TestUpgradeFeatureExtractor_DefaultRisk(t *testing.T) {
|
|
extractor := NewUpgradeFeatures()
|
|
|
|
data := UpgradeData{
|
|
Version: "v1.0.0",
|
|
Changes: []string{"change"},
|
|
Risk: "undefined_risk_level", // Unknown risk level
|
|
TestResults: []string{"pass"},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
features := extractor.Extract(data)
|
|
|
|
riskScore := features["risk_score"]
|
|
// Default/unknown risk should be 0.0
|
|
if riskScore != 0.0 {
|
|
t.Errorf("Expected risk_score 0.0 for unknown risk, got %f", riskScore)
|
|
}
|
|
}
|
|
|
|
// === AI.GO ADDITIONAL TESTS ===
|
|
|
|
func TestSimpleAgent_ResolveDispute_DisputeDeletedDuringResolve(t *testing.T) {
|
|
// This tests the case where dispute is deleted between read and write
|
|
agent, model, _ := newTestAgent()
|
|
|
|
disputeID := "dispute-1"
|
|
agent.AddDispute(disputeID, "fork", "chain-1", []string{"a", "b"}, []string{"e1"})
|
|
|
|
// Create a model that simulates slow response
|
|
model.responses["How should dispute dispute-1 be resolved based on evidence?"] = &SimpleDecision{
|
|
Action: "approve",
|
|
Confidence: 0.9,
|
|
Reasoning: "Test resolution",
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
// Delete the dispute while resolving (simulated by another goroutine scenario)
|
|
// In practice, this tests the nil check in ResolveDispute
|
|
ctx := context.Background()
|
|
decision, err := agent.ResolveDispute(ctx, disputeID)
|
|
|
|
if err != nil {
|
|
t.Fatalf("ResolveDispute failed: %v", err)
|
|
}
|
|
|
|
if decision == nil {
|
|
t.Fatal("Decision is nil")
|
|
}
|
|
}
|
|
|
|
// === AGENT.GO ADDITIONAL TESTS (ProposeDecision flow) ===
|
|
|
|
// mockPhotonEmitter mocks the photon emitter
|
|
type mockPhotonEmitter struct {
|
|
emitErr error
|
|
nodes []interface{}
|
|
}
|
|
|
|
func (m *mockPhotonEmitter) Emit(proposal interface{}) ([]interface{}, error) {
|
|
if m.emitErr != nil {
|
|
return nil, m.emitErr
|
|
}
|
|
return m.nodes, nil
|
|
}
|
|
|
|
// Test ProposeDecision - requires photon which is nil in most tests
|
|
// We need to test the private methods directly
|
|
|
|
func TestAgent_UpdateHallucination(t *testing.T) {
|
|
model := &mockAgentModel[BlockData]{}
|
|
agent := New[BlockData]("test-node", model, nil, nil)
|
|
|
|
decision := &Decision[BlockData]{
|
|
ID: "test-decision",
|
|
Action: "approve",
|
|
Confidence: 0.9,
|
|
Data: BlockData{
|
|
Height: 100,
|
|
Timestamp: time.Now(),
|
|
},
|
|
Reasoning: "Test decision",
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
// Call updateHallucination
|
|
agent.updateHallucination(decision)
|
|
|
|
// Check that hallucination was created
|
|
if len(agent.hallucinations) != 1 {
|
|
t.Errorf("Expected 1 hallucination, got %d", len(agent.hallucinations))
|
|
}
|
|
|
|
// Find the hallucination
|
|
for _, h := range agent.hallucinations {
|
|
if h.Confidence != 0.9 {
|
|
t.Errorf("Expected confidence 0.9, got %f", h.Confidence)
|
|
}
|
|
if h.ModelID != "test-node" {
|
|
t.Errorf("Expected model ID 'test-node', got '%s'", h.ModelID)
|
|
}
|
|
if len(h.Evidence) != 1 {
|
|
t.Errorf("Expected 1 evidence, got %d", len(h.Evidence))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAgent_FocusConsensus(t *testing.T) {
|
|
model := &mockAgentModel[BlockData]{}
|
|
agent := New[BlockData]("test-node", model, nil, nil)
|
|
|
|
proposal := &Proposal[BlockData]{
|
|
ID: "test-proposal",
|
|
NodeID: "test-node",
|
|
Decision: &Decision[BlockData]{
|
|
ID: "test-decision",
|
|
Action: "approve",
|
|
Confidence: 0.9,
|
|
Data: BlockData{Height: 100},
|
|
},
|
|
Confidence: 0.9,
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
decision, err := agent.focusConsensus(ctx, proposal)
|
|
|
|
if err != nil {
|
|
t.Fatalf("focusConsensus failed: %v", err)
|
|
}
|
|
|
|
if decision == nil {
|
|
t.Fatal("Decision is nil")
|
|
}
|
|
|
|
if agent.consensus.Phase != PhaseFocus {
|
|
t.Errorf("Expected phase %s, got %s", PhaseFocus, agent.consensus.Phase)
|
|
}
|
|
}
|
|
|
|
func TestAgent_PrismValidation(t *testing.T) {
|
|
model := &mockAgentModel[BlockData]{}
|
|
agent := New[BlockData]("test-node", model, nil, nil)
|
|
|
|
// Add participants required for prism validation
|
|
agent.consensus.Participants = []string{"node1", "node2", "node3"}
|
|
|
|
// Valid decision
|
|
decision := &Decision[BlockData]{
|
|
ID: "test-decision",
|
|
Action: "approve",
|
|
Confidence: 0.9,
|
|
Data: BlockData{Height: 100},
|
|
}
|
|
|
|
err := agent.prismValidation(decision)
|
|
if err != nil {
|
|
t.Fatalf("prismValidation failed: %v", err)
|
|
}
|
|
|
|
if agent.consensus.Phase != PhasePrism {
|
|
t.Errorf("Expected phase %s, got %s", PhasePrism, agent.consensus.Phase)
|
|
}
|
|
|
|
// Invalid decision (nil)
|
|
err = agent.prismValidation(nil)
|
|
if err == nil {
|
|
t.Fatal("Expected error for nil decision, got nil")
|
|
}
|
|
|
|
// Invalid decision (empty ID)
|
|
emptyIDDecision := &Decision[BlockData]{
|
|
ID: "",
|
|
}
|
|
err = agent.prismValidation(emptyIDDecision)
|
|
if err == nil {
|
|
t.Fatal("Expected error for empty ID decision, got nil")
|
|
}
|
|
}
|
|
|
|
func TestAgent_HorizonFinalization(t *testing.T) {
|
|
model := &mockAgentModel[BlockData]{}
|
|
agent := New[BlockData]("test-node", model, nil, nil)
|
|
|
|
decision := &Decision[BlockData]{
|
|
ID: "test-decision",
|
|
Action: "approve",
|
|
Confidence: 0.9,
|
|
Data: BlockData{Height: 100},
|
|
}
|
|
|
|
finalDecision, err := agent.horizonFinalization(decision)
|
|
if err != nil {
|
|
t.Fatalf("horizonFinalization failed: %v", err)
|
|
}
|
|
|
|
if finalDecision != decision {
|
|
t.Error("Expected same decision to be returned")
|
|
}
|
|
|
|
if agent.consensus.Phase != PhaseHorizon {
|
|
t.Errorf("Expected phase %s, got %s", PhaseHorizon, agent.consensus.Phase)
|
|
}
|
|
|
|
if agent.consensus.Finalized != decision {
|
|
t.Error("Expected consensus finalized to be set")
|
|
}
|
|
|
|
if agent.consensus.FinalizedAt.IsZero() {
|
|
t.Error("Expected FinalizedAt to be set")
|
|
}
|
|
}
|
|
|
|
func TestAgent_ProposeDecision_ModelError(t *testing.T) {
|
|
model := &mockAgentModel[BlockData]{
|
|
proposeErr: errors.New("model propose failed"),
|
|
}
|
|
agent := New[BlockData]("test-node", model, nil, nil)
|
|
|
|
ctx := context.Background()
|
|
_, err := agent.ProposeDecision(ctx, BlockData{Height: 100}, nil)
|
|
|
|
if err == nil {
|
|
t.Fatal("Expected error from model propose, got nil")
|
|
}
|
|
|
|
if !containsString(err.Error(), "photon proposal failed") {
|
|
t.Errorf("Expected photon proposal failed error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestAgent_BroadcastProposal tests broadcastProposal when photon is nil
|
|
// This will cause a panic, which we need to recover from
|
|
func TestAgent_BroadcastProposal_NilPhoton(t *testing.T) {
|
|
model := &mockAgentModel[BlockData]{}
|
|
agent := New[BlockData]("test-node", model, nil, nil) // nil photon
|
|
|
|
proposal := &Proposal[BlockData]{
|
|
ID: "test-proposal",
|
|
NodeID: "test-node",
|
|
Decision: &Decision[BlockData]{
|
|
ID: "test-decision",
|
|
Action: "approve",
|
|
},
|
|
}
|
|
|
|
// broadcastProposal should panic with nil photon
|
|
defer func() {
|
|
if r := recover(); r == nil {
|
|
t.Log("broadcastProposal with nil photon did not panic")
|
|
}
|
|
}()
|
|
|
|
// This should panic
|
|
_ = agent.broadcastProposal(proposal)
|
|
}
|
|
|
|
// === HELPER TYPES ===
|
|
|
|
// securityFeatureExtractor for SecurityData
|
|
type securityFeatureExtractor struct{}
|
|
|
|
func (e *securityFeatureExtractor) Extract(data SecurityData) map[string]float64 {
|
|
threatScore := 0.0
|
|
switch data.ThreatLevel {
|
|
case "low":
|
|
threatScore = 0.25
|
|
case "medium":
|
|
threatScore = 0.5
|
|
case "high":
|
|
threatScore = 0.75
|
|
case "critical":
|
|
threatScore = 1.0
|
|
}
|
|
|
|
return map[string]float64{
|
|
"threat_score": threatScore,
|
|
"threat_count": float64(len(data.Threats)),
|
|
"evidence_count": float64(len(data.Evidence)),
|
|
}
|
|
}
|
|
|
|
func (e *securityFeatureExtractor) Names() []string {
|
|
return []string{"threat_score", "threat_count", "evidence_count"}
|
|
}
|
|
|
|
// disputeFeatureExtractor for DisputeData
|
|
type disputeFeatureExtractor struct{}
|
|
|
|
func (e *disputeFeatureExtractor) Extract(data DisputeData) map[string]float64 {
|
|
return map[string]float64{
|
|
"party_count": float64(len(data.Parties)),
|
|
"evidence_count": float64(len(data.Evidence)),
|
|
"age_hours": time.Since(data.Timestamp).Hours(),
|
|
}
|
|
}
|
|
|
|
func (e *disputeFeatureExtractor) Names() []string {
|
|
return []string{"party_count", "evidence_count", "age_hours"}
|
|
}
|
|
|
|
// errorFeatureExtractor that returns empty features
|
|
type errorFeatureExtractor struct{}
|
|
|
|
func (e *errorFeatureExtractor) Extract(data BlockData) map[string]float64 {
|
|
return map[string]float64{}
|
|
}
|
|
|
|
func (e *errorFeatureExtractor) Names() []string {
|
|
return []string{}
|
|
}
|
|
|
|
// testModuleWithInitError for testing initialization errors
|
|
type testModuleWithInitError struct {
|
|
id string
|
|
typ ModuleType
|
|
initErr error
|
|
}
|
|
|
|
func (m *testModuleWithInitError) ID() string { return m.id }
|
|
func (m *testModuleWithInitError) Type() ModuleType { return m.typ }
|
|
|
|
func (m *testModuleWithInitError) Initialize(ctx context.Context, config Config) error {
|
|
return m.initErr
|
|
}
|
|
|
|
func (m *testModuleWithInitError) Process(ctx context.Context, input Input) (Output, error) {
|
|
return Output{Type: "processed", Data: map[string]interface{}{}}, nil
|
|
}
|
|
|
|
func (m *testModuleWithInitError) Start(ctx context.Context) error { return nil }
|
|
func (m *testModuleWithInitError) Stop(ctx context.Context) error { return nil }
|
|
|
|
// containsString is a simpler helper for string containment check
|
|
func containsString(s, substr string) bool {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// mockSecurityModel implements Model[SecurityData] for testing
|
|
type mockSecurityModel struct {
|
|
action string
|
|
confidence float64
|
|
}
|
|
|
|
func (m *mockSecurityModel) Decide(ctx context.Context, input SecurityData, context map[string]interface{}) (*Decision[SecurityData], error) {
|
|
return &Decision[SecurityData]{
|
|
ID: generateID(),
|
|
Action: m.action,
|
|
Data: input,
|
|
Confidence: m.confidence,
|
|
Reasoning: "mock security decision",
|
|
Context: context,
|
|
Timestamp: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
func (m *mockSecurityModel) ProposeDecision(ctx context.Context, input SecurityData) (*Proposal[SecurityData], error) {
|
|
decision, _ := m.Decide(ctx, input, nil)
|
|
return &Proposal[SecurityData]{
|
|
ID: generateID(),
|
|
NodeID: "security-node",
|
|
Decision: decision,
|
|
Evidence: []Evidence[SecurityData]{},
|
|
Weight: 1.0,
|
|
Confidence: m.confidence,
|
|
Timestamp: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
func (m *mockSecurityModel) ValidateProposal(proposal *Proposal[SecurityData]) (float64, error) {
|
|
return m.confidence, nil
|
|
}
|
|
|
|
func (m *mockSecurityModel) Learn(examples []TrainingExample[SecurityData]) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockSecurityModel) UpdateWeights(gradients []float64) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockSecurityModel) GetState() map[string]interface{} {
|
|
return map[string]interface{}{}
|
|
}
|
|
|
|
func (m *mockSecurityModel) LoadState(state map[string]interface{}) error {
|
|
return nil
|
|
}
|
|
|
|
// mockSecurityModelWithError implements Model[SecurityData] for testing error paths
|
|
type mockSecurityModelWithError struct {
|
|
decideErr error
|
|
}
|
|
|
|
func (m *mockSecurityModelWithError) Decide(ctx context.Context, input SecurityData, context map[string]interface{}) (*Decision[SecurityData], error) {
|
|
return nil, m.decideErr
|
|
}
|
|
|
|
func (m *mockSecurityModelWithError) ProposeDecision(ctx context.Context, input SecurityData) (*Proposal[SecurityData], error) {
|
|
return nil, m.decideErr
|
|
}
|
|
|
|
func (m *mockSecurityModelWithError) ValidateProposal(proposal *Proposal[SecurityData]) (float64, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
func (m *mockSecurityModelWithError) Learn(examples []TrainingExample[SecurityData]) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockSecurityModelWithError) UpdateWeights(gradients []float64) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockSecurityModelWithError) GetState() map[string]interface{} {
|
|
return map[string]interface{}{}
|
|
}
|
|
|
|
func (m *mockSecurityModelWithError) LoadState(state map[string]interface{}) error {
|
|
return nil
|
|
}
|
|
|
|
// mockUpgradeModel implements Model[UpgradeData] for testing error paths
|
|
type mockUpgradeModel struct {
|
|
decideErr error
|
|
}
|
|
|
|
func (m *mockUpgradeModel) Decide(ctx context.Context, input UpgradeData, context map[string]interface{}) (*Decision[UpgradeData], error) {
|
|
return nil, m.decideErr
|
|
}
|
|
|
|
func (m *mockUpgradeModel) ProposeDecision(ctx context.Context, input UpgradeData) (*Proposal[UpgradeData], error) {
|
|
return nil, m.decideErr
|
|
}
|
|
|
|
func (m *mockUpgradeModel) ValidateProposal(proposal *Proposal[UpgradeData]) (float64, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
func (m *mockUpgradeModel) Learn(examples []TrainingExample[UpgradeData]) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockUpgradeModel) UpdateWeights(gradients []float64) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockUpgradeModel) GetState() map[string]interface{} {
|
|
return map[string]interface{}{}
|
|
}
|
|
|
|
func (m *mockUpgradeModel) LoadState(state map[string]interface{}) error {
|
|
return nil
|
|
}
|
|
|
|
// mockBlockModel implements Model[BlockData] for testing error paths
|
|
type mockBlockModel struct {
|
|
decideErr error
|
|
}
|
|
|
|
func (m *mockBlockModel) Decide(ctx context.Context, input BlockData, context map[string]interface{}) (*Decision[BlockData], error) {
|
|
return nil, m.decideErr
|
|
}
|
|
|
|
func (m *mockBlockModel) ProposeDecision(ctx context.Context, input BlockData) (*Proposal[BlockData], error) {
|
|
return nil, m.decideErr
|
|
}
|
|
|
|
func (m *mockBlockModel) ValidateProposal(proposal *Proposal[BlockData]) (float64, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
func (m *mockBlockModel) Learn(examples []TrainingExample[BlockData]) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockBlockModel) UpdateWeights(gradients []float64) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockBlockModel) GetState() map[string]interface{} {
|
|
return map[string]interface{}{}
|
|
}
|
|
|
|
func (m *mockBlockModel) LoadState(state map[string]interface{}) error {
|
|
return nil
|
|
}
|
|
|
|
// mockDisputeModel implements Model[DisputeData] for testing error paths
|
|
type mockDisputeModel struct {
|
|
decideErr error
|
|
}
|
|
|
|
func (m *mockDisputeModel) Decide(ctx context.Context, input DisputeData, context map[string]interface{}) (*Decision[DisputeData], error) {
|
|
return nil, m.decideErr
|
|
}
|
|
|
|
func (m *mockDisputeModel) ProposeDecision(ctx context.Context, input DisputeData) (*Proposal[DisputeData], error) {
|
|
return nil, m.decideErr
|
|
}
|
|
|
|
func (m *mockDisputeModel) ValidateProposal(proposal *Proposal[DisputeData]) (float64, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
func (m *mockDisputeModel) Learn(examples []TrainingExample[DisputeData]) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockDisputeModel) UpdateWeights(gradients []float64) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockDisputeModel) GetState() map[string]interface{} {
|
|
return map[string]interface{}{}
|
|
}
|
|
|
|
func (m *mockDisputeModel) LoadState(state map[string]interface{}) error {
|
|
return nil
|
|
}
|