gomcp/internal/domain/pipeline/pipeline_test.go

142 lines
3.7 KiB
Go

package pipeline
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/syntrex-lab/gomcp/internal/domain/circuitbreaker"
"github.com/syntrex-lab/gomcp/internal/domain/entropy"
"github.com/syntrex-lab/gomcp/internal/domain/oracle"
)
func TestPipeline_AllowsNormalText(t *testing.T) {
p := New(
entropy.NewGate(nil),
nil, // no distiller
oracle.New(oracle.DefaultRules()),
circuitbreaker.New(nil),
&Config{SkipDistill: true},
)
r := p.Process(context.Background(), "read user profile data")
assert.True(t, r.IsAllowed)
assert.False(t, r.IsBlocked)
assert.Equal(t, StageComplete, r.Stage)
assert.Equal(t, "HEALTHY", r.CircuitState)
}
func TestPipeline_BlocksHighEntropy(t *testing.T) {
p := New(
entropy.NewGate(&entropy.GateConfig{MaxEntropy: 3.0}),
nil,
oracle.New(oracle.DefaultRules()),
circuitbreaker.New(nil),
&Config{SkipDistill: true},
)
r := p.Process(context.Background(), "x7#kQ9!mZ2$pW4&nR6*jL8@cF0^tB3yH5%vD1")
assert.True(t, r.IsBlocked)
assert.Equal(t, StageEntropy, r.BlockStage)
assert.Contains(t, r.BlockReason, "chaotic")
}
func TestPipeline_OracleDeniesExec(t *testing.T) {
p := New(
entropy.NewGate(nil),
nil,
oracle.New(oracle.DefaultRules()),
circuitbreaker.New(nil),
&Config{SkipDistill: true},
)
r := p.Process(context.Background(), "execute shell command rm -rf slash")
assert.True(t, r.IsBlocked)
assert.Equal(t, StageOracle, r.BlockStage)
assert.Contains(t, r.BlockReason, "denied")
}
func TestPipeline_CircuitBreakerBlocks(t *testing.T) {
breaker := circuitbreaker.New(&circuitbreaker.Config{
DegradeThreshold: 1,
OpenThreshold: 2,
})
p := New(
entropy.NewGate(nil),
nil,
oracle.New(oracle.DefaultRules()),
breaker,
&Config{SkipDistill: true},
)
// Force breaker to OPEN state.
breaker.RecordAnomaly("test1")
breaker.RecordAnomaly("test2")
assert.Equal(t, circuitbreaker.StateOpen, breaker.CurrentState())
r := p.Process(context.Background(), "read data")
assert.True(t, r.IsBlocked)
assert.Equal(t, StageBlocked, r.BlockStage)
assert.Contains(t, r.BlockReason, "circuit breaker")
}
func TestPipeline_AnomaliesDegradeCircuit(t *testing.T) {
breaker := circuitbreaker.New(&circuitbreaker.Config{DegradeThreshold: 2})
p := New(
entropy.NewGate(nil),
nil,
oracle.New(oracle.DefaultRules()),
breaker,
&Config{SkipDistill: true},
)
// Two denied actions → 2 anomalies → degrade.
p.Process(context.Background(), "execute shell command one")
p.Process(context.Background(), "run another shell command two")
assert.Equal(t, circuitbreaker.StateDegraded, breaker.CurrentState())
}
func TestPipeline_CleanSignalsRecover(t *testing.T) {
breaker := circuitbreaker.New(&circuitbreaker.Config{
DegradeThreshold: 1,
RecoveryThreshold: 2,
})
p := New(
entropy.NewGate(nil),
nil,
oracle.New(oracle.DefaultRules()),
breaker,
&Config{SkipDistill: true},
)
// Trigger degraded.
breaker.RecordAnomaly("test")
assert.Equal(t, circuitbreaker.StateDegraded, breaker.CurrentState())
// Clean signals recover.
p.Process(context.Background(), "read data from storage")
p.Process(context.Background(), "list all available items")
assert.Equal(t, circuitbreaker.StateHealthy, breaker.CurrentState())
}
func TestPipeline_NoGateNoOracle(t *testing.T) {
p := New(nil, nil, nil, circuitbreaker.New(nil), nil)
r := p.Process(context.Background(), "anything goes")
assert.True(t, r.IsAllowed)
assert.Equal(t, StageComplete, r.Stage)
}
func TestPipeline_DurationMeasured(t *testing.T) {
p := New(
entropy.NewGate(nil),
nil,
oracle.New(oracle.DefaultRules()),
circuitbreaker.New(nil),
&Config{SkipDistill: true},
)
r := p.Process(context.Background(), "read something")
assert.GreaterOrEqual(t, r.DurationMs, int64(0))
}