Fas 1a punkt 2: release_gates tabell + go/no-go-vy
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { describe, expect, it, beforeEach, afterAll } from "vitest";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { createDatabase, closeDatabase } from "../src/client.js";
|
||||
import { schema, seedReleaseGates, evaluateReleaseGates, summarizeReleaseGates } from "../src/index.js";
|
||||
import { BUILT_IN_RELEASE_GATES } from "@app/shared-types";
|
||||
|
||||
const { db, pool } = createDatabase();
|
||||
|
||||
async function reset() {
|
||||
await db.delete(schema.productAnalyticsEvents);
|
||||
await db.delete(schema.releaseGates);
|
||||
await db
|
||||
.delete(schema.users)
|
||||
.where(eq(schema.users.email, "release-gates-test@example.invalid"));
|
||||
}
|
||||
|
||||
async function createTestUser(id: string) {
|
||||
const [user] = await db
|
||||
.insert(schema.users)
|
||||
.values({
|
||||
id,
|
||||
email: `release-gates-test-${id.slice(0, 8)}@example.invalid`,
|
||||
displayName: "Release Gate Test",
|
||||
locale: "sv-SE",
|
||||
})
|
||||
.returning({ id: schema.users.id });
|
||||
return user!.id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await reset();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await reset();
|
||||
await closeDatabase();
|
||||
});
|
||||
|
||||
describe("release gates", () => {
|
||||
it("seeds built-in gates idempotently", async () => {
|
||||
await seedReleaseGates(db);
|
||||
const first = await db.select().from(schema.releaseGates);
|
||||
expect(first.length).toBe(BUILT_IN_RELEASE_GATES.length);
|
||||
|
||||
await seedReleaseGates(db);
|
||||
const second = await db.select().from(schema.releaseGates);
|
||||
expect(second.length).toBe(BUILT_IN_RELEASE_GATES.length);
|
||||
});
|
||||
|
||||
it("evaluates first_scan_rate from product_analytics_events", async () => {
|
||||
await seedReleaseGates(db);
|
||||
const userA = await createTestUser("11111111-1111-1111-1111-111111111111");
|
||||
const userB = await createTestUser("22222222-2222-2222-2222-222222222222");
|
||||
const now = new Date();
|
||||
await db.insert(schema.productAnalyticsEvents).values([
|
||||
{ eventName: "account_created", userId: userA, occurredAt: now, receivedAt: now, properties: {} },
|
||||
{ eventName: "account_created", userId: userB, occurredAt: now, receivedAt: now, properties: {} },
|
||||
{ eventName: "scan_completed", userId: userA, occurredAt: now, receivedAt: now, properties: {} },
|
||||
]);
|
||||
|
||||
const results = await evaluateReleaseGates(db);
|
||||
const firstScan = results.find((g) => g.gateKey === "first_scan_rate");
|
||||
expect(firstScan).toBeDefined();
|
||||
expect(firstScan?.value).toBeCloseTo(0.5);
|
||||
expect(firstScan?.status).toBe("failed"); // target 70%
|
||||
});
|
||||
|
||||
it("summarizes overall go/no-go verdict", async () => {
|
||||
await seedReleaseGates(db);
|
||||
const results = await evaluateReleaseGates(db);
|
||||
const summary = summarizeReleaseGates(results);
|
||||
expect(summary.overall).toBeOneOf(["go", "no_go", "pending"]);
|
||||
expect(summary.passed + summary.failed + summary.pending + summary.notMeasurable).toBe(
|
||||
BUILT_IN_RELEASE_GATES.length,
|
||||
);
|
||||
});
|
||||
|
||||
it("persists evaluation results", async () => {
|
||||
await seedReleaseGates(db);
|
||||
const user = await createTestUser("33333333-3333-3333-3333-333333333333");
|
||||
const now = new Date();
|
||||
await db.insert(schema.productAnalyticsEvents).values([
|
||||
{ eventName: "account_created", userId: user, occurredAt: now, receivedAt: now, properties: {} },
|
||||
{ eventName: "scan_completed", userId: user, occurredAt: now, receivedAt: now, properties: {} },
|
||||
]);
|
||||
|
||||
await evaluateReleaseGates(db);
|
||||
const [gate] = await db.select().from(schema.releaseGates).where(eq(schema.releaseGates.gateKey, "first_scan_rate"));
|
||||
expect(gate?.lastValue).toBeCloseTo(1);
|
||||
expect(gate?.status).toBe("passed");
|
||||
expect(gate?.lastEvaluatedAt).not.toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user