File size: 2,419 Bytes
eb3f11e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import { Value } from "typebox/value";
import { describe, expect, it } from "vitest";
import { AskUserToolSchema, normalizeAskUserParams } from "./ask-user-tool-normalization.js";

const validArgs = {
  questions: [
    {
      id: "deploy_target",
      header: "Deployment target",
      question: "Where should this deploy?",
      options: [
        { label: "Staging (Recommended)", description: "Safer default" },
        { label: "Production" },
      ],
    },
  ],
};

describe("ask_user normalization", () => {
  it("normalizes headers, forces free text, and clamps timeout", () => {
    const normalized = normalizeAskUserParams({ ...validArgs, timeoutSeconds: 5 });

    expect(normalized.timeoutSeconds).toBe(30);
    expect(normalized.questions[0]).toMatchObject({
      questionId: "deploy_target",
      header: "Deployment t",
      isOther: true,
    });
    expect(normalizeAskUserParams({ ...validArgs, timeoutSeconds: 9_999 }).timeoutSeconds).toBe(
      3_600,
    );
    expect(Value.Check(AskUserToolSchema, validArgs)).toBe(true);
    expect(
      Value.Check(AskUserToolSchema, {
        questions: [{ ...validArgs.questions[0], isSecret: true }],
      }),
    ).toBe(false);
    expect(normalized.questions[0]).not.toHaveProperty("isSecret");
  });

  it("repeats the structured-choice contract in the model-visible schema", () => {
    const schema = JSON.stringify(AskUserToolSchema);

    expect(schema).toContain("Put all selectable choices in options");
    expect(schema).toContain("Every selectable choice");
    expect(schema).toContain("True only when the user may choose several options at once");
  });

  it.each([
    ["empty questions", { questions: [] }, "1 to 3 questions"],
    [
      "too many questions",
      { questions: Array.from({ length: 4 }, () => validArgs.questions[0]) },
      "1 to 3 questions",
    ],
    [
      "too few options",
      { questions: [{ ...validArgs.questions[0], options: [{ label: "Only" }] }] },
      "2 to 4 options",
    ],
    [
      "duplicate ids",
      { questions: [validArgs.questions[0], validArgs.questions[0]] },
      "duplicate question id 'deploy_target'",
    ],
    [
      "invalid id",
      { questions: [{ ...validArgs.questions[0], id: "Deploy Target" }] },
      "must be snake_case",
    ],
  ])("rejects %s", (_name, args, error) => {
    expect(() => normalizeAskUserParams(args)).toThrow(error);
  });
});