@emseepea/testing 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/package.json +1 -1
- package/semantic/cli.mjs +11 -0
- package/semantic/test.d.mts +5 -1
- package/semantic/test.mjs +18 -3
package/README.md
CHANGED
|
@@ -22,6 +22,13 @@ it with `assertToolArguments` for the stable call and `assertFeedback` for the
|
|
|
22
22
|
feedback observation and important detail. Keep `assertToolCalls` when every
|
|
23
23
|
complete argument should match exactly and no feedback tool is advertised.
|
|
24
24
|
|
|
25
|
+
For an intentionally optional permission check, use
|
|
26
|
+
`assertToolNames(turn, { oneOf: [["update"], ["permissions", "update"]] })`.
|
|
27
|
+
Supply one to eight explicit sequences. Each trial must match a complete
|
|
28
|
+
sequence in the listed order and count. The evidence retains all alternatives
|
|
29
|
+
and the matched sequence; arguments, meaning, confirmation and feedback still
|
|
30
|
+
need their own assertions.
|
|
31
|
+
|
|
25
32
|
For a successful turn that advertises feedback, use
|
|
26
33
|
`assertToolCallsWithOptionalFeedback`. It requires the exact ordered primary
|
|
27
34
|
calls and arguments, then allows no feedback call or one trailing
|
package/package.json
CHANGED
package/semantic/cli.mjs
CHANGED
|
@@ -172,6 +172,15 @@ function validToolAssertions(turn, isHash) {
|
|
|
172
172
|
|| (turn.toolCalls.length === 1 && turn.toolCalls[0].name === turn.expectedOptionalTool));
|
|
173
173
|
}
|
|
174
174
|
if (JSON.stringify(turn.selectedTools) !== JSON.stringify(turn.expectedTools)) return false;
|
|
175
|
+
if (turn.expectedToolAlternatives !== undefined) {
|
|
176
|
+
const alternatives = turn.expectedToolAlternatives;
|
|
177
|
+
if (!Array.isArray(alternatives) || alternatives.length < 1 || alternatives.length > 8
|
|
178
|
+
|| !alternatives.every((names) => Array.isArray(names)
|
|
179
|
+
&& names.every((name) => typeof name === "string" && name.trim()))
|
|
180
|
+
|| !alternatives.some((names) => JSON.stringify(names) === JSON.stringify(turn.expectedTools))) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
175
184
|
if (isHash(turn.expectedCallsSha256)) {
|
|
176
185
|
return JSON.stringify(turn.toolCalls.map(({ name, arguments: args }) => ({ name, arguments: args })))
|
|
177
186
|
=== JSON.stringify(turn.expectedCalls);
|
|
@@ -181,6 +190,8 @@ function validToolAssertions(turn, isHash) {
|
|
|
181
190
|
tools: turn.expectedTools,
|
|
182
191
|
arguments: turn.expectedArguments,
|
|
183
192
|
feedback: turn.expectedFeedback,
|
|
193
|
+
...(turn.expectedToolAlternatives === undefined
|
|
194
|
+
? {} : { alternatives: turn.expectedToolAlternatives }),
|
|
184
195
|
})).digest("hex");
|
|
185
196
|
if (expectedHash !== turn.expectedSelectionSha256) return false;
|
|
186
197
|
for (const [name, expected] of Object.entries(turn.expectedArguments ?? {})) {
|
package/semantic/test.d.mts
CHANGED
|
@@ -54,7 +54,11 @@ export function assertToolCallsWithOptionalFeedback(
|
|
|
54
54
|
export function assertNoToolCalls(turn: ConversationTurn): void;
|
|
55
55
|
/** Allows no tool call or exactly one call to the named tool in each trial. */
|
|
56
56
|
export function assertOptionalToolCall(turn: ConversationTurn, name: string): void;
|
|
57
|
-
|
|
57
|
+
/** Requires an exact sequence, or one of at most eight explicitly authored sequences. */
|
|
58
|
+
export function assertToolNames(
|
|
59
|
+
turn: ConversationTurn,
|
|
60
|
+
expected: readonly string[] | { readonly oneOf: readonly (readonly string[])[] },
|
|
61
|
+
): void;
|
|
58
62
|
export function assertToolArguments(
|
|
59
63
|
turn: ConversationTurn,
|
|
60
64
|
name: string,
|
package/semantic/test.mjs
CHANGED
|
@@ -268,15 +268,28 @@ export function assertOptionalToolCall(turn, name) {
|
|
|
268
268
|
|
|
269
269
|
export function assertToolNames(turn, expected) {
|
|
270
270
|
const trials = turnTrials(turn);
|
|
271
|
-
|
|
271
|
+
const validNames = (names) => Array.isArray(names)
|
|
272
|
+
&& names.every((name) => typeof name === "string" && name.trim());
|
|
273
|
+
const alternatives = Array.isArray(expected) ? undefined : expected?.oneOf;
|
|
274
|
+
if (alternatives !== undefined && (!Array.isArray(alternatives)
|
|
275
|
+
|| alternatives.length < 1 || alternatives.length > 8 || !alternatives.every(validNames))) {
|
|
276
|
+
throw new Error("Expected tool alternatives must contain one to eight string arrays");
|
|
277
|
+
}
|
|
278
|
+
if (alternatives === undefined && !validNames(expected)) {
|
|
272
279
|
throw new Error("Expected tool names must be a string array");
|
|
273
280
|
}
|
|
281
|
+
const sequences = (alternatives ?? [expected]).map((names) => [...names]);
|
|
274
282
|
for (const trial of trials) {
|
|
275
|
-
trial.
|
|
283
|
+
const actual = trial.calls.map(({ name }) => name);
|
|
284
|
+
const match = sequences.find((names) => JSON.stringify(names) === JSON.stringify(actual));
|
|
285
|
+
trial.record.expectedTools = match ?? sequences[0];
|
|
286
|
+
if (alternatives !== undefined) trial.record.expectedToolAlternatives = sequences;
|
|
276
287
|
recordFlexibleExpectation(trial.record);
|
|
277
288
|
}
|
|
278
289
|
try {
|
|
279
|
-
for (const trial of trials)
|
|
290
|
+
for (const trial of trials) {
|
|
291
|
+
assert.deepStrictEqual(trial.calls.map(({ name }) => name), trial.record.expectedTools);
|
|
292
|
+
}
|
|
280
293
|
} catch {
|
|
281
294
|
failAssertion(trials, "tool-name assertion");
|
|
282
295
|
throw new Error("Tool names did not match the expected order and count");
|
|
@@ -552,6 +565,8 @@ function recordFlexibleExpectation(record) {
|
|
|
552
565
|
tools: record.expectedTools,
|
|
553
566
|
arguments: record.expectedArguments,
|
|
554
567
|
feedback: record.expectedFeedback,
|
|
568
|
+
...(record.expectedToolAlternatives === undefined
|
|
569
|
+
? {} : { alternatives: record.expectedToolAlternatives }),
|
|
555
570
|
}));
|
|
556
571
|
}
|
|
557
572
|
|