@wenathlan/getry 2.0.152 → 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.
@@ -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
+ });