import { describe, expect, it } from "vitest"; import { createDatabaseTransportPolicy } from "../src/database/index.js"; import { loadHostConfiguration } from "../src/host/index.js"; import { createIdentityConfig } from "../src/identity/index.js"; import { createMailProviderConfig } from "../src/mail/index.js"; import { createStorageProviderConfig } from "../src/storage/index.js"; /** * A complete, valid production deployment. Every optional service is present, because the bug this * suite exists for was a *configured* service being reported absent — an empty environment would * never have shown it. */ const productionEnvironment = Object.freeze({ DATABASE_URL: "postgres://ekklesia@db.example.org:5432/ekklesia", EKKLESIA_CLIENT_ADDRESS_MODE: "trusted-proxy", EKKLESIA_CLIENT_ADDRESS_TRUSTED_PROXIES: "10.40.0.1, 10.40.0.2 ,", EKKLESIA_IDENTITY_BASE_URL: "https://ekklesia.example.org", EKKLESIA_IDENTITY_SECRET: "b7QwZ2rTn4Xs9LmVpKdF6yHc3JgEuNaW", EKKLESIA_IDENTITY_TRUSTED_ORIGINS: "https://ekklesia.example.org, https://display.example.org", EKKLESIA_MODULE_SECRET__ANNOUNCEMENTS__STORAGE_CREDENTIAL: "module-secret-material", EKKLESIA_SMTP_FROM_ADDRESS: "noreply@example.org", EKKLESIA_SMTP_FROM_NAME: "Ekklesia", EKKLESIA_SMTP_HELO_NAME: "mail.example.org", EKKLESIA_SMTP_HOST: "smtp.example.org", EKKLESIA_SMTP_PASSWORD: "sup3r-secret-relay-password", EKKLESIA_SMTP_PORT: "587", EKKLESIA_SMTP_SECURITY: "starttls", EKKLESIA_SMTP_USERNAME: "relay@example.org", NODE_ENV: "production", S3_ACCESS_KEY: "AKIAEXAMPLEACCESSID", S3_BUCKET_PREFIX: "ekklesia", S3_DELIVERY_ENDPOINT: "https://files.example.org", S3_ENDPOINT: "https://storage.example.org", S3_FORCE_PATH_STYLE: "true", S3_REGION: "us-east-1", S3_SECRET_KEY: "sk-example-secret-material", }); describe("host configuration loading", () => { it("never reads the process environment, only the object it is given", () => { // Proved by construction: an empty environment produces an unconfigured deployment even though // this test process's own `process.env` may carry DATABASE_URL, S3_*, or EKKLESIA_SMTP_*. const host = loadHostConfiguration({}); expect(host.databaseUrl).toBeUndefined(); expect(host.storage.endpoint).toBeUndefined(); expect(host.mail.host).toBeUndefined(); expect(host.identity.secret).toBeUndefined(); }); it("carries the configured modules directory verbatim, and nothing when it is unset", () => { expect(loadHostConfiguration({}).modulesDirectory).toBeUndefined(); expect( loadHostConfiguration({ EKKLESIA_MODULES_DIR: "/var/lib/ekklesia/modules" }).modulesDirectory, ).toBe("/var/lib/ekklesia/modules"); // Validation is the resolver's job (ADR 0048). The loader neither repairs a relative or blank // value nor treats a set-but-empty variable as unset, so the resolver can refuse it. expect( loadHostConfiguration({ EKKLESIA_MODULES_DIR: "relative/modules" }).modulesDirectory, ).toBe("relative/modules"); expect(loadHostConfiguration({ EKKLESIA_MODULES_DIR: "" }).modulesDirectory).toBe(""); }); it("carries every object-storage field the storage passes established", () => { const host = loadHostConfiguration(productionEnvironment); expect(host.storage).toEqual({ accessKey: "AKIAEXAMPLEACCESSID", bucketPrefix: "ekklesia", deliveryEndpoint: "https://files.example.org", endpoint: "https://storage.example.org", forcePathStyle: "true", region: "us-east-1", secretKey: "sk-example-secret-material", }); }); it("carries every SMTP field, leaving credentials optional as a pair", () => { const host = loadHostConfiguration(productionEnvironment); expect(host.mail).toEqual({ fromAddress: "noreply@example.org", fromName: "Ekklesia", heloName: "mail.example.org", host: "smtp.example.org", password: "sup3r-secret-relay-password", port: "587", security: "starttls", username: "relay@example.org", }); const relayByAddress = loadHostConfiguration({ EKKLESIA_SMTP_FROM_ADDRESS: "noreply@example.org", EKKLESIA_SMTP_HOST: "smtp-relay.example.org", }); expect(relayByAddress.mail.username).toBeUndefined(); expect(relayByAddress.mail.password).toBeUndefined(); // The SMTP client identity is optional too, and unset is the ordinary case: it matters only // where the machine's own hostname is not a fully qualified name the relay accepts. expect(relayByAddress.mail.heloName).toBeUndefined(); // A source-IP-authorized relay has no credentials at all, and that must still validate. expect(createMailProviderConfig(relayByAddress.mail)).toBeDefined(); }); it("resolves module secrets only under names derived from a module's own declared key", () => { const host = loadHostConfiguration(productionEnvironment); expect(host.moduleSecrets.get("announcements", "storageCredential")).toBe( "module-secret-material", ); expect(host.moduleSecrets.get("announcements", "unknownKey")).toBeUndefined(); // There is no accessor that takes a raw variable name, so a module can never reach these. expect(host.moduleSecrets.get("announcements", "DATABASE_URL")).toBeUndefined(); }); it("treats only the exact string development as development", () => { expect(loadHostConfiguration({ NODE_ENV: "development" }).environment).toBe("development"); for (const value of ["Development", "dev", "test", "", "production", undefined]) { expect(loadHostConfiguration({ NODE_ENV: value }).environment).toBe("production"); } }); it("omits client-address settings entirely unless a mode is set", () => { expect(loadHostConfiguration({}).clientAddress).toBeUndefined(); // Half-configuring proxy mode by setting only the header must not silently enable it. expect( loadHostConfiguration({ EKKLESIA_CLIENT_ADDRESS_HEADER: "x-forwarded-for" }).clientAddress, ).toBeUndefined(); expect(loadHostConfiguration(productionEnvironment).clientAddress).toEqual({ header: undefined, mode: "trusted-proxy", trustedProxies: ["10.40.0.1", "10.40.0.2"], }); }); it("omits database transport keys that are unset rather than passing undefined values", () => { expect(loadHostConfiguration({}).databaseTransport).toEqual({}); expect( loadHostConfiguration({ EKKLESIA_DATABASE_CA: "-----BEGIN CERTIFICATE-----", EKKLESIA_DATABASE_TLS_MODE: "verify-full", }).databaseTransport, ).toEqual({ certificateAuthority: "-----BEGIN CERTIFICATE-----", mode: "verify-full", }); }); it("splits comma-separated lists identically wherever they appear", () => { const host = loadHostConfiguration({ EKKLESIA_CLIENT_ADDRESS_MODE: "trusted-proxy", EKKLESIA_CLIENT_ADDRESS_TRUSTED_PROXIES: " 10.0.0.1 ,, 10.0.0.2 ", EKKLESIA_IDENTITY_TRUSTED_ORIGINS: " https://a.example.org ,, https://b.example.org ", }); expect(host.identity.trustedOrigins).toEqual([ "https://a.example.org", "https://b.example.org", ]); expect(host.clientAddress?.trustedProxies).toEqual(["10.0.0.1", "10.0.0.2"]); // A variable that is set but empty yields an empty list, not `undefined` — the two mean // different things to the identity validator (`TRUSTED_ORIGINS_EMPTY` vs // `TRUSTED_ORIGINS_MISSING`), and both executables must see the same one. expect( loadHostConfiguration({ EKKLESIA_IDENTITY_TRUSTED_ORIGINS: "" }).identity.trustedOrigins, ).toEqual([]); expect(loadHostConfiguration({}).identity.trustedOrigins).toBeUndefined(); }); it("passes a malformed value through unchanged rather than repairing or discarding it", () => { // The loader deliberately does not validate: `startApi` wants a malformed value to abort // startup and Doctor wants it reported, and neither can happen if the load already threw or // silently dropped it. const host = loadHostConfiguration({ EKKLESIA_PUBLIC_SIGNUP_ENABLED: "", S3_ENDPOINT: "not-a-url", }); expect(host.identity.publicSignupEnabled).toBe(""); expect(host.storage.endpoint).toBe("not-a-url"); expect(() => createStorageProviderConfig(host.storage)).toThrowError(/absolute http/u); expect(() => createIdentityConfig(host.identity)).toThrowError(); }); it("is accepted unchanged by every Core validator that consumes it", () => { const host = loadHostConfiguration(productionEnvironment); expect(createIdentityConfig(host.identity, { environment: host.environment })).toMatchObject({ baseUrl: "https://ekklesia.example.org", publicSignupEnabled: false, }); expect( createDatabaseTransportPolicy(host.databaseTransport, { environment: host.environment }), ).toBeDefined(); expect(createStorageProviderConfig(host.storage, { environment: host.environment })).toEqual({ accessKey: "AKIAEXAMPLEACCESSID", bucketPrefix: "ekklesia", deliveryEndpoint: "https://files.example.org", endpoint: "https://storage.example.org", forcePathStyle: true, region: "us-east-1", secretKey: "sk-example-secret-material", }); expect(createMailProviderConfig(host.mail, { environment: host.environment })).toMatchObject({ host: "smtp.example.org", port: 587, security: "starttls", }); }); });