Lumi/scripts/verify-command-policies.js
2026-07-19 16:04:01 +02:00

117 lines
5.8 KiB
JavaScript

const assert = require("assert");
const fs = require("fs");
const path = require("path");
const root = path.join(__dirname, "..");
const sandbox = fs.mkdtempSync(path.join(root, ".tmp-lumi-command-policies-"));
const serviceDir = path.join(sandbox, "src", "services");
let database = null;
(async () => {
try {
fs.cpSync(path.join(root, "src", "services"), serviceDir, { recursive: true });
database = require(path.join(serviceDir, "db.js"));
database.migrate();
const conditional = require(path.join(serviceDir, "command-conditional.js"));
const policies = require(path.join(serviceDir, "command-policies.js"));
const defaultGroup = policies.listGroups().find((group) => group.is_default);
assert(defaultGroup, "a protected default command group must be created automatically");
assert.throws(() => policies.deleteGroup(defaultGroup.id), /cannot be deleted/);
const replies = conditional.normalizeConditionalReplies([
{ keyword: "backseating", response: "Please avoid backseating." },
{ keyword: "spoilers", response: "No spoilers." }
]);
assert.strictEqual(conditional.findConditionalReply(replies, "backseating").match.response, "Please avoid backseating.");
const fuzzy = conditional.findConditionalReply(replies, "backseatng");
assert.strictEqual(fuzzy.match.keyword, "backseating");
assert.strictEqual(fuzzy.fuzzy, true);
assert.strictEqual(conditional.findConditionalReply(replies, "something unrelated").match, null);
const groupId = policies.createGroup({
name: "Verification group",
policy: {
window: { uses: 3, seconds: 30, scope: "global" },
requirement: { role: "subscriber", subscriberTier: 2 },
cost: { amount: 5 }
}
});
const key = policies.commandKey("verification", "hello");
policies.saveCommandPolicy(key, {
window: { uses: 1, seconds: 60, scope: "user" },
requirement: false,
cost: { amount: 2 }
}, [groupId]);
const resolved = policies.resolvePolicy(key);
assert.strictEqual(resolved.windows.length, 1, "a direct rate limit must replace group limits");
assert.strictEqual(resolved.windows[0].value.uses, 1);
assert.strictEqual(resolved.requirements.length, 0, "a direct public setting must replace group roles");
assert.strictEqual(resolved.cost, 2, "a direct cost must replace the group cost");
policies.updateGroup(defaultGroup.id, {
name: defaultGroup.name,
description: defaultGroup.description,
policy: { window: { uses: 4, seconds: 90, scope: "global" } }
});
const inheritedKey = policies.commandKey("verification", "inherited");
assert.strictEqual(policies.resolvePolicy(inheritedKey).windows[0].value.uses, 4, "the default group must apply without explicit membership");
policies.saveCommandPolicies([
{ commandKey: inheritedKey, policy: { cost: { amount: 3 } }, groupIds: [] },
{ commandKey: policies.commandKey("verification", "second"), policy: { requirement: false }, groupIds: [groupId] }
]);
assert.strictEqual(policies.resolvePolicy(inheritedKey).cost, 3, "batch saves must persist every submitted command");
const charges = [];
global.lumiFrameworks = {
economy: {
removeBalance(payload) { charges.push(["charge", payload]); return { ok: true }; },
addBalance(payload) { charges.push(["refund", payload]); return "refund-id"; }
}
};
const ctx = {
platform: "discord",
trigger: "hello",
raw: "!hello",
user: { id: "verification-user", platformId: "platform-user" },
platformUser: { id: "platform-user" },
meta: {}
};
const first = await policies.beginCommandUse({ commandKey: key, ctx });
assert.strictEqual(first.allowed, true);
assert.strictEqual(charges[0][0], "charge");
const limited = await policies.beginCommandUse({ commandKey: key, ctx });
assert.strictEqual(limited.allowed, false, "the second use inside the window must be denied");
await first.rollback("verification");
assert.strictEqual(charges[1][0], "refund", "failed commands must refund their logged currency cost");
const afterRollback = await policies.beginCommandUse({ commandKey: key, ctx });
assert.strictEqual(afterRollback.allowed, true, "rollback must release the rate-limit reservation");
await afterRollback.commit();
const dbColumns = database.db.prepare("PRAGMA table_info(custom_commands)").all().map((column) => column.name);
assert(dbColumns.includes("conditional_replies_json"));
assert(dbColumns.includes("conditional_fuzzy"));
assert(database.db.prepare("PRAGMA table_info(command_groups)").all().some((column) => column.name === "is_default"));
for (const table of ["command_groups", "command_group_members", "command_policies", "command_policy_uses", "command_stream_sessions"]) {
assert(database.db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(table), `missing ${table}`);
}
const server = fs.readFileSync(path.join(root, "src", "web", "server.js"), "utf8");
const view = fs.readFileSync(path.join(root, "src", "web", "views", "admin-command-policies.ejs"), "utf8");
const commandView = fs.readFileSync(path.join(root, "src", "web", "views", "admin-commands.ejs"), "utf8");
assert(server.includes('/admin/command-policies') && server.includes('commandPolicyFromBody'));
assert(view.includes("Command groups") && view.includes("Save all command settings"));
assert(view.includes("data-policy-search") && view.includes("data-policy-setting-filter"));
assert(view.includes("Protected default") && view.includes("data-command-policy-batch"));
assert(commandView.includes('value="conditional"') && commandView.includes("data-conditional-replies"));
database.db.close();
database = null;
console.log("Command policies and conditional commands verified.");
} finally {
try { database?.db?.close?.(); } catch {}
fs.rmSync(sandbox, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});