mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 19:41:17 +00:00
Fixed an issue where all Docker containers were showing 'click to update' even when they were up to date. The root cause was comparing the wrong digest types: - Previously: Compared ImageID (local config hash) vs registry manifest digest - Now: Uses RepoDigests from image inspect, which is the actual manifest digest that Docker received from the registry when pulling the image For multi-arch images, the registry returns a manifest list digest, while Docker stores the platform-specific image config digest locally. These will never match, causing false positives for all multi-arch images. Changes: - Added ImageInspectWithRaw to dockerClient interface - Added getImageRepoDigest method to extract RepoDigest from image - Added matchesImageReference helper for Docker Hub naming conventions - Added tests for matchesImageReference Fixes #955
77 lines
2.1 KiB
Go
77 lines
2.1 KiB
Go
package ai
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/ai/baseline"
|
|
)
|
|
|
|
func TestBaselineStoreAdapter(t *testing.T) {
|
|
store := baseline.NewStore(baseline.StoreConfig{
|
|
MinSamples: 1,
|
|
})
|
|
|
|
err := store.Learn("node:pve1", "node", "cpu", []baseline.MetricPoint{
|
|
{Value: 10, Timestamp: time.Now().Add(-time.Minute)},
|
|
{Value: 10, Timestamp: time.Now()},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Learn: %v", err)
|
|
}
|
|
|
|
adapter := NewBaselineStoreAdapter(store)
|
|
if adapter == nil {
|
|
t.Fatalf("expected adapter")
|
|
}
|
|
|
|
mean, stddev, samples, ok := adapter.GetBaseline("node:pve1", "cpu")
|
|
if !ok {
|
|
t.Fatalf("expected baseline to exist")
|
|
}
|
|
if mean != 10 || stddev != 0 || samples != 2 {
|
|
t.Fatalf("unexpected baseline: mean=%v stddev=%v samples=%d", mean, stddev, samples)
|
|
}
|
|
|
|
severity, z, gotMean, gotStd, ok := adapter.CheckAnomaly("node:pve1", "cpu", 16)
|
|
if !ok {
|
|
t.Fatalf("expected anomaly check ok")
|
|
}
|
|
// With new practical thresholds: 6 point difference from stable baseline (stddev=0)
|
|
// should be flagged as medium severity (not critical, since we don't have variance data)
|
|
if severity != "medium" || gotMean != 10 || gotStd != 0 {
|
|
t.Fatalf("unexpected anomaly: severity=%q z=%v mean=%v stddev=%v", severity, z, gotMean, gotStd)
|
|
}
|
|
}
|
|
|
|
func TestBaselineStoreAdapter_NilStore(t *testing.T) {
|
|
adapter := &BaselineStoreAdapter{}
|
|
if _, _, _, ok := adapter.GetBaseline("r", "m"); ok {
|
|
t.Fatalf("expected ok=false")
|
|
}
|
|
if _, _, _, _, ok := adapter.CheckAnomaly("r", "m", 1); ok {
|
|
t.Fatalf("expected ok=false")
|
|
}
|
|
}
|
|
|
|
func TestBaselineStoreAdapter_NilFactory(t *testing.T) {
|
|
if NewBaselineStoreAdapter(nil) != nil {
|
|
t.Fatalf("expected nil adapter for nil store")
|
|
}
|
|
}
|
|
|
|
func TestBaselineStoreAdapter_MissingBaseline(t *testing.T) {
|
|
store := baseline.NewStore(baseline.StoreConfig{MinSamples: 1})
|
|
adapter := NewBaselineStoreAdapter(store)
|
|
if adapter == nil {
|
|
t.Fatalf("expected adapter")
|
|
}
|
|
|
|
if _, _, _, ok := adapter.GetBaseline("missing", "cpu"); ok {
|
|
t.Fatalf("expected ok=false for missing baseline")
|
|
}
|
|
|
|
if _, _, _, _, ok := adapter.CheckAnomaly("missing", "cpu", 42); ok {
|
|
t.Fatalf("expected ok=false for missing anomaly data")
|
|
}
|
|
}
|