84 lines
3.4 KiB
TypeScript
84 lines
3.4 KiB
TypeScript
/**
|
|
* E2E verification that REQUIRE_PLUGINS=1 fails the rollout via /healthcheck.
|
|
*
|
|
* The unit tests in @trigger.dev/rbac cover the loader throw. This file
|
|
* closes the loop end-to-end: spawn a real webapp, hit /healthcheck via
|
|
* HTTP, and verify the route's catch turns the throw into a 500 — the
|
|
* status the ECS/k8s readiness probe rolls back on.
|
|
*
|
|
* Each case spawns its own webapp + Postgres + Redis container (~30s) so
|
|
* env can differ per case. Slow but isolated, matching api-auth.e2e.test.ts.
|
|
*
|
|
* Requires a pre-built webapp: pnpm run build --filter webapp
|
|
*
|
|
* The REQUIRE_PLUGINS=1 case relies on the plugin NOT being resolvable
|
|
* from the spawned webapp. CI satisfies this because the plugin isn't in
|
|
* pnpm-lock.yaml. Local devs who ran `pnpm dev:link-webapp` have the
|
|
* plugin symlinked into apps/webapp/node_modules — that case is detected
|
|
* and skipped below.
|
|
*/
|
|
import { existsSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
import type { TestServer } from "@internal/testcontainers/webapp";
|
|
import { startTestServer } from "@internal/testcontainers/webapp";
|
|
|
|
const LINKED_PLUGIN_PATH = resolve(__dirname, "..", "node_modules", "@triggerdotdev", "plugins");
|
|
const pluginLocallyLinked = existsSync(LINKED_PLUGIN_PATH);
|
|
|
|
vi.setConfig({ testTimeout: 180_000 });
|
|
|
|
describe("/healthcheck with REQUIRE_PLUGINS", () => {
|
|
describe.skipIf(pluginLocallyLinked)("REQUIRE_PLUGINS=1 + plugin missing", () => {
|
|
let server: TestServer;
|
|
|
|
beforeAll(async () => {
|
|
// requirePlugins: true implies forceRbacFallback: false, so the
|
|
// loader actually tries to dynamic-import the plugin. The plugin
|
|
// is not installed in this OSS repo, so the import fails and the
|
|
// loader throws (instead of falling back) because REQUIRE_PLUGINS=1.
|
|
// The throw surfaces on the first .isUsingPlugin() call from the
|
|
// /healthcheck route, which catches it and returns 500.
|
|
server = await startTestServer({ requirePlugins: true });
|
|
}, 180_000);
|
|
|
|
afterAll(async () => {
|
|
await server?.stop();
|
|
}, 120_000);
|
|
|
|
it("returns 500 so the readiness probe fails and the rollout is rolled back", async () => {
|
|
const res = await server.webapp.fetch("/healthcheck");
|
|
expect(res.status).toBe(500);
|
|
expect(await res.text()).toBe("ERROR");
|
|
});
|
|
});
|
|
|
|
// Surface the skip in dev so it doesn't go unnoticed. CI hits the real test above.
|
|
describe.runIf(pluginLocallyLinked)(
|
|
"REQUIRE_PLUGINS=1 + plugin LOCALLY LINKED (cross-repo dev setup)",
|
|
() => {
|
|
it.skip(`skipped because ${LINKED_PLUGIN_PATH} exists — plugin would load successfully. Run \`pnpm dev:unlink-webapp\` to exercise this case locally; CI runs it without the link.`, () => {});
|
|
}
|
|
);
|
|
|
|
describe("REQUIRE_PLUGINS unset + plugin missing", () => {
|
|
let server: TestServer;
|
|
|
|
beforeAll(async () => {
|
|
// Default: forceRbacFallback=true so the loader short-circuits to
|
|
// the fallback without trying to import. /healthcheck succeeds.
|
|
server = await startTestServer();
|
|
}, 180_000);
|
|
|
|
afterAll(async () => {
|
|
await server?.stop();
|
|
}, 120_000);
|
|
|
|
it("returns 200 (baseline — unchanged self-hoster behaviour)", async () => {
|
|
const res = await server.webapp.fetch("/healthcheck");
|
|
expect(res.status).toBe(200);
|
|
expect(await res.text()).toBe("OK");
|
|
});
|
|
});
|
|
});
|