Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
import {
afterEach,
beforeEach,
describe,
expect,
it,
} from "@suaveplan/testing/runner";
import { MailpitFixture } from "./mailpit-fixture.js";
function makeContainerFake(smtpPort = 1025, apiPort = 8025) {
const stopped: string[] = [];
const portMap: Record<number, number> = {
1025: smtpPort,
8025: apiPort,
};
const fakeStarted = {
getMappedPort: (port: number) => portMap[port] ?? port,
stop: async () => {
stopped.push("stopped");
},
};
const fakeContainer = {
withExposedPorts: function () {
return this;
},
withWaitStrategy: function () {
return this;
},
start: async () => fakeStarted,
};
return {
fakeContainer,
fakeStarted,
stopped,
factory: (_img: string) => fakeContainer as never,
};
}
describe("MailpitFixture", () => {
beforeEach(() => {
delete process.env["MAILPIT_SMTP_URL"];
delete process.env["MAILPIT_API_URL"];
});
afterEach(() => {
delete process.env["MAILPIT_SMTP_URL"];
delete process.env["MAILPIT_API_URL"];
});
it("name property returns mailpit", { timeout: 5000 }, () => {
const fixture = new MailpitFixture();
expect(fixture.name).toBe("mailpit");
});
it("passthrough mode: returns config from both env vars", {
timeout: 30000,
}, async () => {
process.env["MAILPIT_SMTP_URL"] = "smtp://localhost:1025";
process.env["MAILPIT_API_URL"] = "http://localhost:8025";
const fixture = new MailpitFixture();
const config = await fixture.start();
expect(config.smtpUrl).toBe("smtp://localhost:1025");
expect(config.apiUrl).toBe("http://localhost:8025");
});
it("passthrough mode: stop is a no-op", { timeout: 30000 }, async () => {
process.env["MAILPIT_SMTP_URL"] = "smtp://localhost:1025";
process.env["MAILPIT_API_URL"] = "http://localhost:8025";
const fixture = new MailpitFixture();
await fixture.start();
await expect(fixture.stop()).resolves.toBeUndefined();
});
it("partial passthrough (only SMTP set) falls through to container mode", {
timeout: 30000,
}, async () => {
process.env["MAILPIT_SMTP_URL"] = "smtp://localhost:1025";
// MAILPIT_API_URL not set
const { factory } = makeContainerFake(11025, 18025);
const fixture = new MailpitFixture({ containerFactory: factory });
const config = await fixture.start();
// Should have used the container, not env var
expect(config.smtpUrl).toBe("smtp://localhost:11025");
expect(config.apiUrl).toBe("http://localhost:18025");
});
it("container mode happy path: returns correct URLs", {
timeout: 30000,
}, async () => {
const { factory } = makeContainerFake(11025, 18025);
const fixture = new MailpitFixture({ containerFactory: factory });
const config = await fixture.start();
expect(config.smtpUrl).toBe("smtp://localhost:11025");
expect(config.apiUrl).toBe("http://localhost:18025");
});
it("container mode: stop calls container.stop()", {
timeout: 30000,
}, async () => {
const { factory, stopped } = makeContainerFake(11025, 18025);
const fixture = new MailpitFixture({ containerFactory: factory });
await fixture.start();
await fixture.stop();
expect(stopped).toContain("stopped");
});
it("healthCheck returns true via injected fetchFn", {
timeout: 30000,
}, async () => {
const { factory } = makeContainerFake(11025, 18025);
const fixture = new MailpitFixture({
containerFactory: factory,
fetchFn: async () => ({ ok: true }) as Response,
});
await fixture.start();
const healthy = await fixture.healthCheck();
expect(healthy).toBe(true);
});
it("healthCheck returns false via injected fetchFn (ok: false)", {
timeout: 30000,
}, async () => {
const { factory } = makeContainerFake(11025, 18025);
const fixture = new MailpitFixture({
containerFactory: factory,
fetchFn: async () => ({ ok: false }) as Response,
});
await fixture.start();
const healthy = await fixture.healthCheck();
expect(healthy).toBe(false);
});
it("healthCheck returns false when fetch throws", {
timeout: 30000,
}, async () => {
const { factory } = makeContainerFake(11025, 18025);
const fixture = new MailpitFixture({
containerFactory: factory,
fetchFn: async () => {
throw new Error("ECONNREFUSED");
},
});
await fixture.start();
const healthy = await fixture.healthCheck();
expect(healthy).toBe(false);
});
it("healthCheck returns false when no container", {
timeout: 30000,
}, async () => {
const fixture = new MailpitFixture();
const healthy = await fixture.healthCheck();
expect(healthy).toBe(false);
});
it("stop when no container is a no-op", { timeout: 30000 }, async () => {
const fixture = new MailpitFixture();
await expect(fixture.stop()).resolves.toBeUndefined();
});
it("resolveContainerFactory: falls back to GenericContainer import", {
timeout: 60000,
}, async () => {
class Exposed extends MailpitFixture {
resolveFactory() {
return this.resolveContainerFactory();
}
}
const exposed = new Exposed();
const factory = await exposed.resolveFactory();
expect(typeof factory).toBe("function");
const container = factory("axllent/mailpit:latest");
expect(container).toBeDefined();
});
});