From 85f5f4e8a6d687277cb222e1b13d9712aa03d8a0 Mon Sep 17 00:00:00 2001 From: Ted Kim Date: Fri, 3 Jul 2026 14:38:32 -0400 Subject: [PATCH] test(logs): route /logs/search stub responses by content; document intentional hand-marshal bypass --- internal/api/client_test.go | 18 +++++++++++++++--- internal/api/frontends_test.go | 6 +++--- internal/api/logs.go | 7 +++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/internal/api/client_test.go b/internal/api/client_test.go index d52b9c4..36ab2b7 100644 --- a/internal/api/client_test.go +++ b/internal/api/client_test.go @@ -421,11 +421,11 @@ func TestFunctionMethodsUseGeneratedRoutes(t *testing.T) { var body map[string]any require.NoError(t, json.NewDecoder(r.Body).Decode(&body)) logSearchBodies = append(logSearchBodies, body) - if len(logSearchBodies) == 1 { - writeAPIJSON(t, w, http.StatusOK, logsResponse("function runtime")) + if logSearchIsBuild(body) { + writeAPIJSON(t, w, http.StatusOK, logsResponse("deployment build")) return } - writeAPIJSON(t, w, http.StatusOK, logsResponse("deployment build")) + writeAPIJSON(t, w, http.StatusOK, logsResponse("function runtime")) case r.Method == http.MethodGet && r.URL.Path == "/projects/"+projectIDText+"/functions/"+functionIDText+"/schedulers": writeAPIJSON(t, w, http.StatusOK, map[string]any{ "data": []any{functionSchedulerResponse(schedulerIDText, functionIDText, projectIDText, "hello scheduler", true)}, @@ -720,6 +720,18 @@ func functionDeploymentResponse(id, projectID, functionID string) map[string]any } } +// logSearchIsBuild reports whether a captured /logs/search request body targets +// build logs (carries a deployment selector) rather than runtime logs. Routing +// stub responses on request content is more robust than relying on call order. +func logSearchIsBuild(body map[string]any) bool { + resource, ok := body["resource"].(map[string]any) + if !ok { + return false + } + _, hasDeployments := resource["deployments"] + return hasDeployments +} + func logsResponse(message string) map[string]any { return map[string]any{ "data": []any{ diff --git a/internal/api/frontends_test.go b/internal/api/frontends_test.go index e835e9e..6b383e7 100644 --- a/internal/api/frontends_test.go +++ b/internal/api/frontends_test.go @@ -96,11 +96,11 @@ func TestFrontendDomainAndLogsMethodsUseGeneratedRoutes(t *testing.T) { var body map[string]any require.NoError(t, json.NewDecoder(r.Body).Decode(&body)) logSearchBodies = append(logSearchBodies, body) - if len(logSearchBodies) == 1 { - writeAPIJSON(t, w, http.StatusOK, logsResponse("frontend runtime")) + if logSearchIsBuild(body) { + writeAPIJSON(t, w, http.StatusOK, logsResponse("frontend build")) return } - writeAPIJSON(t, w, http.StatusOK, logsResponse("frontend build")) + writeAPIJSON(t, w, http.StatusOK, logsResponse("frontend runtime")) case r.Method == http.MethodPost && r.URL.Path == "/projects/"+projectIDText+"/frontends/"+frontendIDText+"/domain": require.NoError(t, json.NewDecoder(r.Body).Decode(&createBody)) writeAPIJSON(t, w, http.StatusCreated, frontendCustomDomainResponse("app.example.com", "provisioning")) diff --git a/internal/api/logs.go b/internal/api/logs.go index 0c8df70..7fc2e43 100644 --- a/internal/api/logs.go +++ b/internal/api/logs.go @@ -17,6 +17,13 @@ const ( logResourceTypeFunction = "function" ) +// logSearchRequest is a hand-marshalled body for POST /projects/{id}/logs/search, +// sent via SearchProjectLogsWithBodyWithResponse rather than the typed +// SearchProjectLogsWithResponse. The generated apiclient models the request's +// `resource` selector as an oapi-codegen oneOf union (apiclient.LogRequestResource), +// built through From.../As... accessors; this flat struct produces the identical +// wire format while keeping the call sites readable. Intentional — do not +// "simplify" it back to the typed union call. type logSearchRequest struct { Resource logRequestResource `json:"resource"` Limit *int `json:"limit,omitempty"`