import { expect } from "@std/expect";
import { describe, test } from "@std/testing/bdd";
import { groups } from "#db/groups.ts";
import { getAllListings } from "#db/listings/records.ts";
import { getAllPageItems } from "#db/site-page-items.ts";
import { sitePages } from "#db/site-pages.ts";
import { emptyListsFor } from "#fp";
import {
BENCHMARK_BOOKING_SLUG,
BENCHMARK_NAV_PAGES,
BENCHMARK_PACKAGE_GROUPS,
BENCHMARK_REGULAR_GROUPS,
balancedCycles,
balancedRotation,
benchmarkBookingMarkers,
benchmarkCatalogueMarkers,
benchmarkGroupName,
benchmarkListingName,
requiredEnv,
requireMarkers,
samplesFor,
seedBenchmarkCatalogue,
seedBenchmarkNavTree,
} from "#scripts/bench/cold-start/support.ts";
import { describeWithEnv } from "#test-utils/db.ts";
import { createTestSitePage } from "#test-utils/db-helpers/misc.ts";
import { withEnv } from "#test-utils/env.ts";
import type { SitePageItemType } from "#types";
const completeCatalogueBody = (): string =>
benchmarkCatalogueMarkers().join("\n");
describe("cold-start benchmark support", () => {
test("balancedCycles rejects a partial measurement cycle", () => {
expect(() => balancedCycles([1, 2, 3, 4, 5], 4)).toThrow(
"Run count 5 must be a multiple of positions per cycle 4",
);
});
test("balancedCycles keeps every complete measurement cycle", () => {
expect(balancedCycles([1, 2, 3, 4, 5, 6], 3)).toEqual([
[1, 2, 3],
[4, 5, 6],
]);
});
test("balancedRotation balances positions and pair order", () => {
const items = ["a", "b", "c"];
expect(
Array.from({ length: 6 }, (_, run) => balancedRotation(items, run)),
).toEqual([
["a", "b", "c"],
["b", "c", "a"],
["c", "a", "b"],
["c", "b", "a"],
["b", "a", "c"],
["a", "c", "b"],
]);
});
test("requireMarkers accepts the complete seeded page", () => {
expect(() =>
requireMarkers(
{ body: completeCatalogueBody(), status: 200 },
"request",
benchmarkCatalogueMarkers(),
),
).not.toThrow();
});
test("requireMarkers rejects an error response", () => {
expect(() =>
requireMarkers(
{ body: "", status: 500 },
"request",
benchmarkCatalogueMarkers(),
),
).toThrow("request failed with status 500");
});
test("requireMarkers rejects a partial catalogue", () => {
expect(() =>
requireMarkers(
{
body: completeCatalogueBody().replace("Benchmark listing 14", ""),
status: 200,
},
"request",
benchmarkCatalogueMarkers(),
),
).toThrow("request did not render Benchmark listing 14");
});
test("requireMarkers rejects a booking page without the active nav chain", () => {
// The form markers alone: a page that dropped the navigation.
const body = benchmarkBookingMarkers().slice(0, 2).join("\n");
expect(() =>
requireMarkers(
{ body, status: 200 },
"booking request",
benchmarkBookingMarkers(),
),
).toThrow(
`booking request did not render Visit`,
);
});
test("requiredEnv returns a configured value", () => {
const key = "COLD_START_SUPPORT_TEST";
using _env = withEnv({ [key]: "configured" });
expect(requiredEnv(key)).toBe("configured");
});
test("requiredEnv rejects a missing value", () => {
const key = "COLD_START_SUPPORT_MISSING_TEST";
using _env = withEnv({ [key]: undefined });
expect(() => requiredEnv(key)).toThrow(`${key} is required`);
});
test("emptyListsFor stores samples for each requested key", () => {
const samples = emptyListsFor(["cold", "warm"]);
samplesFor(samples, "cold").push(42);
expect(samplesFor(samples, "cold")).toEqual([42]);
expect(samplesFor(samples, "warm")).toEqual([]);
});
test("samplesFor rejects an unknown key", () => {
expect(() => samplesFor(emptyListsFor(["cold"]), "warm")).toThrow(
"Samples missing for warm",
);
});
});
describeWithEnv("cold-start benchmark seeding", { db: true }, () => {
test("seedBenchmarkCatalogue inserts one group and one listing per number", async () => {
const ids = await seedBenchmarkCatalogue();
const total = BENCHMARK_REGULAR_GROUPS + BENCHMARK_PACKAGE_GROUPS;
expect(ids.groupIds.size).toBe(total);
expect(ids.listingIds.size).toBe(total);
const groupRows = await groups.cache.getAll();
const listingRows = await getAllListings();
expect(groupRows).toHaveLength(total);
expect(listingRows).toHaveLength(total);
// The packages continue the numbering after the regular groups.
const group1 = groupRows.find((row) => row.id === ids.groupIds.get(1));
expect(group1?.name).toBe(benchmarkGroupName(1, false));
expect(group1?.is_package).toBe(false);
const firstPackage = groupRows.find(
(row) => row.id === ids.groupIds.get(BENCHMARK_REGULAR_GROUPS + 1),
);
expect(firstPackage?.name).toBe(
benchmarkGroupName(BENCHMARK_REGULAR_GROUPS + 1, true),
);
expect(firstPackage?.is_package).toBe(true);
const listing1 = listingRows.find(
(row) => row.id === ids.listingIds.get(1),
);
expect(listing1?.name).toBe(benchmarkListingName(1));
expect(listing1?.slug).toBe(BENCHMARK_BOOKING_SLUG);
});
test("seedBenchmarkNavTree wires the Visit chain and the off-chain Extras group", async () => {
const ids = await seedBenchmarkCatalogue();
await seedBenchmarkNavTree(ids);
const pages = await sitePages.getAll();
const visit = pages.find(
(page) => page.slug === BENCHMARK_NAV_PAGES.visit.slug,
);
const extras = pages.find(
(page) => page.slug === BENCHMARK_NAV_PAGES.extras.slug,
);
expect(visit?.name).toBe(BENCHMARK_NAV_PAGES.visit.name);
expect(extras?.name).toBe(BENCHMARK_NAV_PAGES.extras.name);
const items = await getAllPageItems();
const idsOnPage = (
pageId: number | undefined,
itemType: SitePageItemType,
): (number | undefined)[] =>
items
.filter(
(item) => item.page_id === pageId && item.item_type === itemType,
)
.map((item) => item.item_id);
// Visit carries its child page and the two benchmark listings; Extras
// carries the one group, off the chain a listing's page highlights.
expect(idsOnPage(visit?.id, "page")).toEqual([extras?.id]);
expect(idsOnPage(visit?.id, "listing")).toEqual([
ids.listingIds.get(1),
ids.listingIds.get(2),
]);
expect(idsOnPage(extras?.id, "group")).toEqual([ids.groupIds.get(1)]);
});
test("seedBenchmarkNavTree refuses a page whose slug is already used", async () => {
const ids = await seedBenchmarkCatalogue();
await createTestSitePage(BENCHMARK_NAV_PAGES.visit.slug);
await expect(seedBenchmarkNavTree(ids)).rejects.toThrow(
`site page slug is already used: ${BENCHMARK_NAV_PAGES.visit.slug}`,
);
});
});