import { ITEM_IDS, PLAN_LIMITS, PlanId } from "@hexclave/shared/dist/plans"; import { HexclaveAssertionError } from "@hexclave/shared/dist/utils/errors"; import { wait } from "@hexclave/shared/dist/utils/promises"; import { deindent } from "@hexclave/shared/dist/utils/strings"; import { it } from "../../../../helpers"; import { Project, User, niceBackendFetch, withInternalProject } from "../../../backend-helpers"; import { waitForItemQuantityToReach } from "../../../payment-quota-helpers"; async function runQuery(body: { query: string, params?: Record, timeout_ms?: number }) { await Project.createAndSwitch({ config: { magic_link_enabled: true } }); const response = await niceBackendFetch("/api/v1/internal/analytics/query", { method: "POST", accessType: "admin", body, }); return response; } async function runQueryWithPlan(planId: PlanId, body: { query: string, params?: Record, timeout_ms?: number }) { const { createProjectResponse } = await Project.createAndSwitch({ config: { magic_link_enabled: true } }); const ownerTeamId = createProjectResponse.body.owner_team_id; if (planId !== "free") { await withInternalProject(async () => { const grantResponse = await niceBackendFetch(`/api/v1/payments/products/team/${ownerTeamId}`, { method: "POST", accessType: "server", body: { product_id: planId }, }); if (grantResponse.status !== 200) { throw new HexclaveAssertionError(`Failed to grant plan '${planId}' to team '${ownerTeamId}'`, { response: grantResponse }); } }); await waitForItemQuantityToReach(ownerTeamId, ITEM_IDS.analyticsTimeoutSeconds, PLAN_LIMITS[planId].analyticsTimeoutSeconds); } const response = await niceBackendFetch("/api/v1/internal/analytics/query", { method: "POST", accessType: "admin", body, }); return response; } type ExpectLike = ((value: unknown) => { toEqual: (value: unknown) => void }) & { any: (constructor: unknown) => unknown, }; const stripQueryId = | null }>(response: T, expect: ExpectLike) => { if (response.status === 200 && response.body) { expect(response.body.query_id).toEqual(expect.any(String)); delete response.body.query_id; } return response; }; async function fetchQueryTiming(queryId: string) { return await niceBackendFetch("/api/v1/internal/analytics/query/timing", { method: "POST", accessType: "server", body: { query_id: queryId, }, }); } async function fetchQueryTimingWithRetry(queryId: string, attempts = 5, delayMs = 200) { let response = await fetchQueryTiming(queryId); for (let attempt = 0; attempt < attempts; attempt += 1) { if (response.status === 200) { break; } await wait(delayMs); response = await fetchQueryTiming(queryId); } return response; } it("can execute a basic query with admin access", async ({ expect }) => { const response = await runQuery({ query: "SELECT 1 as value" }); expect(stripQueryId(response, expect)).toMatchInlineSnapshot(` NiceResponse { "status": 200, "body": { "result": [{ "value": 1 }] }, "headers": Headers {