Files
ITWorx Pulse release export bd774932d5
Public source validation / validate (push) Failing after 3m8s
Publish ITWorx Pulse source
2026-09-03 02:09:19 +02:00

106 lines
4.3 KiB
Go

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)
}
}