guenther/internal/transform/engine_test.go

106 lines
3.2 KiB
Go

package transform
import (
"context"
"testing"
"time"
"codeberg.org/pata1704/guenther/internal/config"
"codeberg.org/pata1704/guenther/pkg/types"
"github.com/stretchr/testify/assert"
)
func TestTransformEngine_Fusion(t *testing.T) {
logChan := make(chan types.LogEvent, 100)
metricChan := make(chan types.MetricSnapshot, 100)
serviceStatusChan := make(chan types.ServiceStatus, 100)
featureChan := make(chan types.FeatureVector, 100)
healthChan := make(chan types.StageHealth, 10)
cfg := &config.Config{}
cfg.Transformation.WindowSize = 1 * time.Second
cfg.Transformation.DbPath = ":memory:"
engine, err := NewTransformEngine(cfg, logChan, metricChan, serviceStatusChan, featureChan, healthChan)
assert.NoError(t, err)
baseTime := time.Date(2026, 1, 1, 12, 0, 0, 0, time.Local)
// 1. Send data for first window
metricChan <- types.MetricSnapshot{
Timestamp: baseTime,
CPUPercent: 50.0,
MemoryUsedMB: 1000,
MemoryDirtyMB: 100,
NetworkInMBps: 10.0,
NetworkOutMBps: 20.0,
TCPRetransPerS: 5,
NetPacketsInPerS: 100,
NetPacketsOutPerS: 200,
}
// 2. Start engine and wait for first window
ctx, cancel := context.WithCancel(context.Background())
engine.Start(ctx)
defer func() {
cancel()
engine.Wait()
}()
select {
case fv := <-featureChan:
assert.Equal(t, 50.0, fv.AvgCPUPercent)
// Deltas are absolute value on first window because tracker starts at 0
assert.Equal(t, 10.0, fv.DeltaNetIn)
case <-time.After(2 * time.Second):
t.Fatal("Timeout waiting for first FeatureVector")
}
// 3. Send data for second window (triggers deltas)
secondTime := baseTime.Add(cfg.Transformation.WindowSize)
metricChan <- types.MetricSnapshot{
Timestamp: secondTime,
CPUPercent: 60.0,
MemoryUsedMB: 1000,
MemoryDirtyMB: 200,
NetworkInMBps: 15.0, // DeltaNetIn = 15.0 - 10.0 = 5.0
NetworkOutMBps: 20.0,
TCPRetransPerS: 10, // DeltaTCPRetrans = 10.0 - 5.0 = 5.0
NetPacketsInPerS: 150,
NetPacketsOutPerS: 200,
}
select {
case fv := <-featureChan:
// Check original logic
assert.Equal(t, 60.0, fv.AvgCPUPercent)
// Check new delta features
assert.Equal(t, 5.0, fv.DeltaNetIn)
assert.Equal(t, 5.0, fv.DeltaTCPRetrans)
// Check ratio features
// MemPressure = dirty / (used + 1) = 200/1001
expectedPressure := 200.0 / 1001.0
assert.InDelta(t, expectedPressure, fv.MemPressure, 1e-9)
// NetAsymmetry = in / (out + 1e-3) = 15/20.001
expectedAsym := 15.0 / 20.001
assert.InDelta(t, expectedAsym, fv.NetAsymmetry, 1e-9)
// Check NormalizedVector length (should be 45 base + params)
assert.GreaterOrEqual(t, len(fv.NormalizedVector), 45)
// Verify slots 39-44 (Engineered Features tail)
nv := fv.NormalizedVector
assert.Equal(t, 5.0, nv[39]) // DeltaNetIn
assert.Equal(t, 5.0, nv[40]) // DeltaTCPRetrans
// TcpRollStd and NetRollStd will have values (even if just 2 pts)
assert.Greater(t, nv[41], 0.0) // TcpRollStd (10 and 5)
assert.Equal(t, 0.0, nv[42]) // NetRollStd (20 and 20 -> std=0)
assert.InDelta(t, expectedPressure, nv[43], 1e-9) // MemPressure
assert.InDelta(t, expectedAsym, nv[44], 1e-9) // NetAsymmetry
case <-time.After(2 * time.Second):
t.Fatal("Timeout waiting for second FeatureVector")
}
}