import { loadHostConfiguration } from "@ekklesia/core/host"; import { createMailProviderConfig } from "@ekklesia/core/mail"; import { createStorageProviderConfig } from "@ekklesia/core/storage"; import { describe, expect, it } from "vitest"; import { createApiRuntimeConfig } from "../src/config.js"; import { readConfigFromEnvironment } from "../src/server.js"; /** * `pnpm doctor` and `startApi` must reach the same conclusions from the same environment. They * previously could not: each executable read its own subset of the variables, and Doctor's subset * omitted `S3_*` and the module secrets entirely. * * These tests assert the property directly — the API's startup inputs and the inputs Doctor * validates come from one `loadHostConfiguration` call and produce identical validated * configuration, including every default. */ const secretKey = "sk-example-secret-material"; const smtpPassword = "sup3r-secret-relay-password"; function environment( overrides: Readonly> = {}, ): Readonly> { return { DATABASE_URL: "postgres://ekklesia@db.example.org:5432/ekklesia", EKKLESIA_IDENTITY_BASE_URL: "https://ekklesia.example.org", EKKLESIA_IDENTITY_SECRET: "b7QwZ2rTn4Xs9LmVpKdF6yHc3JgEuNaW", EKKLESIA_IDENTITY_TRUSTED_ORIGINS: "https://ekklesia.example.org", NODE_ENV: "production", ...overrides, }; } const configuredInfrastructure = Object.freeze({ EKKLESIA_SMTP_FROM_ADDRESS: "noreply@example.org", EKKLESIA_SMTP_HOST: "smtp.example.org", EKKLESIA_SMTP_PASSWORD: smtpPassword, EKKLESIA_SMTP_USERNAME: "relay@example.org", S3_ACCESS_KEY: "AKIAEXAMPLEACCESSID", S3_ENDPOINT: "https://storage.example.org", S3_SECRET_KEY: secretKey, }); /** Exactly what Doctor validates, from exactly the same load the API performs. */ function doctorView(env: Readonly>) { const host = loadHostConfiguration(env); return { mail: createMailProviderConfig(host.mail, { environment: host.environment }), storage: createStorageProviderConfig(host.storage, { environment: host.environment }), }; } function apiView(env: Readonly>) { const { environment: resolved, input } = readConfigFromEnvironment(env); return createApiRuntimeConfig(input, { environment: resolved }); } describe("host configuration parity between startApi and Doctor", () => { it("derives identical object-storage configuration, defaults included", () => { const env = environment(configuredInfrastructure); expect(apiView(env).storage).toEqual(doctorView(env).storage); // Named explicitly rather than only compared, so a change to either default fails here loudly. expect(apiView(env).storage).toEqual({ accessKey: "AKIAEXAMPLEACCESSID", bucketPrefix: "ekklesia", deliveryEndpoint: "https://storage.example.org", endpoint: "https://storage.example.org", forcePathStyle: true, region: "us-east-1", secretKey, }); }); it("derives identical outbound-email configuration, defaults included", () => { const env = environment(configuredInfrastructure); expect(apiView(env).mail).toEqual(doctorView(env).mail); expect(apiView(env).mail).toMatchObject({ host: "smtp.example.org", port: 587, security: "starttls", }); }); it("agrees that an installation with neither service is complete", () => { const env = environment(); expect(apiView(env).storage).toBeUndefined(); expect(doctorView(env).storage).toBeUndefined(); expect(apiView(env).mail).toBeUndefined(); expect(doctorView(env).mail).toBeUndefined(); }); it("carries every storage field the direct-delivery pass established", () => { const env = environment({ ...configuredInfrastructure, S3_BUCKET_PREFIX: "rogc", S3_DELIVERY_ENDPOINT: "https://files.example.org", S3_FORCE_PATH_STYLE: "false", S3_REGION: "eu-west-1", }); expect(apiView(env).storage).toEqual(doctorView(env).storage); expect(apiView(env).storage).toMatchObject({ bucketPrefix: "rogc", deliveryEndpoint: "https://files.example.org", forcePathStyle: false, region: "eu-west-1", }); }); it("rejects the same malformed environment with the same sanitized code", () => { for (const [overrides, code] of [ [{ S3_ENDPOINT: "https://storage.example.org" }, "STORAGE_ACCESS_KEY_MISSING"], [{ ...configuredInfrastructure, S3_ENDPOINT: "not-a-url" }, "STORAGE_ENDPOINT_INVALID"], [ { ...configuredInfrastructure, S3_ENDPOINT: "http://storage.example.org" }, "STORAGE_ENDPOINT_INSECURE_IN_PRODUCTION", ], [ { ...configuredInfrastructure, EKKLESIA_SMTP_PASSWORD: undefined }, "MAIL_USERNAME_WITHOUT_PASSWORD", ], [{ ...configuredInfrastructure, EKKLESIA_SMTP_PORT: "70000" }, "MAIL_PORT_INVALID"], ] as const) { const env = environment(overrides); expect(() => apiView(env)).toThrowError(expect.objectContaining({ code })); expect(() => doctorView(env)).toThrowError(expect.objectContaining({ code })); } }); it("resolves module secrets from the same source the runtime is given", () => { const env = environment({ EKKLESIA_MODULE_SECRET__ANNOUNCEMENTS__STORAGE_CREDENTIAL: "module-secret-material", }); expect( readConfigFromEnvironment(env).moduleSecrets.get("announcements", "storageCredential"), ).toBe("module-secret-material"); expect(loadHostConfiguration(env).moduleSecrets.get("announcements", "storageCredential")).toBe( "module-secret-material", ); }); it("reads only this listener's own address and port outside the shared loader", () => { const config = apiView( environment({ EKKLESIA_API_HOST: "0.0.0.0", EKKLESIA_API_PORT: "8080" }), ); expect(config.host).toBe("0.0.0.0"); expect(config.port).toBe(8080); // Defaults, when unset, stay where they always were. const defaults = apiView(environment()); expect(defaults.host).toBe("127.0.0.1"); expect(defaults.port).toBe(3000); }); it("interprets a set-but-empty flag identically instead of one side ignoring it", () => { // Doctor used to drop any falsy identity value before validating, so `startApi` refused to // start on `EKKLESIA_PUBLIC_SIGNUP_ENABLED=` while Doctor reported the deployment ready. const env = environment({ EKKLESIA_PUBLIC_SIGNUP_ENABLED: "" }); expect(loadHostConfiguration(env).identity.publicSignupEnabled).toBe(""); expect(() => apiView(env)).toThrowError( expect.objectContaining({ code: "PUBLIC_SIGNUP_ENABLED_INVALID" }), ); }); });