Say what a skill proposal changes, so a kept runbook never reads as saved
propose_skill took no strict schema, so a runbook sent under another key was dropped and the saved one proposed again; the confirm still said "Saved". Now an unknown key is an error, a change that changes nothing is refused, and both the bot and the confirm outcome are told which fields change and whether the runbook is among them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import type { SkillRecord } from "@/src/types";
|
||||
import skillProposalChanges from "./skill-proposal-changes";
|
||||
|
||||
const current: SkillRecord = {
|
||||
id: 2,
|
||||
name: "WhatsApp Web API",
|
||||
slug: "whatsapp-web-api",
|
||||
description: "Old description",
|
||||
body: "1. Install Baileys.\n2. Pair.",
|
||||
all_bots: false,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
describe("skillProposalChanges", () => {
|
||||
test("a change that kept the old runbook doesn't name the runbook", () => {
|
||||
const changes = skillProposalChanges({ current, data: { ...current, skill_id: 2, description: "New description" } });
|
||||
|
||||
expect(changes.fields).toEqual(["description"]);
|
||||
expect(changes.body_changed).toBe(false);
|
||||
});
|
||||
|
||||
test("a new runbook is named, with both lengths", () => {
|
||||
const body = "1. Install Baileys, @hapi/boom and pino.\n2. Pair.\n3. Allowlist.";
|
||||
const changes = skillProposalChanges({ current, data: { ...current, skill_id: 2, body } });
|
||||
|
||||
expect(changes.fields).toEqual(["runbook"]);
|
||||
expect(changes.body_chars).toBe(body.length);
|
||||
expect(changes.was_body_chars).toBe(current.body.length);
|
||||
});
|
||||
|
||||
test("saving it as it is changes nothing", () => {
|
||||
expect(skillProposalChanges({ current, data: { ...current, skill_id: 2 } }).fields).toEqual([]);
|
||||
});
|
||||
|
||||
test("turning it off and giving it to every bot are changes", () => {
|
||||
const changes = skillProposalChanges({ current, data: { ...current, enabled: false, all_bots: true } });
|
||||
|
||||
expect(changes.fields).toEqual(["on/off", "every bot gets it"]);
|
||||
});
|
||||
|
||||
test("a new skill is all new", () => {
|
||||
const changes = skillProposalChanges({
|
||||
current: null,
|
||||
data: { name: "Tone", slug: "tone", description: "Every reply", body: "Be brief.", enabled: true, all_bots: false },
|
||||
});
|
||||
|
||||
expect(changes.fields).toEqual(["name", "slug", "description", "runbook"]);
|
||||
expect(changes.body_changed).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { SaveSkillRequest } from "@/src/schemas/extension-schema";
|
||||
import type { SkillRecord } from "@/src/types";
|
||||
|
||||
type Params = {
|
||||
data: SaveSkillRequest;
|
||||
/**
|
||||
* The skill as saved now. Null for a new one, where everything is new.
|
||||
*/
|
||||
current: SkillRecord | null;
|
||||
};
|
||||
|
||||
export type SkillProposalChanges = {
|
||||
/**
|
||||
* What a change would make different, in words ("name", "runbook", ...).
|
||||
* Empty when confirming it would save the skill exactly as it is.
|
||||
*/
|
||||
fields: string[];
|
||||
body_changed: boolean;
|
||||
body_chars: number;
|
||||
was_body_chars: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* What saving a proposed skill would change against the saved one, so the
|
||||
* bot and the confirm outcome can say it plainly. A proposal whose runbook
|
||||
* came out the same (the bot left out `body`) reads "runbook" nowhere here.
|
||||
*/
|
||||
export default function skillProposalChanges({ data, current }: Params): SkillProposalChanges {
|
||||
const body_changed = !current || current.body != data.body;
|
||||
const checks = [
|
||||
{ field: "name", changed: !current || current.name != data.name },
|
||||
{ field: "slug", changed: !current || current.slug != data.slug },
|
||||
{ field: "description", changed: !current || current.description != data.description },
|
||||
{ field: "runbook", changed: body_changed },
|
||||
{ field: "on/off", changed: !!current && !!current.enabled != data.enabled },
|
||||
{ field: "every bot gets it", changed: !!current && data.all_bots && !current.all_bots },
|
||||
];
|
||||
|
||||
return {
|
||||
fields: checks.filter((check) => check.changed).map((check) => check.field),
|
||||
body_changed,
|
||||
body_chars: data.body.length,
|
||||
was_body_chars: current?.body.length || 0,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user