Release Fixture Tools 0.2.0
Verify / verify (push) Canceled after 0s

This commit is contained in:
2026-09-02 13:00:37 +02:00
parent f5d018c64f
commit f1c41e7079
22 changed files with 1007 additions and 126 deletions
+22 -1
View File
@@ -54,6 +54,27 @@ test("imports CSV headings and exports an inspectable model", async ({
expect((await pending).suggestedFilename()).toBe("fixture-model.json");
});
test("imports composite constraints and exposes topological coverage", async ({
page,
}) => {
await page.goto("/deep/nested/fixture/");
await page.getByLabel("Fixture model source").fill(`CREATE TABLE tenants (
region VARCHAR(8), tenant_id INTEGER, PRIMARY KEY (region, tenant_id)
);
CREATE TABLE events (
region VARCHAR(8), tenant_id INTEGER,
FOREIGN KEY (region, tenant_id) REFERENCES tenants(region, tenant_id)
);`);
await page.getByRole("button", { name: "Import model" }).click();
await page.getByRole("button", { name: "Generate fixtures" }).click();
await page.getByRole("button", { name: "Coverage" }).click();
await expect(
page.getByRole("heading", { name: "Constraint coverage matrix" }),
).toBeVisible();
await expect(page.getByText("tenants → events")).toBeVisible();
await expect(page.getByText(/foreignKey\(region,tenant_id\)/u)).toBeVisible();
});
test("retains results after a failed import and downloads output", async ({
page,
}) => {
@@ -97,7 +118,7 @@ test("integrates help, themes, PWA identity and hardened headers", async ({
const manifest = await request.get("/deep/nested/fixture/toolbox-app.json");
await expect(manifest.json()).resolves.toMatchObject({
id: "de.add-ideas.fixture-tools",
version: "0.1.0",
version: "0.2.0",
privacy: { processing: "local", telemetry: false },
});
});
+18
View File
@@ -0,0 +1,18 @@
import { expect, test } from "@playwright/test";
test("keeps the primary workspace inside a narrow viewport", async ({
page,
}) => {
await page.goto("/deep/nested/fixture/");
await expect(page.locator("main").first()).toBeVisible();
await expect(
page.locator("main .loading, main .workbench-loading"),
).toHaveCount(0);
const widths = await page.evaluate(() => ({
content: document.documentElement.scrollWidth,
viewport: document.documentElement.clientWidth,
}));
expect(widths.viewport).toBeLessThanOrEqual(430);
expect(widths.content).toBeLessThanOrEqual(widths.viewport + 1);
});
+102
View File
@@ -1,9 +1,11 @@
import { describe, expect, it } from "vitest";
import {
generateFixtures,
constraintCoverageMatrix,
modelJson,
parseFixtureModel,
serializeDataset,
topologicalTableOrder,
type GeneratedDataset,
} from "../../src/fixture/model";
@@ -175,4 +177,104 @@ describe("fixture model", () => {
}),
).toThrow(/100,000 field values/u);
});
it("imports and generates composite relational constraints topologically", () => {
const sql = `CREATE TABLE tenants (
region VARCHAR(8) NOT NULL,
tenant_id INTEGER NOT NULL,
PRIMARY KEY (region, tenant_id)
);
CREATE TABLE events (
region VARCHAR(8) NOT NULL,
tenant_id INTEGER NOT NULL,
sequence INTEGER NOT NULL,
UNIQUE (region, tenant_id, sequence),
FOREIGN KEY (region, tenant_id) REFERENCES tenants(region, tenant_id)
);`;
const model = parseFixtureModel(sql, "sql").model;
expect(model.tables[1]?.constraints).toEqual([
{ kind: "unique", fields: ["region", "tenant_id", "sequence"] },
{
kind: "foreignKey",
fields: ["region", "tenant_id"],
references: { table: "tenants", fields: ["region", "tenant_id"] },
},
]);
expect(topologicalTableOrder(model)).toEqual(["tenants", "events"]);
const dataset = generateFixtures(model, {
seed: "composite",
rows: 12,
boundaryPercent: 10,
invalidPercent: 0,
providerProfile: "auto",
});
const targets = new Set(
dataset.tables.tenants?.map((row) => `${row.region}:${row.tenant_id}`),
);
expect(
dataset.tables.events?.every((row) =>
targets.has(`${row.region}:${row.tenant_id}`),
),
).toBe(true);
expect(dataset.generationOrder).toEqual(["tenants", "events"]);
expect(
constraintCoverageMatrix(model, dataset).some(
(entry) =>
entry.table === "events" &&
entry.constraint.startsWith("foreignKey(region,tenant_id)"),
),
).toBe(true);
});
it("accepts scalar table-level primary and foreign-key constraints", () => {
const sql = `CREATE TABLE accounts (
account_id INTEGER NOT NULL,
PRIMARY KEY (account_id)
);
CREATE TABLE entries (
account_id INTEGER NOT NULL,
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
);`;
const model = parseFixtureModel(sql, "sql").model;
expect(model.tables[0]?.constraints).toEqual([
{ kind: "unique", fields: ["account_id"] },
]);
expect(model.tables[1]?.constraints).toEqual([
{
kind: "foreignKey",
fields: ["account_id"],
references: { table: "accounts", fields: ["account_id"] },
},
]);
const dataset = generateFixtures(model, {
seed: "scalar-table-constraints",
rows: 8,
boundaryPercent: 0,
invalidPercent: 0,
});
const accounts = new Set(
dataset.tables.accounts?.map((row) => row.account_id),
);
expect(
dataset.tables.entries?.every((row) => accounts.has(row.account_id)),
).toBe(true);
});
it("supports deterministic built-in and custom provider profiles", () => {
const model = parseFixtureModel("display_name,host,sku\n", "csv").model;
const options = {
seed: "providers",
rows: 4,
boundaryPercent: 0,
invalidPercent: 0,
providerProfile: "auto",
};
const generated = generateFixtures(model, options);
expect(generated).toEqual(generateFixtures(model, options));
expect(generated.tables.records?.[0]).toMatchObject({
display_name: expect.stringMatching(/ /u),
host: expect.stringMatching(/\.example\.test$/u),
sku: "SKU-000001",
});
});
});