105 lines
4.4 KiB
TypeScript
105 lines
4.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { ConfigError, loadConfig, parseExportConfig, parseRoomMappings, toGroupFileMapping } from "./config.js";
|
|
|
|
const WS = "11111111-1111-4111-8111-111111111111";
|
|
const GROUP = "22222222-2222-4222-8222-222222222222";
|
|
|
|
describe("parseRoomMappings", () => {
|
|
it("accepts immutable room IDs with UUID workspace/group", () => {
|
|
expect(parseRoomMappings(JSON.stringify([{ roomId: "!abc:example.org", workspaceId: WS, groupId: GROUP }])))
|
|
.toEqual([{ roomId: "!abc:example.org", workspaceId: WS, groupId: GROUP }]);
|
|
});
|
|
|
|
it("rejects aliases, non-UUIDs, duplicates, empty and malformed input", () => {
|
|
expect(() => parseRoomMappings(JSON.stringify([{ roomId: "#archive:example.org", workspaceId: WS, groupId: GROUP }])))
|
|
.toThrow(ConfigError);
|
|
expect(() => parseRoomMappings(JSON.stringify([{ roomId: "!a:x", workspaceId: "ws", groupId: GROUP }]))).toThrow(ConfigError);
|
|
const dup = { roomId: "!a:x", workspaceId: WS, groupId: GROUP };
|
|
expect(() => parseRoomMappings(JSON.stringify([dup, dup]))).toThrow(/duplicate/);
|
|
expect(() => parseRoomMappings("[]")).toThrow(ConfigError);
|
|
expect(() => parseRoomMappings("{nope")).toThrow(ConfigError);
|
|
});
|
|
|
|
it("maps to an unencrypted-archive GroupFileMapping kept forever", () => {
|
|
const mapping = toGroupFileMapping({ roomId: "!a:x", workspaceId: WS, groupId: GROUP });
|
|
expect(mapping.archivePolicy).toBe("unencrypted-archive");
|
|
expect(mapping.retention).toEqual({ legalHold: false, mode: "keep" });
|
|
});
|
|
});
|
|
|
|
const env = {
|
|
DATABASE_URL: "postgres://bot@db/hub",
|
|
MATRIX_HOMESERVER_URL: "https://matrix.example.org",
|
|
MATRIX_USER: "archive-bot",
|
|
MATRIX_PASSWORD: "pw",
|
|
ROOM_MAPPINGS: JSON.stringify([{ roomId: "!a:x", workspaceId: WS, groupId: GROUP }]),
|
|
FILES_OCID_WEBDAV_URL: "https://files.example.org",
|
|
FILES_OCID_USER: "svc",
|
|
FILES_OCID_PASSWORD: "pw2",
|
|
ARCHIVE_BOT_HEALTH_PORT: "9100",
|
|
MATRIX_MEDIA_REDIRECT_HOSTS: " R2.example.com ,",
|
|
};
|
|
void env;
|
|
|
|
describe("loadConfig", () => {
|
|
it("parses the full env contract", () => {
|
|
const config = loadConfig(env);
|
|
expect(config.healthPort).toBe(9100);
|
|
expect(config.mediaRedirectHosts).toEqual(["r2.example.com"]);
|
|
expect(config.matrixDeviceId).toBe("HUB_ARCHIVE_BOT");
|
|
});
|
|
|
|
it("fails on missing secrets or a bad port", () => {
|
|
expect(() => loadConfig({ ...env, MATRIX_PASSWORD: "" })).toThrow(/MATRIX_PASSWORD/);
|
|
expect(() => loadConfig({ ...env, ARCHIVE_BOT_HEALTH_PORT: "70000" })).toThrow(/PORT/);
|
|
expect(() => loadConfig({ ...env, FILES_OCID_WEBDAV_URL: "ftp://x" })).toThrow(/http/);
|
|
});
|
|
});
|
|
|
|
describe("parseExportConfig (phase-B encrypted exports)", () => {
|
|
const R = "age1" + "q".repeat(58);
|
|
const base = {
|
|
ARCHIVE_EXPORT_AGE_RECIPIENTS: R,
|
|
ARCHIVE_EXPORT_R2_ACCESS_KEY: "k",
|
|
ARCHIVE_EXPORT_R2_SECRET_KEY: "s",
|
|
};
|
|
|
|
it("is null when nothing is configured", () => {
|
|
expect(parseExportConfig({})).toBeNull();
|
|
});
|
|
|
|
it("is null when explicitly disabled, even with credentials present", () => {
|
|
expect(parseExportConfig({ ...base, ARCHIVE_EXPORT_ENABLED: "false" })).toBeNull();
|
|
});
|
|
|
|
it("parses recipients, R2 credentials and defaults", () => {
|
|
const cfg = parseExportConfig(base);
|
|
expect(cfg).toMatchObject({
|
|
enabled: true,
|
|
ageRecipients: [R],
|
|
every: 500,
|
|
r2: { region: "auto", bucket: "hub-matrix-media" },
|
|
});
|
|
});
|
|
|
|
it("de-duplicates recipients and honours ARCHIVE_EXPORT_EVERY", () => {
|
|
const cfg = parseExportConfig({ ...base, ARCHIVE_EXPORT_AGE_RECIPIENTS: `${R}, ${R}`, ARCHIVE_EXPORT_EVERY: "50" });
|
|
expect(cfg?.ageRecipients).toEqual([R]);
|
|
expect(cfg?.every).toBe(50);
|
|
});
|
|
|
|
it("rejects malformed recipients and missing credentials", () => {
|
|
expect(() => parseExportConfig({ ...base, ARCHIVE_EXPORT_AGE_RECIPIENTS: "AGE-SECRET-KEY-1X" })).toThrow(ConfigError);
|
|
expect(() => parseExportConfig({ ARCHIVE_EXPORT_AGE_RECIPIENTS: R })).toThrow(/ACCESS_KEY/);
|
|
expect(() => parseExportConfig({ ...base, ARCHIVE_EXPORT_EVERY: "-1" })).toThrow(ConfigError);
|
|
expect(() => parseExportConfig({ ...base, ARCHIVE_EXPORT_R2_ENDPOINT: "http://insecure" })).toThrow(ConfigError);
|
|
});
|
|
|
|
it("wires into loadConfig", () => {
|
|
const cfg = loadConfig({ ...env, ...base });
|
|
expect(cfg.export).not.toBeNull();
|
|
expect(cfg.export?.ageRecipients).toEqual([R]);
|
|
// and stays null in the default case
|
|
expect(loadConfig(env).export).toBeNull();
|
|
});
|
|
});
|