111 lines
3.8 KiB
TypeScript
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>();
|
|
});
|
|
});
|