package api import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "gitea.dooplex.hu/admin/felhom-hub/internal/store" ) // doArtifacts GETs /artifacts/{id} with the passphrase header (the do() helper only sets Bearer). func doArtifacts(h *Handler, customerID, pw string) *httptest.ResponseRecorder { req := httptest.NewRequest(http.MethodGet, "/api/v1/artifacts/"+customerID, nil) if pw != "" { req.Header.Set("X-Retrieval-Password", pw) } rr := httptest.NewRecorder() h.ServeHTTP(rr, req) return rr } // Scenario A — a recorded manifest is returned verbatim. func TestArtifacts_ReturnsRecordedSet(t *testing.T) { h, st, _ := newTestHandler(t) st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ckey", RetrievalPassword: "pass-phrase"}) if err := st.SetArtifactManifest(store.ArtifactManifest{ AgentVersion: "0.43.0", AgentSHA256: strings.Repeat("a", 64), GoldenVersion: "0.85.1", GoldenSHA256: strings.Repeat("b", 64), }); err != nil { t.Fatalf("SetArtifactManifest: %v", err) } rr := doArtifacts(h, "c1", "pass-phrase") if rr.Code != http.StatusOK { t.Fatalf("status = %d, body=%s", rr.Code, rr.Body.String()) } var got artifactManifestResponse if err := json.Unmarshal(rr.Body.Bytes(), &got); err != nil { t.Fatalf("decode: %v", err) } if got.Agent.Version != "0.43.0" || got.Agent.SHA256 != strings.Repeat("a", 64) { t.Errorf("agent = %+v", got.Agent) } if got.Golden.Version != "0.85.1" || got.Golden.SHA256 != strings.Repeat("b", 64) { t.Errorf("golden = %+v", got.Golden) } } // Scenario B — an unset manifest returns 200 with empty fields (NOT an error). The script falls back // to the local golden / fails clearly on a missing binary. func TestArtifacts_EmptyWhenUnset(t *testing.T) { h, st, _ := newTestHandler(t) st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ckey", RetrievalPassword: "pass-phrase"}) rr := doArtifacts(h, "c1", "pass-phrase") if rr.Code != http.StatusOK { t.Fatalf("status = %d, body=%s", rr.Code, rr.Body.String()) } var got artifactManifestResponse if err := json.Unmarshal(rr.Body.Bytes(), &got); err != nil { t.Fatalf("decode: %v", err) } if got.Agent.Version != "" || got.Agent.SHA256 != "" || got.Golden.Version != "" || got.Golden.SHA256 != "" { t.Errorf("expected all-empty manifest, got %+v", got) } } // Scenario C — wrong passphrase is 401 (auth mirrors config-retrieve). func TestArtifacts_WrongPassphrase(t *testing.T) { h, st, _ := newTestHandler(t) st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ckey", RetrievalPassword: "pass-phrase"}) rr := doArtifacts(h, "c1", "wrong") if rr.Code != http.StatusUnauthorized { t.Fatalf("status = %d, want 401, body=%s", rr.Code, rr.Body.String()) } } // Scenario D — missing passphrase header is 401. func TestArtifacts_MissingPassphrase(t *testing.T) { h, st, _ := newTestHandler(t) st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ckey", RetrievalPassword: "pass-phrase"}) rr := doArtifacts(h, "c1", "") if rr.Code != http.StatusUnauthorized { t.Fatalf("status = %d, want 401, body=%s", rr.Code, rr.Body.String()) } } // Scenario E — unknown customer is 404 (before any auth comparison leaks). func TestArtifacts_UnknownCustomer(t *testing.T) { h, _, _ := newTestHandler(t) rr := doArtifacts(h, "nope", "whatever") if rr.Code != http.StatusNotFound { t.Fatalf("status = %d, want 404, body=%s", rr.Code, rr.Body.String()) } } // Scenario F — store round-trip: a partial set (agent only) persists independently. func TestArtifacts_PartialSetRoundTrips(t *testing.T) { _, st, _ := newTestHandler(t) if err := st.SetArtifactManifest(store.ArtifactManifest{AgentVersion: "0.43.0", AgentSHA256: strings.Repeat("c", 64)}); err != nil { t.Fatalf("SetArtifactManifest: %v", err) } m := st.GetArtifactManifest() if m.AgentVersion != "0.43.0" || m.AgentSHA256 != strings.Repeat("c", 64) { t.Errorf("agent fields not round-tripped: %+v", m) } if m.GoldenVersion != "" || m.GoldenSHA256 != "" { t.Errorf("golden fields should be empty: %+v", m) } }