import { createServer } from "node:net"; import { once } from "node:events"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { DEFAULT_ORG_ID, prisma, resetDb, TEST_DATABASE_URL, TEST_SECRET_KEY, TEST_SECRET_KEY_ID, testSecretEnvelope } from "./helpers.js"; import { FeishuApplicationConnectionService } from "../../src/connections/feishuApplicationConnections.js"; import { ProviderConnectionService } from "../../src/connections/providerConnections.js"; const ENV_KEYS = [ "DATABASE_URL", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_API_KEY", "HUB_SILO_ORGANIZATION_ID", "HUB_PROJECT_WORKSPACE_ROOT", "HUB_SESSION_SECRET", "HUB_PUBLIC_BASE_URL", "HUB_FEISHU_LISTENER_ENABLED", "HUB_SECRET_KEYRING_FILE", "HOST", "PORT", "HUB_HTTP_BODY_LIMIT_BYTES", "HUB_MAX_FILES_PER_MESSAGE", "HUB_MAX_FILE_BYTES", "HUB_AGENT_MAX_CONCURRENT_RUNS", "HUB_AGENT_MAX_RUN_SECONDS", "HUB_HTTP_REQUESTS_PER_MINUTE", "HUB_FEISHU_EVENTS_PER_MINUTE", ] as const; describe("Hub startup", () => { const previousEnv = new Map(ENV_KEYS.map((key) => [key, process.env[key]])); afterEach(() => { for (const [key, value] of previousEnv) { if (value === undefined) delete process.env[key]; else process.env[key] = value; } }); it("rejects startup before the Feishu listener when the HTTP bind fails", async () => { await resetDb(); const owner = await prisma.user.create({ data: { feishuOpenId: "legacy-server-start-owner", displayName: "Server Start Owner", organizationMemberships: { create: { organizationId: DEFAULT_ORG_ID, role: "OWNER" } }, }, }); await new FeishuApplicationConnectionService(prisma, testSecretEnvelope, async () => {}).rotateCustomerApplication({ organizationId: DEFAULT_ORG_ID, actorUserId: owner.id, appId: "test-app", appSecret: "test-secret", botOpenId: "ou_test_bot", }); await new ProviderConnectionService(prisma, testSecretEnvelope, async () => {}).rotateByok({ organizationId: DEFAULT_ORG_ID, actorUserId: owner.id, providerId: "openrouter", baseUrl: "https://openrouter.ai/api", authToken: "test-provider-token", }); const blocker = createServer(); blocker.listen(0, "127.0.0.1"); await once(blocker, "listening"); const address = blocker.address(); if (address === null || typeof address === "string") throw new Error("expected an INET test listener"); const keyringDirectory = await mkdtemp(join(tmpdir(), "cph-hub-start-keyring-")); const keyringFile = join(keyringDirectory, "keyring.json"); await writeFile(keyringFile, JSON.stringify({ version: 1, activeKeyId: TEST_SECRET_KEY_ID, keys: { [TEST_SECRET_KEY_ID]: TEST_SECRET_KEY.toString("base64") }, }), { mode: 0o600 }); Object.assign(process.env, { DATABASE_URL: TEST_DATABASE_URL, HUB_SILO_ORGANIZATION_ID: DEFAULT_ORG_ID, HUB_PROJECT_WORKSPACE_ROOT: "/tmp/cph-hub-server-start-test", HUB_SESSION_SECRET: "integration-session-secret-with-32-bytes", HUB_PUBLIC_BASE_URL: "https://hub.example.test", HUB_FEISHU_LISTENER_ENABLED: "false", HUB_SECRET_KEYRING_FILE: keyringFile, HOST: "127.0.0.1", PORT: String(address.port), HUB_HTTP_BODY_LIMIT_BYTES: "1048576", HUB_MAX_FILES_PER_MESSAGE: "8", HUB_MAX_FILE_BYTES: "26214400", HUB_AGENT_MAX_CONCURRENT_RUNS: "1", HUB_AGENT_MAX_RUN_SECONDS: "900", HUB_HTTP_REQUESTS_PER_MINUTE: "120", HUB_FEISHU_EVENTS_PER_MINUTE: "120", }); try { const { startHub } = await import("../../src/hub.js"); await expect(startHub()).rejects.toMatchObject({ code: "EADDRINUSE" }); } finally { blocker.close(); await once(blocker, "close"); await rm(keyringDirectory, { recursive: true, force: true }); } }); });