@wenathlan/getry 2.0.154 → 2.0.155
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/package.json +182 -11
- package/tests/cli.test.ts +300 -0
- package/tests/debonairlane.test.ts +66 -21
- package/tests/gateway.test.ts +962 -0
- package/tests/gatewayserver.ts +99 -0
- package/tests/intelligence.test.ts +62 -0
- package/tests/jsonpolicy.test.ts +289 -0
- package/tests/llm.test.ts +696 -0
- package/tests/modelroute.test.ts +112 -0
- package/tests/promptlibrary.test.ts +116 -0
- package/tests/provider.constants.test.ts +134 -0
- package/tests/provider.fingerprint.test.ts +149 -0
- package/tests/provider.models.test.ts +250 -0
- package/tests/provider.oauth.test.ts +150 -0
- package/tests/providercli.test.ts +84 -0
- package/tests/providerfixtures.ts +224 -0
- package/tests/researchfixtures.ts +4572 -0
- package/tests/server.auth.test.ts +394 -0
- package/tests/server.engine.test.ts +1074 -0
- package/tests/server.property.test.ts +220 -0
- package/tests/server.utils.test.ts +546 -0
- package/tests/torture.llm.test.ts +807 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* property — fast-check property-based fuzzing tests
|
|
3
|
+
* the http transport and shared helpers are verified with generated
|
|
4
|
+
* inputs (scorecard detects fast-check as the property-based fuzzing
|
|
5
|
+
* integration for typescript)
|
|
6
|
+
*
|
|
7
|
+
* properties verified:
|
|
8
|
+
* parsesseframes — splitting is lossless: frames joined with the
|
|
9
|
+
* separator reproduce the input; the residual is a suffix of the
|
|
10
|
+
* input; no frame contains the double newline separator
|
|
11
|
+
* extractdata — data lines concatenate exactly; the done marker
|
|
12
|
+
* yields null
|
|
13
|
+
* maskmodel — every model id inside the payload is replaced by the
|
|
14
|
+
* target id and the replacement count matches
|
|
15
|
+
* makechunk — openai chunk envelope invariants: object constant,
|
|
16
|
+
* nonempty delta carries either content or reasoning
|
|
17
|
+
* securerandom — uniform float in [0 1)
|
|
18
|
+
* genid — prefix preserved, unique for distinct calls
|
|
19
|
+
* randombase36 — length and alphabet invariants
|
|
20
|
+
* clamp — never leaves the bounds
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import fc from "fast-check";
|
|
24
|
+
import { describe, expect, it } from "vitest";
|
|
25
|
+
|
|
26
|
+
import { extractdata, isdone, makechunk, makefinalchunk, maskmodel, parsesseframes } from "../server.js";
|
|
27
|
+
import { clamp, genid, randombase36, securerandom } from "../utils.js";
|
|
28
|
+
|
|
29
|
+
/** arbitrary printable ascii lines without the sse separator */
|
|
30
|
+
const lineArb = fc.string({
|
|
31
|
+
unit: fc.constantFrom(..."abcdefghijklmnopqrstuvwxyz0123456789: ".split("")),
|
|
32
|
+
minLength: 0,
|
|
33
|
+
maxLength: 40,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** arbitrary sse frame content lines */
|
|
37
|
+
const frameArb = fc.array(lineArb, { minLength: 1, maxLength: 5 }).map((lines) => lines.join("\n"));
|
|
38
|
+
|
|
39
|
+
/** arbitrary model id */
|
|
40
|
+
const modelidArb = fc
|
|
41
|
+
.string({
|
|
42
|
+
unit: fc.constantFrom(..."abcdefghijklmnopqrstuvwxyz0123456789-.".split("")),
|
|
43
|
+
minLength: 1,
|
|
44
|
+
maxLength: 24,
|
|
45
|
+
})
|
|
46
|
+
.filter((s) => !s.includes(":"));
|
|
47
|
+
|
|
48
|
+
describe("parsesseframes properties", () => {
|
|
49
|
+
it("splitting is lossless: frames + residual reproduce the input", () => {
|
|
50
|
+
fc.assert(
|
|
51
|
+
fc.property(fc.array(frameArb, { minLength: 0, maxLength: 8 }), (frames) => {
|
|
52
|
+
const input = frames.length > 0 ? `${frames.join("\n\n")}\n\n` : "";
|
|
53
|
+
const { frames: out, residual } = parsesseframes(input);
|
|
54
|
+
// frames are the between-separator segments: the input is the
|
|
55
|
+
// frames rejoined with the separator plus the trailing
|
|
56
|
+
// residual (the trailing separator belongs to the last
|
|
57
|
+
// complete frame)
|
|
58
|
+
const reconstructed = out.length > 0 ? `${out.join("\n\n")}\n\n${residual}` : residual;
|
|
59
|
+
expect(reconstructed).toBe(input.replace(/\r\n/g, "\n"));
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("the residual is always a proper suffix of the input", () => {
|
|
65
|
+
fc.assert(
|
|
66
|
+
fc.property(frameArb, frameArb, (a, b) => {
|
|
67
|
+
const input = `${a}\n\n${b}`; // b may itself contain separators
|
|
68
|
+
const { residual } = parsesseframes(input);
|
|
69
|
+
expect(input.replace(/\r\n/g, "\n")).toContain(residual);
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("no complete frame contains the double newline separator", () => {
|
|
75
|
+
fc.assert(
|
|
76
|
+
fc.property(fc.array(frameArb, { minLength: 0, maxLength: 6 }), (frames) => {
|
|
77
|
+
const input = frames.length > 0 ? `${frames.join("\n\n")}\n\n` : "";
|
|
78
|
+
for (const frame of parsesseframes(input).frames) {
|
|
79
|
+
expect(frame.includes("\n\n")).toBe(false);
|
|
80
|
+
}
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("extractdata and isdone properties", () => {
|
|
87
|
+
it("data lines concatenate exactly and never include the data prefix", () => {
|
|
88
|
+
fc.assert(
|
|
89
|
+
fc.property(
|
|
90
|
+
fc.array(
|
|
91
|
+
fc
|
|
92
|
+
.string({ minLength: 0, maxLength: 30 })
|
|
93
|
+
.filter((s) => !s.startsWith("data:") && !s.includes("\n") && !/^\s/.test(s)),
|
|
94
|
+
{ minLength: 1, maxLength: 5 },
|
|
95
|
+
),
|
|
96
|
+
(datas) => {
|
|
97
|
+
const frame = datas.map((d) => `data: ${d}`).join("\n");
|
|
98
|
+
const extracted = extractdata(frame);
|
|
99
|
+
expect(extracted).toBe(datas.join("\n"));
|
|
100
|
+
},
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("the done marker always maps to null and isdone", () => {
|
|
106
|
+
fc.assert(
|
|
107
|
+
fc.property(fc.constantFrom("[DONE]", "[done]", "done", "data: [DONE]"), (marker) => {
|
|
108
|
+
expect(extractdata(`data: ${marker}`)).toBeNull();
|
|
109
|
+
expect(isdone(marker)).toBe(true);
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("maskmodel properties", () => {
|
|
116
|
+
it("every model field is replaced with the target id", () => {
|
|
117
|
+
fc.assert(
|
|
118
|
+
fc.property(modelidArb, modelidArb, modelidArb, (target, id1, id2) => {
|
|
119
|
+
const payload = `{"model":"${id1}","choices":[{"delta":{"content":"𝄞"},"model":"${id2}"}]}`;
|
|
120
|
+
const masked = maskmodel(payload, target);
|
|
121
|
+
// every model field now carries exactly the target id
|
|
122
|
+
const fields = masked.match(/"model":"[^"]*"/g) ?? [];
|
|
123
|
+
expect(fields).toHaveLength(2);
|
|
124
|
+
for (const field of fields) {
|
|
125
|
+
expect(field).toBe(`"model":"${target}"`);
|
|
126
|
+
}
|
|
127
|
+
// an id distinct from the target is fully gone
|
|
128
|
+
if (id1 !== target) expect(masked).not.toContain(`"${id1}"`);
|
|
129
|
+
if (id2 !== target && id2 !== id1) expect(masked).not.toContain(`"${id2}"`);
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("makechunk invariants", () => {
|
|
136
|
+
it("the envelope shape is constant and the delta carries payload", () => {
|
|
137
|
+
fc.assert(
|
|
138
|
+
fc.property(
|
|
139
|
+
fc.string({ minLength: 1, maxLength: 50 }),
|
|
140
|
+
fc.string({ minLength: 1, maxLength: 50 }),
|
|
141
|
+
fc.option(fc.string({ minLength: 1, maxLength: 8 }), { nil: undefined }),
|
|
142
|
+
(id, content, reasoning) => {
|
|
143
|
+
const chunk = makechunk(id, "meta-model", content, reasoning) as Record<string, unknown>;
|
|
144
|
+
expect(chunk["object"]).toBe("chat.completion.chunk");
|
|
145
|
+
expect(chunk["model"]).toBe("meta-model");
|
|
146
|
+
const choice = (chunk["choices"] as Array<Record<string, unknown>>)[0];
|
|
147
|
+
const delta = choice["delta"] as Record<string, unknown>;
|
|
148
|
+
expect(delta["content"]).toBe(content);
|
|
149
|
+
if (reasoning) expect(delta["reasoning_content"]).toBe(reasoning);
|
|
150
|
+
},
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("the final chunk always carries a finish reason", () => {
|
|
156
|
+
fc.assert(
|
|
157
|
+
fc.property(fc.constantFrom("stop", "length", "tool_calls", "content_filter"), (reason) => {
|
|
158
|
+
const chunk = makefinalchunk("id", "meta-model", reason) as Record<string, unknown>;
|
|
159
|
+
const choice = (chunk["choices"] as Array<Record<string, unknown>>)[0];
|
|
160
|
+
expect(choice["finish_reason"]).toBe(reason);
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe("utils properties", () => {
|
|
167
|
+
it("securerandom is a uniform float in [0 1)", () => {
|
|
168
|
+
fc.assert(
|
|
169
|
+
fc.property(fc.nat(500), () => {
|
|
170
|
+
const value = securerandom();
|
|
171
|
+
expect(value).toBeGreaterThanOrEqual(0);
|
|
172
|
+
expect(value).toBeLessThan(1);
|
|
173
|
+
}),
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("genid keeps the prefix and stays unique across calls", () => {
|
|
178
|
+
fc.assert(
|
|
179
|
+
fc.property(
|
|
180
|
+
fc.string({
|
|
181
|
+
unit: fc.constantFrom(..."abcdefghijklmnopqrstuvwxyz-".split("")),
|
|
182
|
+
minLength: 1,
|
|
183
|
+
maxLength: 12,
|
|
184
|
+
}),
|
|
185
|
+
fc.nat(200),
|
|
186
|
+
(prefix, count) => {
|
|
187
|
+
const ids = new Set<string>();
|
|
188
|
+
for (let i = 0; i <= count; i += 1) {
|
|
189
|
+
const id = genid(prefix);
|
|
190
|
+
expect(id.startsWith(`${prefix}-`)).toBe(true);
|
|
191
|
+
ids.add(id);
|
|
192
|
+
}
|
|
193
|
+
expect(ids.size).toBe(count + 1);
|
|
194
|
+
},
|
|
195
|
+
),
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("randombase36 has the exact length and alphabet", () => {
|
|
200
|
+
fc.assert(
|
|
201
|
+
fc.property(fc.integer({ min: 1, max: 64 }), (n) => {
|
|
202
|
+
const out = randombase36(n);
|
|
203
|
+
expect(out).toHaveLength(n);
|
|
204
|
+
expect(out).toMatch(/^[0-9a-z]+$/);
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("clamp never leaves the bounds", () => {
|
|
210
|
+
fc.assert(
|
|
211
|
+
fc.property(fc.nat(1000), fc.nat(1000), fc.nat(1000), (a, b, c) => {
|
|
212
|
+
const lo = Math.min(a, b);
|
|
213
|
+
const hi = Math.max(a, b);
|
|
214
|
+
const clamped = clamp(c, lo, hi);
|
|
215
|
+
expect(clamped).toBeGreaterThanOrEqual(lo);
|
|
216
|
+
expect(clamped).toBeLessThanOrEqual(hi);
|
|
217
|
+
}),
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
});
|