mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-26 12:56:21 +02:00
- Rename Go module: sentinel-community/gomcp -> syntrex/gomcp (50+ files) - Rename npm package: sentinel-dashboard -> syntrex-dashboard - Update Cargo.toml repository URL to syntrex/syntrex - Update all doc references from DmitrL-dev/AISecurity to syntrex - Add root Makefile (build-all, test-all, lint-all, clean-all) - Add MIT LICENSE - Add .editorconfig (Go/Rust/TS/C cross-language) - Add .github/workflows/ci.yml (Go + Rust + Dashboard) - Add dashboard next.config.ts and .env.example - Clean ARCHITECTURE.md: remove brain/immune/strike/micro-swarm, fix 61->67 engines
229 lines
5.9 KiB
Go
229 lines
5.9 KiB
Go
package hardware
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/syntrex/gomcp/internal/domain/alert"
|
|
)
|
|
|
|
func testConfig(dir string) LeashConfig {
|
|
return LeashConfig{
|
|
KeyPath: filepath.Join(dir, ".sentinel_key"),
|
|
LeashPath: filepath.Join(dir, ".sentinel_leash"),
|
|
CheckInterval: 100 * time.Millisecond,
|
|
MissThreshold: 3,
|
|
SignalDir: dir,
|
|
}
|
|
}
|
|
|
|
func TestLeash_ArmedWhenKeyExists(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := testConfig(dir)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
|
|
bus := alert.NewBus(10)
|
|
leash := NewLeash(cfg, bus, nil, nil)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go leash.Start(ctx)
|
|
time.Sleep(120 * time.Millisecond)
|
|
assert.Equal(t, LeashArmed, leash.Status())
|
|
cancel()
|
|
}
|
|
|
|
func TestLeash_TriggersOnMissingKey(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := testConfig(dir)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
|
|
var triggered atomic.Int32
|
|
bus := alert.NewBus(10)
|
|
leash := NewLeash(cfg, bus, nil, func() { triggered.Add(1) })
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go leash.Start(ctx)
|
|
time.Sleep(120 * time.Millisecond)
|
|
|
|
os.Remove(cfg.KeyPath)
|
|
time.Sleep(350 * time.Millisecond)
|
|
assert.GreaterOrEqual(t, triggered.Load(), int32(1))
|
|
assert.Equal(t, LeashTriggered, leash.Status())
|
|
cancel()
|
|
}
|
|
|
|
func TestLeash_ReArmsWhenKeyRestored(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := testConfig(dir)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
|
|
bus := alert.NewBus(10)
|
|
leash := NewLeash(cfg, bus, nil, nil)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go leash.Start(ctx)
|
|
time.Sleep(120 * time.Millisecond)
|
|
|
|
os.Remove(cfg.KeyPath)
|
|
time.Sleep(120 * time.Millisecond)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
time.Sleep(120 * time.Millisecond)
|
|
|
|
assert.Equal(t, LeashArmed, leash.Status())
|
|
cancel()
|
|
}
|
|
|
|
func TestLeash_SignalExtract(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := testConfig(dir)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
|
|
var extracted atomic.Int32
|
|
bus := alert.NewBus(10)
|
|
leash := NewLeash(cfg, bus, func() { extracted.Add(1) }, nil)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go leash.Start(ctx)
|
|
time.Sleep(120 * time.Millisecond)
|
|
|
|
os.WriteFile(filepath.Join(dir, "signal_extract"), nil, 0o644)
|
|
time.Sleep(120 * time.Millisecond)
|
|
assert.GreaterOrEqual(t, extracted.Load(), int32(1))
|
|
cancel()
|
|
}
|
|
|
|
func TestLeash_SignalApoptosis(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := testConfig(dir)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
|
|
var triggered atomic.Int32
|
|
bus := alert.NewBus(10)
|
|
leash := NewLeash(cfg, bus, nil, func() { triggered.Add(1) })
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go leash.Start(ctx)
|
|
time.Sleep(120 * time.Millisecond)
|
|
|
|
os.WriteFile(filepath.Join(dir, "signal_apoptosis"), nil, 0o644)
|
|
time.Sleep(120 * time.Millisecond)
|
|
assert.GreaterOrEqual(t, triggered.Load(), int32(1))
|
|
assert.Equal(t, LeashTriggered, leash.Status())
|
|
cancel()
|
|
}
|
|
|
|
func TestLeashStatus_String(t *testing.T) {
|
|
assert.Equal(t, "DISARMED", LeashDisarmed.String())
|
|
assert.Equal(t, "ARMED", LeashArmed.String())
|
|
assert.Equal(t, "TRIGGERED", LeashTriggered.String())
|
|
}
|
|
|
|
// --- v3.2 State Machine Tests ---
|
|
|
|
func TestParseMode(t *testing.T) {
|
|
assert.Equal(t, ModeArmed, ParseMode("ARMED"))
|
|
assert.Equal(t, ModeArmed, ParseMode("armed"))
|
|
assert.Equal(t, ModeArmed, ParseMode("anything"))
|
|
assert.Equal(t, ModeArmed, ParseMode(""))
|
|
assert.Equal(t, ModeZeroG, ParseMode("ZERO-G"))
|
|
assert.Equal(t, ModeZeroG, ParseMode("ZEROG"))
|
|
assert.Equal(t, ModeZeroG, ParseMode("ZERO_G"))
|
|
assert.Equal(t, ModeZeroG, ParseMode(" zero-g "))
|
|
assert.Equal(t, ModeSafe, ParseMode("SAFE"))
|
|
assert.Equal(t, ModeSafe, ParseMode("READ-ONLY"))
|
|
assert.Equal(t, ModeSafe, ParseMode("READONLY"))
|
|
}
|
|
|
|
func TestSystemMode_String(t *testing.T) {
|
|
assert.Equal(t, "ARMED", ModeArmed.String())
|
|
assert.Equal(t, "ZERO-G", ModeZeroG.String())
|
|
assert.Equal(t, "SAFE", ModeSafe.String())
|
|
}
|
|
|
|
func TestLeash_ModeDefault(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := testConfig(dir)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
|
|
bus := alert.NewBus(10)
|
|
leash := NewLeash(cfg, bus, nil, nil)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go leash.Start(ctx)
|
|
time.Sleep(120 * time.Millisecond)
|
|
|
|
// No .sentinel_leash file → ModeArmed.
|
|
assert.Equal(t, ModeArmed, leash.Mode())
|
|
cancel()
|
|
}
|
|
|
|
func TestLeash_ModeZeroG(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := testConfig(dir)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
os.WriteFile(cfg.LeashPath, []byte("ZERO-G"), 0o644)
|
|
|
|
bus := alert.NewBus(10)
|
|
leash := NewLeash(cfg, bus, nil, nil)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go leash.Start(ctx)
|
|
time.Sleep(120 * time.Millisecond)
|
|
|
|
assert.Equal(t, ModeZeroG, leash.Mode())
|
|
cancel()
|
|
}
|
|
|
|
func TestLeash_ModeSafe(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := testConfig(dir)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
os.WriteFile(cfg.LeashPath, []byte("SAFE"), 0o644)
|
|
|
|
bus := alert.NewBus(10)
|
|
leash := NewLeash(cfg, bus, nil, nil)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go leash.Start(ctx)
|
|
time.Sleep(120 * time.Millisecond)
|
|
|
|
assert.Equal(t, ModeSafe, leash.Mode())
|
|
cancel()
|
|
}
|
|
|
|
func TestLeash_ModeTransition(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := testConfig(dir)
|
|
os.WriteFile(cfg.KeyPath, nil, 0o644)
|
|
|
|
var transitions atomic.Int32
|
|
bus := alert.NewBus(10)
|
|
leash := NewLeash(cfg, bus, nil, nil)
|
|
leash.SetModeChangeCallback(func(m SystemMode) {
|
|
transitions.Add(1)
|
|
})
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go leash.Start(ctx)
|
|
time.Sleep(120 * time.Millisecond)
|
|
|
|
// ARMED → ZERO-G.
|
|
require.NoError(t, os.WriteFile(cfg.LeashPath, []byte("ZERO-G"), 0o644))
|
|
time.Sleep(200 * time.Millisecond)
|
|
assert.Equal(t, ModeZeroG, leash.Mode())
|
|
|
|
// ZERO-G → SAFE.
|
|
require.NoError(t, os.WriteFile(cfg.LeashPath, []byte("SAFE"), 0o644))
|
|
time.Sleep(200 * time.Millisecond)
|
|
assert.Equal(t, ModeSafe, leash.Mode())
|
|
|
|
assert.GreaterOrEqual(t, transitions.Load(), int32(2))
|
|
cancel()
|
|
}
|