package main import ( "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "metis/pkg/sentinel" ) func TestSentinelMainWritesHistoryAndPushesSnapshot(t *testing.T) { fakeDir := fakeSentinelCommands(t) t.Setenv("PATH", fakeDir+string(os.PathListSeparator)+os.Getenv("PATH")) historyDir := filepath.Join(t.TempDir(), "history") pushed := false srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { pushed = true if r.Method != http.MethodPost { t.Fatalf("expected POST, got %s", r.Method) } w.WriteHeader(http.StatusOK) })) defer srv.Close() t.Setenv("METIS_SENTINEL_RUN_ONCE", "1") t.Setenv("METIS_SENTINEL_OUT", historyDir) t.Setenv("METIS_SENTINEL_PUSH_URL", srv.URL) t.Setenv("METIS_SENTINEL_INTERVAL_SEC", "1") main() entries, err := os.ReadDir(historyDir) if err != nil { t.Fatalf("ReadDir history: %v", err) } if len(entries) != 1 { t.Fatalf("expected one history entry, got %d", len(entries)) } data, err := os.ReadFile(filepath.Join(historyDir, entries[0].Name())) if err != nil { t.Fatalf("ReadFile history: %v", err) } if !strings.Contains(string(data), `"hostname": "titan-13"`) { t.Fatalf("history file missing snapshot data: %s", data) } if !pushed { t.Fatal("expected pushSnapshot to POST to server") } } func TestSentinelHelpers(t *testing.T) { if got := getenvInt("METIS_SENTINEL_INTERVAL_SEC", 300); got != 300 { t.Fatalf("getenvInt fallback = %d", got) } t.Setenv("METIS_SENTINEL_INTERVAL_SEC", "5") if got := getenvInt("METIS_SENTINEL_INTERVAL_SEC", 300); got != 5 { t.Fatalf("getenvInt = %d", got) } dir := t.TempDir() snap := &sentinel.Snapshot{Hostname: "titan-13", Kernel: "6.6.63"} writeHistory(dir, snap) entries, err := os.ReadDir(dir) if err != nil { t.Fatalf("ReadDir: %v", err) } if len(entries) != 1 { t.Fatalf("expected one file, got %d", len(entries)) } srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { t.Fatalf("expected POST, got %s", r.Method) } var payload map[string]any if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { t.Fatalf("decode push body: %v", err) } w.WriteHeader(http.StatusOK) })) defer srv.Close() if err := pushSnapshot(srv.URL, snap); err != nil { t.Fatalf("pushSnapshot: %v", err) } } func fakeSentinelCommands(t *testing.T) string { t.Helper() dir := t.TempDir() write := func(name, body string) { path := filepath.Join(dir, name) if err := os.WriteFile(path, []byte("#!/usr/bin/env bash\nset -eu\n"+body+"\n"), 0o755); err != nil { t.Fatalf("write %s: %v", name, err) } } write("hostname", `printf 'titan-13\n'`) write("uname", `printf '6.6.63\n'`) write("k3s", `printf 'v1.31.5+k3s1\n'`) write("containerd", `printf '1.7.99\n'`) write("cat", `printf 'PRETTY_NAME="Metis OS"\n'`) write("dpkg-query", `case "${@: -1}" in containerd) printf '1.7.99\n' ;; k3s) printf 'v1.31.5+k3s1\n' ;; nvidia-container-toolkit) printf '1.16.2\n' ;; linux-image-raspi) printf '6.6.63\n' ;; *) printf '1.0.0\n' ;; esac`) write("rpm", `printf '1.0.0\n'`) return dir }