Files
suaveplan/packages/testkit/testing/testing-integration/src/environment/test-environment.spec-conformance.test.ts
2026-05-27 23:47:53 +01:00

111 lines
3.8 KiB
TypeScript

/**
* Spec-conformance — type-level pins for the generic threading on
* {@link TestEnvironment}, {@link HealthPoller}, and the public
* {@link createIntegrationSetup}/{@link skipIfUnavailable} façade.
*
* These tests exist purely to fail at compile time if the generics
* regress. They emit no runtime assertions other than a single
* `expectTypeOf(...).toEqualTypeOf<…>()` per scenario.
*/
import { describe, expectTypeOf, it } from "@suaveplan/testing/runner";
import type { ServiceFixture } from "../fixture/service-fixture.js";
import { HealthPoller } from "../helpers/health-poller.js";
import { skipIfUnavailable } from "../helpers/skip-if-unavailable.js";
import { createIntegrationSetup } from "../setup/create-integration-setup.js";
import { TestEnvironment } from "./test-environment.js";
interface RedisConfig extends Record<string, unknown> {
url: string;
host: string;
port: number;
}
interface PostgresConfig extends Record<string, unknown> {
url: string;
database: string;
}
const fakeRedis: ServiceFixture<RedisConfig> = {
name: "redis",
start: async () => ({ url: "r", host: "h", port: 1 }),
stop: async () => {},
healthCheck: async () => true,
};
const fakePg: ServiceFixture<PostgresConfig> = {
name: "postgres",
start: async () => ({ url: "p", database: "d" }),
stop: async () => {},
healthCheck: async () => true,
};
const fixtures = { redis: fakeRedis, postgres: fakePg };
describe("TestEnvironment generic conformance", () => {
it("setup() configs[K] is the precise fixture TConfig", {
timeout: 5000,
}, async () => {
const env = new TestEnvironment(fixtures);
const result = await env.setup();
expectTypeOf(result.configs.redis).toEqualTypeOf<
RedisConfig | undefined
>();
expectTypeOf(result.configs.postgres).toEqualTypeOf<
PostgresConfig | undefined
>();
expectTypeOf(result.failed).toEqualTypeOf<string[]>();
await env.teardown();
});
it("default generic parameter keeps existing call sites compiling", {
timeout: 5000,
}, () => {
// Equivalent to pre-generic call sites; should still typecheck.
const env: TestEnvironment = new TestEnvironment(
{} as Record<string, ServiceFixture<Record<string, unknown>>>
);
expectTypeOf(env).toMatchTypeOf<TestEnvironment>();
});
});
describe("HealthPoller.poll generic conformance", () => {
it("accepts any ServiceFixture<TConfig> without widening cast", {
timeout: 5000,
}, async () => {
const poller = new HealthPoller({
timeoutMs: 1,
intervalMs: 1,
sleep: async () => {},
});
// Compile-time check: precise TConfig is preserved.
const fn = poller.poll<RedisConfig>;
expectTypeOf(fn).parameters.toMatchTypeOf<
[Pick<ServiceFixture<RedisConfig>, "name" | "healthCheck">]
>();
await poller.poll(fakeRedis);
});
});
describe("createIntegrationSetup typed inject() conformance", () => {
it("inject(key) returns the precise per-fixture config type", {
timeout: 5000,
}, () => {
const { inject } = createIntegrationSetup(fixtures);
expectTypeOf(inject).parameter(0).toEqualTypeOf<"redis" | "postgres">();
expectTypeOf<
ReturnType<typeof inject<"redis">>
>().toEqualTypeOf<RedisConfig>();
expectTypeOf<
ReturnType<typeof inject<"postgres">>
>().toEqualTypeOf<PostgresConfig>();
});
});
describe("skipIfUnavailable typed-name conformance", () => {
it("constrains fixtureName via TName generic", { timeout: 5000 }, () => {
type Names = "redis" | "postgres";
const cb = skipIfUnavailable<Names>("redis");
expectTypeOf(cb).toMatchTypeOf<(ctx: { skip: () => void }) => void>();
});
});