This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package unraid
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/itworx/pulse/internal/container"
|
||||
)
|
||||
|
||||
func TestContainerSourceAcceptsBoundedOperationalHeadroomAndRejectsOverflow(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
count int
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "bounded headroom", count: container.DefaultMaxContainers},
|
||||
{name: "overflow", count: container.DefaultMaxContainers + 1, wantErr: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
items := make([]map[string]any, 0, test.count)
|
||||
for index := 0; index < test.count; index++ {
|
||||
items = append(items, map[string]any{"id": fmt.Sprintf("id-%03d", index), "names": []string{fmt.Sprintf("/container-%03d", index)}, "state": "RUNNING", "status": "Up 1 hour", "autoStart": true})
|
||||
}
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"data": map[string]any{"docker": map[string]any{"containers": items}}})
|
||||
}))
|
||||
defer server.Close()
|
||||
client, err := New(server.URL, "test-key", server.Client())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
snapshot, err := (ContainerSource{Client: client}).Snapshot(context.Background())
|
||||
if test.wantErr {
|
||||
if err == nil {
|
||||
t.Fatal("expected oversized response rejection")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil || len(snapshot.Containers) != test.count {
|
||||
t.Fatalf("containers=%d err=%v", len(snapshot.Containers), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerSourceNormalizesDocumentedReadOnlyContainerFields(t *testing.T) {
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"data":{"docker":{"containers":[{"id":"two","names":["/zeta"],"state":"EXITED","status":"Exited (0) 2 days ago","autoStart":false},{"id":"one","names":"/alpha","state":"RUNNING","status":"Up 34 hours (healthy)","autoStart":true},{"id":"three","names":"/beta","state":"RUNNING","status":"Up 34 hours","autoStart":true},{"id":"four","names":"/gamma","state":"EXITED","status":"Restarting (137) 11 seconds ago","autoStart":false}]}}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
client, err := New(server.URL, "test-key", server.Client())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
now := time.Date(2026, 8, 10, 2, 0, 0, 0, time.UTC)
|
||||
snapshot, err := (ContainerSource{Client: client, Now: func() time.Time { return now }}).Snapshot(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(snapshot.Containers) != 4 || snapshot.Containers[0].Name != "zeta" || !snapshot.Containers[0].IntentionalStop {
|
||||
t.Fatalf("unexpected normalized snapshot: %+v", snapshot)
|
||||
}
|
||||
if snapshot.Containers[0].State != "exited" || snapshot.Containers[0].Health != "unknown" || snapshot.Containers[1].Health != "healthy" || snapshot.Containers[2].Health != "unknown" || snapshot.Containers[3].State != "restarting" || snapshot.Containers[3].IntentionalStop {
|
||||
t.Fatalf("runtime status was mistaken for health: %+v", snapshot.Containers)
|
||||
}
|
||||
if !snapshot.ObservedAt.Equal(now) || snapshot.Source.Type != "unraid" {
|
||||
t.Fatalf("unexpected source timestamps: %+v", snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerSourceRejectsOversizedOrInvalidPayload(t *testing.T) {
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"data":{"docker":{"containers":[{"id":"","names":[],"state":"running","status":"healthy","autoStart":true}]}}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
client, err := New(server.URL, "test-key", server.Client())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := (ContainerSource{Client: client}).Snapshot(context.Background()); err == nil {
|
||||
t.Fatal("expected invalid identity rejection")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerStatusClassification(t *testing.T) {
|
||||
healthCases := map[string]string{
|
||||
"healthy": "healthy", "Up 34 hours (healthy)": "healthy",
|
||||
"unhealthy": "unhealthy", "Up 2 minutes (unhealthy)": "unhealthy",
|
||||
"starting": "starting", "Up 3 seconds (health: starting)": "starting",
|
||||
"Up 34 hours": "unknown", "Exited (0) 2 days ago": "unknown",
|
||||
}
|
||||
for input, want := range healthCases {
|
||||
if got := containerHealth(input); got != want {
|
||||
t.Fatalf("health %q = %q, want %q", input, got, want)
|
||||
}
|
||||
}
|
||||
if got := containerRuntimeState("EXITED", "Restarting (137) 11 seconds ago"); got != "restarting" {
|
||||
t.Fatalf("runtime = %q, want restarting", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user