package api import ( "context" "net/http" "net/http/httptest" "strings" "testing" "bat-api/internal/backendrpc" ) type releaseBackendStub struct { *fakeBackend root string available bool distributionParams []backendrpc.ReleaseDistributionParams } func (b *controlBackend) ReleaseStatus(context.Context) (*backendrpc.ReleaseStatusReport, error) { b.calls = append(b.calls, "release.status") return &backendrpc.ReleaseStatusReport{Status: "ready", StatusCode: "distribution.ready"}, nil } func (b *controlBackend) ReleaseList(context.Context, backendrpc.ReleaseListParams) (*backendrpc.ReleaseListReport, error) { b.calls = append(b.calls, "release.list") return &backendrpc.ReleaseListReport{Status: "ready", StatusCode: "distribution.ready"}, nil } func (b *controlBackend) ReleaseDistribution(context.Context, backendrpc.ReleaseDistributionParams) (*backendrpc.ReleaseDistributionPage, error) { b.calls = append(b.calls, "release.distribution") return &backendrpc.ReleaseDistributionPage{Available: false, StatusCode: "distribution.blocked"}, nil } func (b *controlBackend) ReleaseCleanup(context.Context, backendrpc.ReleaseCleanupParams) (*backendrpc.ReleaseCleanupReport, error) { b.calls = append(b.calls, "release.cleanup") return &backendrpc.ReleaseCleanupReport{PlanID: "plan-1"}, nil } func (b *releaseBackendStub) ReleaseStatus(context.Context) (*backendrpc.ReleaseStatusReport, error) { return &backendrpc.ReleaseStatusReport{ Status: "ready", StatusCode: "distribution.ready", OfficialCurrentReleaseID: "official-1", DefaultDistributionChannel: "official", OfficialDistributionReady: true, }, nil } func (b *releaseBackendStub) ReleaseList(context.Context, backendrpc.ReleaseListParams) (*backendrpc.ReleaseListReport, error) { return &backendrpc.ReleaseListReport{ Status: "ready", StatusCode: "distribution.ready", Releases: []backendrpc.ReleaseSummary{{ Channel: "localized", ID: "localized-1", ManifestContractStatus: "valid", ArtifactIntegrityStatus: "valid", DistributionIntegrityStatus: "valid", }}, }, nil } func (b *releaseBackendStub) ReleaseDistribution(_ context.Context, params backendrpc.ReleaseDistributionParams) (*backendrpc.ReleaseDistributionPage, error) { b.distributionParams = append(b.distributionParams, params) return &backendrpc.ReleaseDistributionPage{ Available: b.available, Channel: params.Channel, ReleaseID: params.ReleaseID, ResourceRoot: b.root, Status: "ready", StatusCode: "distribution.ready", ArtifactIntegrityStatus: "valid", Total: 1, Limit: 1000, Entries: []backendrpc.ReleaseDistributionEntry{{ URL: "https://prod-clientpatch.bluearchiveyostar.com/r93_fixture/TableBundles/TableCatalog.bytes", Destination: "prod-clientpatch.bluearchiveyostar.com/r93_fixture/TableBundles/TableCatalog.bytes", Bytes: 21, BLAKE3: "not-used-by-http-index", }}, }, nil } func (*releaseBackendStub) ReleaseCleanup(context.Context, backendrpc.ReleaseCleanupParams) (*backendrpc.ReleaseCleanupReport, error) { return &backendrpc.ReleaseCleanupReport{PlanID: "plan-1"}, nil } func TestReleaseHTTPForwardsTypedSelectionAndDoesNotFallback(t *testing.T) { cfg := DefaultConfig() if err := cfg.Normalize(); err != nil { t.Fatal(err) } backend := &releaseBackendStub{fakeBackend: &fakeBackend{}, root: fixtureRoot(t), available: true} server := NewServer(cfg, backend, nil) recorder := httptest.NewRecorder() server.Handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/v1/releases?channel=localized", nil)) if recorder.Code != http.StatusOK { t.Fatalf("release list status=%d body=%s", recorder.Code, recorder.Body.String()) } recorder = httptest.NewRecorder() server.Handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/v1/distribution?channel=localized&release_id=localized-1&offset=2&limit=10", nil)) if recorder.Code != http.StatusOK { t.Fatalf("distribution status=%d body=%s", recorder.Code, recorder.Body.String()) } if len(backend.distributionParams) != 1 || backend.distributionParams[0].Channel != "localized" || backend.distributionParams[0].ReleaseID != "localized-1" || backend.distributionParams[0].Offset != 2 || backend.distributionParams[0].Limit != 10 { t.Fatalf("distribution params=%#v", backend.distributionParams) } recorder = httptest.NewRecorder() server.Handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/prod-clientpatch.bluearchiveyostar.com/r93_fixture/TableBundles/TableCatalog.bytes?channel=localized&release_id=localized-1", nil)) if recorder.Code != http.StatusOK { t.Fatalf("localized CDN status=%d body=%s", recorder.Code, recorder.Body.String()) } if recorder.Body.String() != "TABLE_CATALOG_FIXTURE" { t.Fatalf("localized CDN body=%q", recorder.Body.String()) } cfg.RequireIndexed = false unindexedServer := NewServer(cfg, backend, nil) recorder = httptest.NewRecorder() unindexedServer.Handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/prod-clientpatch.bluearchiveyostar.com/r93_fixture/TableBundles/not-listed.bytes?channel=localized&release_id=localized-1", nil)) if recorder.Code != http.StatusNotFound { t.Fatalf("unlisted localized CDN status=%d body=%s", recorder.Code, recorder.Body.String()) } blocked := &releaseBackendStub{fakeBackend: &fakeBackend{}, root: fixtureRoot(t), available: false} blockedServer := NewServer(cfg, blocked, nil) recorder = httptest.NewRecorder() blockedServer.Handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/prod-clientpatch.bluearchiveyostar.com/r93_fixture/TableBundles/TableCatalog.bytes?channel=localized", nil)) if recorder.Code != http.StatusConflict { t.Fatalf("blocked localized CDN status=%d body=%s", recorder.Code, recorder.Body.String()) } } func TestAdminReleaseCleanupRequiresAuthAndForwards(t *testing.T) { cfg := DefaultConfig() cfg.AuthToken = "control-token" if err := cfg.Normalize(); err != nil { t.Fatal(err) } backend := &controlBackend{fakeBackend: &fakeBackend{}} server := NewServer(cfg, backend, nil) recorder := httptest.NewRecorder() server.Handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/admin/control/release-cleanup", nil)) if recorder.Code != http.StatusUnauthorized { t.Fatalf("unauthenticated cleanup status=%d", recorder.Code) } request := httptest.NewRequest(http.MethodPost, "/admin/control/release-cleanup", strings.NewReader(`{"execute":false}`)) request.Header.Set("Authorization", "Bearer control-token") recorder = httptest.NewRecorder() server.Handler().ServeHTTP(recorder, request) if recorder.Code != http.StatusAccepted { t.Fatalf("dry-run cleanup status=%d body=%s", recorder.Code, recorder.Body.String()) } if len(backend.calls) != 1 || backend.calls[0] != "release.cleanup" { t.Fatalf("calls=%v", backend.calls) } request = httptest.NewRequest(http.MethodPost, "/admin/control/release-cleanup", strings.NewReader(`{"execute":true}`)) request.Header.Set("Authorization", "Bearer control-token") recorder = httptest.NewRecorder() server.Handler().ServeHTTP(recorder, request) if recorder.Code != http.StatusBadRequest { t.Fatalf("missing plan cleanup status=%d body=%s", recorder.Code, recorder.Body.String()) } }