Files
Moltbot/src/cron/cron-protocol-conformance.test.ts
Advait Paliwal bc67af6ad8 cron: separate webhook POST delivery from announce (#17901)
* cron: split webhook delivery from announce mode

* cron: validate webhook delivery target

* cron: remove legacy webhook fallback config

* fix: finalize cron webhook delivery prep (#17901) (thanks @advaitpaliwal)

---------

Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
2026-02-16 02:36:00 -08:00

92 lines
3.3 KiB
TypeScript

import fs from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { MACOS_APP_SOURCES_DIR } from "../compat/legacy-names.js";
import { CronDeliverySchema } from "../gateway/protocol/schema.js";
type SchemaLike = {
anyOf?: Array<SchemaLike>;
properties?: Record<string, unknown>;
const?: unknown;
};
function extractDeliveryModes(schema: SchemaLike): string[] {
const modeSchema = schema.properties?.mode as SchemaLike | undefined;
const directModes = (modeSchema?.anyOf ?? [])
.map((entry) => entry?.const)
.filter((value): value is string => typeof value === "string");
if (directModes.length > 0) {
return directModes;
}
const unionModes = (schema.anyOf ?? [])
.map((entry) => {
const mode = entry.properties?.mode as SchemaLike | undefined;
return mode?.const;
})
.filter((value): value is string => typeof value === "string");
return Array.from(new Set(unionModes));
}
const UI_FILES = ["ui/src/ui/types.ts", "ui/src/ui/ui-types.ts", "ui/src/ui/views/cron.ts"];
const SWIFT_MODEL_CANDIDATES = [`${MACOS_APP_SOURCES_DIR}/CronModels.swift`];
const SWIFT_STATUS_CANDIDATES = [`${MACOS_APP_SOURCES_DIR}/GatewayConnection.swift`];
async function resolveSwiftFiles(cwd: string, candidates: string[]): Promise<string[]> {
const matches: string[] = [];
for (const relPath of candidates) {
try {
await fs.access(path.join(cwd, relPath));
matches.push(relPath);
} catch {
// ignore missing path
}
}
if (matches.length === 0) {
throw new Error(`Missing Swift cron definition. Tried: ${candidates.join(", ")}`);
}
return matches;
}
describe("cron protocol conformance", () => {
it("ui + swift include all cron delivery modes from gateway schema", async () => {
const modes = extractDeliveryModes(CronDeliverySchema as SchemaLike);
expect(modes.length).toBeGreaterThan(0);
const cwd = process.cwd();
for (const relPath of UI_FILES) {
const content = await fs.readFile(path.join(cwd, relPath), "utf-8");
for (const mode of modes) {
expect(content.includes(`"${mode}"`), `${relPath} missing delivery mode ${mode}`).toBe(
true,
);
}
}
const swiftModelFiles = await resolveSwiftFiles(cwd, SWIFT_MODEL_CANDIDATES);
for (const relPath of swiftModelFiles) {
const content = await fs.readFile(path.join(cwd, relPath), "utf-8");
for (const mode of modes) {
const pattern = new RegExp(`\\bcase\\s+${mode}\\b`);
expect(pattern.test(content), `${relPath} missing case ${mode}`).toBe(true);
}
}
});
it("cron status shape matches gateway fields in UI + Swift", async () => {
const cwd = process.cwd();
const uiTypes = await fs.readFile(path.join(cwd, "ui/src/ui/types.ts"), "utf-8");
expect(uiTypes.includes("export type CronStatus")).toBe(true);
expect(uiTypes.includes("jobs:")).toBe(true);
expect(uiTypes.includes("jobCount")).toBe(false);
const [swiftRelPath] = await resolveSwiftFiles(cwd, SWIFT_STATUS_CANDIDATES);
const swiftPath = path.join(cwd, swiftRelPath);
const swift = await fs.readFile(swiftPath, "utf-8");
expect(swift.includes("struct CronSchedulerStatus")).toBe(true);
expect(swift.includes("let jobs:")).toBe(true);
});
});