@workglow/test-contract 0.6.1
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 +232 -0
- package/dist/ai-provider/assertions/capabilityHonesty.d.ts +7 -0
- package/dist/ai-provider/assertions/dispose.d.ts +7 -0
- package/dist/ai-provider/assertions/inferAdvertisesRegistered.d.ts +15 -0
- package/dist/ai-provider/assertions/inferServesInferred.d.ts +20 -0
- package/dist/ai-provider/assertions/pricingMatchesModality.d.ts +36 -0
- package/dist/ai-provider/assertions/registryCoverage.d.ts +13 -0
- package/dist/ai-provider/assertions/sessionReuse.d.ts +7 -0
- package/dist/ai-provider/assertions/signalHonoring.d.ts +7 -0
- package/dist/ai-provider/assertions/structuredGeneration.d.ts +7 -0
- package/dist/ai-provider/assertions/textGenerationSmoke.d.ts +7 -0
- package/dist/ai-provider/assertions/toolCallAccumulator.d.ts +7 -0
- package/dist/ai-provider/assertions/toolCallMultiTurn.d.ts +7 -0
- package/dist/ai-provider/assertions/usageNormalization.d.ts +61 -0
- package/dist/ai-provider/fixtures.d.ts +8 -0
- package/dist/ai-provider/runAiProviderConformance.d.ts +7 -0
- package/dist/ai-provider/types.d.ts +75 -0
- package/dist/ai-provider.d.ts +25 -0
- package/dist/ai-provider.js +876 -0
- package/dist/ai-provider.js.map +26 -0
- package/dist/browser-context/ConformanceMockContext.d.ts +54 -0
- package/dist/browser-context/assertions/ariaRoundTrip.d.ts +7 -0
- package/dist/browser-context/assertions/capabilityHonesty.d.ts +7 -0
- package/dist/browser-context/assertions/itExpectFail.d.ts +8 -0
- package/dist/browser-context/assertions/networkIntrospection.d.ts +7 -0
- package/dist/browser-context/assertions/tabsLifecycle.d.ts +7 -0
- package/dist/browser-context/fixtures.d.ts +8 -0
- package/dist/browser-context/runIBrowserContextConformance.d.ts +7 -0
- package/dist/browser-context/types.d.ts +48 -0
- package/dist/browser-context.d.ts +18 -0
- package/dist/browser-context.js +567 -0
- package/dist/browser-context.js.map +17 -0
- package/dist/creditExhaustedSkip.d.ts +39 -0
- package/dist/creditExhaustedSkip.js +163 -0
- package/dist/creditExhaustedSkip.js.map +10 -0
- package/dist/entitlement-profile/assertions/denialShape.d.ts +13 -0
- package/dist/entitlement-profile/assertions/dispose.d.ts +16 -0
- package/dist/entitlement-profile/assertions/hierarchyHonoring.d.ts +11 -0
- package/dist/entitlement-profile/assertions/optionalNeverDenied.d.ts +11 -0
- package/dist/entitlement-profile/assertions/requestEntitlementShape.d.ts +12 -0
- package/dist/entitlement-profile/assertions/resourceScoping.d.ts +16 -0
- package/dist/entitlement-profile/assertions/subscribeGrant.d.ts +11 -0
- package/dist/entitlement-profile/assertions/subscribeReload.d.ts +11 -0
- package/dist/entitlement-profile/assertions/subscribeRevocation.d.ts +12 -0
- package/dist/entitlement-profile/assertions/surfaceCoverage.d.ts +7 -0
- package/dist/entitlement-profile/assertions/unsubscribeIdempotent.d.ts +7 -0
- package/dist/entitlement-profile/fixtures.d.ts +21 -0
- package/dist/entitlement-profile/runEntitlementProfileConformance.d.ts +7 -0
- package/dist/entitlement-profile/types.d.ts +45 -0
- package/dist/entitlement-profile.d.ts +23 -0
- package/dist/entitlement-profile.js +436 -0
- package/dist/entitlement-profile.js.map +22 -0
- package/dist/human-connector/MockHumanConnector.d.ts +29 -0
- package/dist/human-connector/assertions/abort.d.ts +7 -0
- package/dist/human-connector/assertions/capabilityHonesty.d.ts +7 -0
- package/dist/human-connector/assertions/concurrentIsolation.d.ts +7 -0
- package/dist/human-connector/assertions/multiTurnFollowUp.d.ts +7 -0
- package/dist/human-connector/assertions/notifyDisplayFastResolve.d.ts +7 -0
- package/dist/human-connector/assertions/roundtrip.d.ts +7 -0
- package/dist/human-connector/fixtures.d.ts +8 -0
- package/dist/human-connector/runHumanConnectorConformance.d.ts +9 -0
- package/dist/human-connector/types.d.ts +91 -0
- package/dist/human-connector.d.ts +19 -0
- package/dist/human-connector.js +843 -0
- package/dist/human-connector.js.map +20 -0
- package/dist/itExpectFail.d.ts +15 -0
- package/dist/itExpectFail.js +180 -0
- package/dist/itExpectFail.js.map +11 -0
- package/dist/storage-migrations/assertions/appliesAndRecords.d.ts +7 -0
- package/dist/storage-migrations/assertions/concurrentRunsSerialize.d.ts +18 -0
- package/dist/storage-migrations/assertions/ensureBookkeepingIdempotent.d.ts +7 -0
- package/dist/storage-migrations/assertions/failedMigrationLeavesNoPartialSchema.d.ts +14 -0
- package/dist/storage-migrations/assertions/failedMigrationNotRecorded.d.ts +7 -0
- package/dist/storage-migrations/assertions/idempotentRun.d.ts +7 -0
- package/dist/storage-migrations/assertions/incrementalApplication.d.ts +7 -0
- package/dist/storage-migrations/runMigrationRunnerContract.d.ts +9 -0
- package/dist/storage-migrations/types.d.ts +78 -0
- package/dist/storage-migrations.d.ts +18 -0
- package/dist/storage-migrations.js +473 -0
- package/dist/storage-migrations.js.map +20 -0
- package/dist/tabular-migrations/assertions/addAndDropIndex.d.ts +7 -0
- package/dist/tabular-migrations/assertions/addColumn.d.ts +7 -0
- package/dist/tabular-migrations/assertions/backfill.d.ts +7 -0
- package/dist/tabular-migrations/assertions/dropColumn.d.ts +7 -0
- package/dist/tabular-migrations/assertions/failedMigrationNotRecorded.d.ts +7 -0
- package/dist/tabular-migrations/assertions/freshDbFastPath.d.ts +7 -0
- package/dist/tabular-migrations/assertions/incrementalApplication.d.ts +7 -0
- package/dist/tabular-migrations/assertions/renameColumn.d.ts +7 -0
- package/dist/tabular-migrations/runTabularMigrationContract.d.ts +8 -0
- package/dist/tabular-migrations/types.d.ts +28 -0
- package/dist/tabular-migrations.d.ts +19 -0
- package/dist/tabular-migrations.js +257 -0
- package/dist/tabular-migrations.js.map +18 -0
- package/dist/tabular-storage/assertions/countMatchesQuery.d.ts +7 -0
- package/dist/tabular-storage/assertions/guardParity.d.ts +25 -0
- package/dist/tabular-storage/assertions/inListCriterion.d.ts +14 -0
- package/dist/tabular-storage/assertions/joinBoundedLeftRead.d.ts +29 -0
- package/dist/tabular-storage/assertions/joinPushdown.d.ts +13 -0
- package/dist/tabular-storage/assertions/notInListCriterion.d.ts +15 -0
- package/dist/tabular-storage/assertions/strategyParity.d.ts +31 -0
- package/dist/tabular-storage/assertions/subscribeToChanges.d.ts +21 -0
- package/dist/tabular-storage/assertions/vectorColumnFormat.d.ts +7 -0
- package/dist/tabular-storage/assertions/withConnectionTransaction.d.ts +7 -0
- package/dist/tabular-storage/assertions/withTransactionRollback.d.ts +7 -0
- package/dist/tabular-storage/fixtures.d.ts +33 -0
- package/dist/tabular-storage/joinFixtures.d.ts +66 -0
- package/dist/tabular-storage/runTabularJoinContract.d.ts +24 -0
- package/dist/tabular-storage/runTabularStorageContract.d.ts +9 -0
- package/dist/tabular-storage/types.d.ts +102 -0
- package/dist/tabular-storage.d.ts +25 -0
- package/dist/tabular-storage.js +1477 -0
- package/dist/tabular-storage.js.map +27 -0
- package/dist/vector-storage/assertions/dimensionValidation.d.ts +17 -0
- package/dist/vector-storage/assertions/legacyEncoding.d.ts +16 -0
- package/dist/vector-storage/assertions/metadataFilter.d.ts +16 -0
- package/dist/vector-storage/assertions/searchOptions.d.ts +16 -0
- package/dist/vector-storage/assertions/shared.d.ts +23 -0
- package/dist/vector-storage/assertions/similarityRanking.d.ts +18 -0
- package/dist/vector-storage/assertions/similaritySearchEvent.d.ts +16 -0
- package/dist/vector-storage/assertions/vectorRoundTrip.d.ts +17 -0
- package/dist/vector-storage/runVectorStorageContract.d.ts +20 -0
- package/dist/vector-storage/types.d.ts +87 -0
- package/dist/vector-storage.d.ts +19 -0
- package/dist/vector-storage.js +412 -0
- package/dist/vector-storage.js.map +21 -0
- package/dist/web-search/assertions/capabilityAgreement.d.ts +23 -0
- package/dist/web-search/assertions/credentialNaming.d.ts +21 -0
- package/dist/web-search/assertions/dateFiltering.d.ts +22 -0
- package/dist/web-search/assertions/domainNormalization.d.ts +24 -0
- package/dist/web-search/assertions/maxResultsCeiling.d.ts +22 -0
- package/dist/web-search/assertions/reportedQuery.d.ts +18 -0
- package/dist/web-search/assertions/signalThreading.d.ts +20 -0
- package/dist/web-search/fixtures.d.ts +40 -0
- package/dist/web-search/runWebSearchProviderConformance.d.ts +24 -0
- package/dist/web-search/types.d.ts +79 -0
- package/dist/web-search.d.ts +19 -0
- package/dist/web-search.js +560 -0
- package/dist/web-search.js.map +18 -0
- package/dist/worker-proxy/assertions/backlogOrdering.d.ts +7 -0
- package/dist/worker-proxy/assertions/disposeTerminatesWorker.d.ts +8 -0
- package/dist/worker-proxy/assertions/errorPropagation.d.ts +7 -0
- package/dist/worker-proxy/assertions/providerCallHelpers.d.ts +23 -0
- package/dist/worker-proxy/browserOnlyStub.d.ts +7 -0
- package/dist/worker-proxy/runWorkerProxyBoundary.d.ts +7 -0
- package/dist/worker-proxy/types.d.ts +47 -0
- package/dist/worker-proxy.d.ts +16 -0
- package/dist/worker-proxy.js +389 -0
- package/dist/worker-proxy.js.map +17 -0
- package/package.json +120 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
// src/worker-proxy/browserOnlyStub.ts
|
|
2
|
+
import { describe, it } from "vitest";
|
|
3
|
+
function browserOnlyStubBlock(opts) {
|
|
4
|
+
describe.skipIf(opts.skip)(`Worker-proxy boundary: ${opts.name}`, () => {
|
|
5
|
+
it.skip(`${opts.name}: requires browser test runner (browserOnly: true)`, () => {});
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
// src/worker-proxy/runWorkerProxyBoundary.ts
|
|
9
|
+
import { afterAll, beforeAll, describe as describe5 } from "vitest";
|
|
10
|
+
|
|
11
|
+
// src/worker-proxy/assertions/backlogOrdering.ts
|
|
12
|
+
import { describe as describe2, expect, it as it3 } from "vitest";
|
|
13
|
+
|
|
14
|
+
// src/creditExhaustedSkip.ts
|
|
15
|
+
import { it as vitestIt } from "vitest";
|
|
16
|
+
var BILLING_CODES = new Set([
|
|
17
|
+
"insufficient_quota",
|
|
18
|
+
"billing_hard_limit_reached",
|
|
19
|
+
"insufficient_credits",
|
|
20
|
+
"insufficient_balance"
|
|
21
|
+
]);
|
|
22
|
+
var CREDIT_MESSAGE = /insufficient credits|not enough credits|no credits remaining|out of credits|insufficient[\s_-]balance|balance is (?:too low|insufficient)|exceeded[\s\S]{0,80}credits|monthly included credits|payment required|exceeded your current quota|check your plan and billing|billing_hard_limit/i;
|
|
23
|
+
var HTTP_402_MESSAGE = /\bHTTP\/?\d?(?:\.\d)?\s*402\b|\bstatus(?:\s*code)?\s*[:=]\s*402\b|\berror code:?\s*402\b|(?:^|:\s+)402\s+[a-z]/im;
|
|
24
|
+
function isCreditExhaustedError(err) {
|
|
25
|
+
const seen = new Set;
|
|
26
|
+
let current = err;
|
|
27
|
+
while (current !== undefined && current !== null && !seen.has(current)) {
|
|
28
|
+
seen.add(current);
|
|
29
|
+
if (matchesCreditExhaustion(current))
|
|
30
|
+
return true;
|
|
31
|
+
current = causeOf(current);
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function matchesCreditExhaustion(err) {
|
|
36
|
+
if (typeof err === "string")
|
|
37
|
+
return messageLooksLikeCreditExhaustion(err);
|
|
38
|
+
if (!err || typeof err !== "object")
|
|
39
|
+
return false;
|
|
40
|
+
const rec = err;
|
|
41
|
+
const status = numericStatus(rec);
|
|
42
|
+
if (status === 402)
|
|
43
|
+
return true;
|
|
44
|
+
const code = stringish(rec.code) ?? stringish(nested(rec.error, "code"));
|
|
45
|
+
const type = stringish(rec.type) ?? stringish(nested(rec.error, "type"));
|
|
46
|
+
if (code && BILLING_CODES.has(code))
|
|
47
|
+
return true;
|
|
48
|
+
if (type && BILLING_CODES.has(type))
|
|
49
|
+
return true;
|
|
50
|
+
const message = [
|
|
51
|
+
stringish(rec.message),
|
|
52
|
+
stringish(nested(rec.error, "message")),
|
|
53
|
+
stringifyUnknown(err)
|
|
54
|
+
].filter((part) => typeof part === "string").join(`
|
|
55
|
+
`);
|
|
56
|
+
return messageLooksLikeCreditExhaustion(message);
|
|
57
|
+
}
|
|
58
|
+
function messageLooksLikeCreditExhaustion(message) {
|
|
59
|
+
return CREDIT_MESSAGE.test(message) || HTTP_402_MESSAGE.test(message);
|
|
60
|
+
}
|
|
61
|
+
function numericStatus(rec) {
|
|
62
|
+
for (const key of ["status", "statusCode"]) {
|
|
63
|
+
const value = rec[key];
|
|
64
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
const nestedStatus = nested(rec.error, "status");
|
|
68
|
+
if (typeof nestedStatus === "number" && Number.isFinite(nestedStatus))
|
|
69
|
+
return nestedStatus;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
function nested(value, key) {
|
|
73
|
+
if (!value || typeof value !== "object")
|
|
74
|
+
return;
|
|
75
|
+
return value[key];
|
|
76
|
+
}
|
|
77
|
+
function stringish(value) {
|
|
78
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
79
|
+
}
|
|
80
|
+
function stringifyUnknown(err) {
|
|
81
|
+
if (err instanceof Error)
|
|
82
|
+
return err.message;
|
|
83
|
+
return String(err);
|
|
84
|
+
}
|
|
85
|
+
function causeOf(err) {
|
|
86
|
+
if (!err || typeof err !== "object")
|
|
87
|
+
return;
|
|
88
|
+
return err.cause;
|
|
89
|
+
}
|
|
90
|
+
function creditSkipNote(err) {
|
|
91
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
92
|
+
return `skipped: provider out of credits (${message})`;
|
|
93
|
+
}
|
|
94
|
+
var SKIP_OVERRIDE_ENV = "WORKGLOW_CREDIT_EXHAUSTED_SKIP";
|
|
95
|
+
function isTruthyFlag(value) {
|
|
96
|
+
return !/^(?:0|false|off|no)$/i.test(value.trim());
|
|
97
|
+
}
|
|
98
|
+
function creditExhaustionSkipsTest(env = process.env) {
|
|
99
|
+
const override = env[SKIP_OVERRIDE_ENV];
|
|
100
|
+
if (override !== undefined && override.trim() !== "")
|
|
101
|
+
return isTruthyFlag(override);
|
|
102
|
+
for (const key of ["CI", "GITHUB_ACTIONS"]) {
|
|
103
|
+
const value = env[key];
|
|
104
|
+
if (value !== undefined && value.trim() !== "" && isTruthyFlag(value))
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
function reportLocalCreditFailure(err) {
|
|
110
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
111
|
+
console.error(`provider out of credits (${message})
|
|
112
|
+
` + ` Failing because this is not CI. Top the account up, or set ${SKIP_OVERRIDE_ENV}=1 to skip instead.`);
|
|
113
|
+
}
|
|
114
|
+
async function runWithCreditSkip(ctx, fn) {
|
|
115
|
+
try {
|
|
116
|
+
return await fn();
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if (isCreditExhaustedError(err)) {
|
|
119
|
+
if (!creditExhaustionSkipsTest()) {
|
|
120
|
+
reportLocalCreditFailure(err);
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
ctx.skip(creditSkipNote(err));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function wrapTestBodyForCreditSkip(fn) {
|
|
130
|
+
return async function wrapped(...args) {
|
|
131
|
+
try {
|
|
132
|
+
return await fn.apply(this, args);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (!isCreditExhaustedError(err))
|
|
135
|
+
throw err;
|
|
136
|
+
if (!creditExhaustionSkipsTest()) {
|
|
137
|
+
reportLocalCreditFailure(err);
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
const ctx = args[0];
|
|
141
|
+
if (ctx && typeof ctx.skip === "function") {
|
|
142
|
+
ctx.skip(creditSkipNote(err));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
console.warn(creditSkipNote(err));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function wrapCallable(fn) {
|
|
151
|
+
return new Proxy(fn, {
|
|
152
|
+
apply(target, thisArg, argArray) {
|
|
153
|
+
const wrapped = argArray.map((arg) => typeof arg === "function" ? wrapTestBodyForCreditSkip(arg) : arg);
|
|
154
|
+
const result = Reflect.apply(target, thisArg, wrapped);
|
|
155
|
+
return typeof result === "function" ? wrapCallable(result) : result;
|
|
156
|
+
},
|
|
157
|
+
get(target, prop, receiver) {
|
|
158
|
+
const value = Reflect.get(target, prop, receiver);
|
|
159
|
+
if (typeof value === "function") {
|
|
160
|
+
return wrapCallable(value.bind(target));
|
|
161
|
+
}
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
var it2 = wrapCallable(vitestIt);
|
|
167
|
+
|
|
168
|
+
// src/itExpectFail.ts
|
|
169
|
+
var itExpectFail = (name, fn, timeout) => {
|
|
170
|
+
const native = it2.fails;
|
|
171
|
+
if (typeof native === "function") {
|
|
172
|
+
native(name, fn, timeout);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
it2(`${name} [expected-fail]`, async () => {
|
|
176
|
+
let passed = false;
|
|
177
|
+
try {
|
|
178
|
+
await fn();
|
|
179
|
+
passed = true;
|
|
180
|
+
} catch (err) {
|
|
181
|
+
if (isCreditExhaustedError(err))
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
if (passed) {
|
|
185
|
+
throw new Error(`Test "${name}" was marked as expected-fail but passed. Remove its name from opts.expectedFailures.`);
|
|
186
|
+
}
|
|
187
|
+
}, timeout);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/worker-proxy/assertions/providerCallHelpers.ts
|
|
191
|
+
import {
|
|
192
|
+
collectStream,
|
|
193
|
+
createEmitQueue,
|
|
194
|
+
getAiProviderRegistry,
|
|
195
|
+
getGlobalModelRepository,
|
|
196
|
+
textGeneration
|
|
197
|
+
} from "@workglow/ai";
|
|
198
|
+
var TEXT_GENERATION = ["text.generation"];
|
|
199
|
+
async function runProviderTextGeneration(modelId, prompt, callOpts) {
|
|
200
|
+
const result = await textGeneration({
|
|
201
|
+
model: modelId,
|
|
202
|
+
prompt,
|
|
203
|
+
maxTokens: callOpts.maxTokens
|
|
204
|
+
}, { timeout: callOpts.timeoutMs });
|
|
205
|
+
return { text: result.text ?? "" };
|
|
206
|
+
}
|
|
207
|
+
async function* streamProviderTextGeneration(modelId, prompt, callOpts) {
|
|
208
|
+
const model = await getGlobalModelRepository().findByName(modelId);
|
|
209
|
+
if (!model)
|
|
210
|
+
throw new Error(`Model not registered: ${modelId}`);
|
|
211
|
+
const registry = getAiProviderRegistry();
|
|
212
|
+
const streamFn = registry.getRunFnFor(model.provider, TEXT_GENERATION);
|
|
213
|
+
if (!streamFn) {
|
|
214
|
+
throw new Error(`No run-fn registered for ${model.provider} serving ["text.generation"]`);
|
|
215
|
+
}
|
|
216
|
+
const ac = new AbortController;
|
|
217
|
+
const onCallerAbort = () => ac.abort(callOpts.signal?.reason);
|
|
218
|
+
const t = setTimeout(() => ac.abort(new Error("timeout")), callOpts.timeoutMs);
|
|
219
|
+
if (callOpts.signal) {
|
|
220
|
+
if (callOpts.signal.aborted) {
|
|
221
|
+
clearTimeout(t);
|
|
222
|
+
ac.abort(callOpts.signal.reason);
|
|
223
|
+
} else {
|
|
224
|
+
callOpts.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const q = createEmitQueue();
|
|
228
|
+
const runP = streamFn({ prompt, maxTokens: callOpts.maxTokens }, model, ac.signal, (e) => q.push(e), undefined, undefined).then(() => q.close(), (err) => q.fail(err));
|
|
229
|
+
try {
|
|
230
|
+
for await (const event of q.iterable) {
|
|
231
|
+
yield event;
|
|
232
|
+
}
|
|
233
|
+
} finally {
|
|
234
|
+
clearTimeout(t);
|
|
235
|
+
callOpts.signal?.removeEventListener("abort", onCallerAbort);
|
|
236
|
+
await runP.catch(() => {});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/worker-proxy/assertions/backlogOrdering.ts
|
|
241
|
+
var FAIL_KEY = "boundary.backlogOrdering";
|
|
242
|
+
async function collectStream2(modelId, prompt, timeoutMs) {
|
|
243
|
+
const events = [];
|
|
244
|
+
let finishCount = 0;
|
|
245
|
+
for await (const ev of streamProviderTextGeneration(modelId, prompt, {
|
|
246
|
+
maxTokens: 32,
|
|
247
|
+
timeoutMs
|
|
248
|
+
})) {
|
|
249
|
+
const e = ev;
|
|
250
|
+
events.push({ type: e.type });
|
|
251
|
+
if (e.type === "finish")
|
|
252
|
+
finishCount += 1;
|
|
253
|
+
}
|
|
254
|
+
return { events, finishCount };
|
|
255
|
+
}
|
|
256
|
+
function backlogOrderingBlock(opts) {
|
|
257
|
+
const failing = opts.expectedFailures?.includes(FAIL_KEY) ?? false;
|
|
258
|
+
const test = failing ? itExpectFail : it3;
|
|
259
|
+
describe2("PostMessage handles concurrent streams independently", () => {
|
|
260
|
+
test("three concurrent streams each deliver deltas before exactly one finish event", async () => {
|
|
261
|
+
const modelId = opts.models.textGeneration;
|
|
262
|
+
if (!modelId) {
|
|
263
|
+
throw new Error(`${opts.name}: models.textGeneration is required for boundary tests`);
|
|
264
|
+
}
|
|
265
|
+
const prompts = [
|
|
266
|
+
"Reply with the single word ALPHA.",
|
|
267
|
+
"Reply with the single word BETA.",
|
|
268
|
+
"Reply with the single word GAMMA."
|
|
269
|
+
];
|
|
270
|
+
const runs = await Promise.all(prompts.map((p) => collectStream2(modelId, p, opts.timeout / 2)));
|
|
271
|
+
for (const run of runs) {
|
|
272
|
+
expect(run.finishCount).toBe(1);
|
|
273
|
+
expect(run.events[run.events.length - 1]?.type).toBe("finish");
|
|
274
|
+
const deltaTypes = run.events.slice(0, -1).map((e) => e.type).filter((t) => t.endsWith("-delta"));
|
|
275
|
+
expect(deltaTypes.length).toBeGreaterThan(0);
|
|
276
|
+
}
|
|
277
|
+
}, opts.timeout);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// src/worker-proxy/assertions/disposeTerminatesWorker.ts
|
|
282
|
+
import { describe as describe3, expect as expect2, it as it4 } from "vitest";
|
|
283
|
+
var FAIL_KEY2 = "boundary.disposeTerminatesWorker";
|
|
284
|
+
function disposeTerminatesWorkerBlock(opts, getHandle, markDisposed) {
|
|
285
|
+
const failing = opts.expectedFailures?.includes(FAIL_KEY2) ?? false;
|
|
286
|
+
const test = failing ? itExpectFail : it4;
|
|
287
|
+
describe3("Dispose terminates worker", () => {
|
|
288
|
+
test("after dispose() the worker no longer responds to traffic", async () => {
|
|
289
|
+
const handle = getHandle();
|
|
290
|
+
const modelId = opts.models.textGeneration;
|
|
291
|
+
if (!modelId) {
|
|
292
|
+
throw new Error(`${opts.name}: models.textGeneration is required for boundary tests`);
|
|
293
|
+
}
|
|
294
|
+
const before = await runProviderTextGeneration(modelId, "Reply with the single word READY.", { maxTokens: 8, timeoutMs: opts.timeout / 4 });
|
|
295
|
+
expect2(before.text.length).toBeGreaterThan(0);
|
|
296
|
+
await handle.dispose();
|
|
297
|
+
markDisposed();
|
|
298
|
+
const after = runProviderTextGeneration(modelId, "Reply with the single word LATE.", {
|
|
299
|
+
maxTokens: 8,
|
|
300
|
+
timeoutMs: opts.timeout / 4
|
|
301
|
+
});
|
|
302
|
+
await expect2(after).rejects.toThrow(/.+/);
|
|
303
|
+
}, opts.timeout);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/worker-proxy/assertions/errorPropagation.ts
|
|
308
|
+
import { describe as describe4, expect as expect3, it as it5 } from "vitest";
|
|
309
|
+
var FAIL_KEY3 = "boundary.errorPropagation";
|
|
310
|
+
function errorPropagationBlock(opts) {
|
|
311
|
+
const failing = opts.expectedFailures?.includes(FAIL_KEY3) ?? false;
|
|
312
|
+
const test = failing ? itExpectFail : it5;
|
|
313
|
+
describe4("Worker-side throw surfaces on main thread", () => {
|
|
314
|
+
test("provider rejection from inside the worker surfaces as a thrown error", async () => {
|
|
315
|
+
const modelId = opts.models.textGeneration;
|
|
316
|
+
if (!modelId) {
|
|
317
|
+
throw new Error(`${opts.name}: models.textGeneration is required for boundary tests`);
|
|
318
|
+
}
|
|
319
|
+
await expect3(runProviderTextGeneration(modelId, "ignored", {
|
|
320
|
+
maxTokens: 1e9,
|
|
321
|
+
timeoutMs: opts.timeout / 4
|
|
322
|
+
})).rejects.toThrow(/.+/);
|
|
323
|
+
}, opts.timeout);
|
|
324
|
+
if (opts.capabilities.errorPropagation) {
|
|
325
|
+
test("worker-thrown Error preserves name and non-empty message across postMessage", async () => {
|
|
326
|
+
const modelId = opts.models.textGeneration;
|
|
327
|
+
if (!modelId) {
|
|
328
|
+
throw new Error(`${opts.name}: models.textGeneration is required for boundary tests`);
|
|
329
|
+
}
|
|
330
|
+
let captured;
|
|
331
|
+
try {
|
|
332
|
+
await runProviderTextGeneration(modelId, "ignored", {
|
|
333
|
+
maxTokens: 1e9,
|
|
334
|
+
timeoutMs: opts.timeout / 4
|
|
335
|
+
});
|
|
336
|
+
} catch (err) {
|
|
337
|
+
captured = err;
|
|
338
|
+
}
|
|
339
|
+
expect3(captured).toBeInstanceOf(Error);
|
|
340
|
+
const e = captured;
|
|
341
|
+
expect3(e.message.length).toBeGreaterThan(0);
|
|
342
|
+
expect3(e.name.length).toBeGreaterThan(0);
|
|
343
|
+
}, opts.timeout);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/worker-proxy/runWorkerProxyBoundary.ts
|
|
349
|
+
function runWorkerProxyBoundary(opts) {
|
|
350
|
+
if (opts.capabilities.browserOnly) {
|
|
351
|
+
browserOnlyStubBlock(opts);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
describe5.skipIf(opts.skip)(`Worker-proxy boundary: ${opts.name}`, () => {
|
|
355
|
+
let handle;
|
|
356
|
+
let disposed = false;
|
|
357
|
+
const getHandle = () => {
|
|
358
|
+
if (!handle)
|
|
359
|
+
throw new Error("worker-proxy handle not initialized");
|
|
360
|
+
return handle;
|
|
361
|
+
};
|
|
362
|
+
const markDisposed = () => {
|
|
363
|
+
disposed = true;
|
|
364
|
+
};
|
|
365
|
+
beforeAll(async () => {
|
|
366
|
+
handle = await opts.factory();
|
|
367
|
+
await handle.register();
|
|
368
|
+
}, opts.timeout);
|
|
369
|
+
afterAll(async () => {
|
|
370
|
+
if (handle && !disposed)
|
|
371
|
+
await handle.dispose();
|
|
372
|
+
});
|
|
373
|
+
errorPropagationBlock(opts);
|
|
374
|
+
backlogOrderingBlock(opts);
|
|
375
|
+
disposeTerminatesWorkerBlock(opts, getHandle, markDisposed);
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
export {
|
|
379
|
+
backlogOrderingBlock,
|
|
380
|
+
browserOnlyStubBlock,
|
|
381
|
+
collectStream,
|
|
382
|
+
disposeTerminatesWorkerBlock,
|
|
383
|
+
errorPropagationBlock,
|
|
384
|
+
runProviderTextGeneration,
|
|
385
|
+
runWorkerProxyBoundary,
|
|
386
|
+
streamProviderTextGeneration
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
//# debugId=292FD920B04AD20B64756E2164756E21
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/worker-proxy/browserOnlyStub.ts", "../src/worker-proxy/runWorkerProxyBoundary.ts", "../src/worker-proxy/assertions/backlogOrdering.ts", "../src/creditExhaustedSkip.ts", "../src/itExpectFail.ts", "../src/worker-proxy/assertions/providerCallHelpers.ts", "../src/worker-proxy/assertions/disposeTerminatesWorker.ts", "../src/worker-proxy/assertions/errorPropagation.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, it } from \"vitest\";\n\nimport type { WorkerProxyBoundaryOpts } from \"./types\";\n\nexport function browserOnlyStubBlock(opts: WorkerProxyBoundaryOpts): void {\n describe.skipIf(opts.skip)(`Worker-proxy boundary: ${opts.name}`, () => {\n it.skip(`${opts.name}: requires browser test runner (browserOnly: true)`, () => {});\n });\n}\n",
|
|
6
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { afterAll, beforeAll, describe } from \"vitest\";\n\nimport type { ConformanceHandle } from \"../ai-provider/types\";\nimport { backlogOrderingBlock } from \"./assertions/backlogOrdering\";\nimport { disposeTerminatesWorkerBlock } from \"./assertions/disposeTerminatesWorker\";\nimport { errorPropagationBlock } from \"./assertions/errorPropagation\";\nimport { browserOnlyStubBlock } from \"./browserOnlyStub\";\nimport type { WorkerProxyBoundaryOpts } from \"./types\";\n\nexport function runWorkerProxyBoundary(opts: WorkerProxyBoundaryOpts): void {\n if (opts.capabilities.browserOnly) {\n browserOnlyStubBlock(opts);\n return;\n }\n\n describe.skipIf(opts.skip)(`Worker-proxy boundary: ${opts.name}`, () => {\n let handle: ConformanceHandle | undefined;\n let disposed = false;\n const getHandle = (): ConformanceHandle => {\n if (!handle) throw new Error(\"worker-proxy handle not initialized\");\n return handle;\n };\n const markDisposed = (): void => {\n disposed = true;\n };\n\n beforeAll(async () => {\n handle = await opts.factory();\n await handle.register();\n }, opts.timeout);\n\n afterAll(async () => {\n if (handle && !disposed) await handle.dispose();\n });\n\n // Order matters: dispose runs last so the live-worker assertions above\n // execute against an undisposed handle. `markDisposed` flips the shared\n // flag so afterAll skips a second dispose.\n errorPropagationBlock(opts);\n backlogOrderingBlock(opts);\n disposeTerminatesWorkerBlock(opts, getHandle, markDisposed);\n });\n}\n",
|
|
7
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, expect, it } from \"vitest\";\n\nimport { itExpectFail } from \"../../itExpectFail\";\nimport type { WorkerProxyBoundaryOpts } from \"../types\";\nimport { streamProviderTextGeneration } from \"./providerCallHelpers\";\n\nconst FAIL_KEY = \"boundary.backlogOrdering\";\n\ninterface CollectedRun {\n readonly events: ReadonlyArray<{ readonly type: string }>;\n readonly finishCount: number;\n}\n\nasync function collectStream(\n modelId: string,\n prompt: string,\n timeoutMs: number\n): Promise<CollectedRun> {\n const events: Array<{ readonly type: string }> = [];\n let finishCount = 0;\n for await (const ev of streamProviderTextGeneration(modelId, prompt, {\n maxTokens: 32,\n timeoutMs,\n })) {\n const e = ev as { type: string };\n events.push({ type: e.type });\n if (e.type === \"finish\") finishCount += 1;\n }\n return { events, finishCount };\n}\n\nexport function backlogOrderingBlock(opts: WorkerProxyBoundaryOpts): void {\n const failing = opts.expectedFailures?.includes(FAIL_KEY) ?? false;\n const test = failing ? itExpectFail : it;\n\n describe(\"PostMessage handles concurrent streams independently\", () => {\n test(\n \"three concurrent streams each deliver deltas before exactly one finish event\",\n async () => {\n const modelId = opts.models.textGeneration;\n if (!modelId) {\n throw new Error(`${opts.name}: models.textGeneration is required for boundary tests`);\n }\n const prompts = [\n \"Reply with the single word ALPHA.\",\n \"Reply with the single word BETA.\",\n \"Reply with the single word GAMMA.\",\n ];\n const runs = await Promise.all(\n prompts.map((p) => collectStream(modelId, p, opts.timeout / 2))\n );\n\n for (const run of runs) {\n expect(run.finishCount).toBe(1);\n expect(run.events[run.events.length - 1]?.type).toBe(\"finish\");\n const deltaTypes = run.events\n .slice(0, -1)\n .map((e) => e.type)\n .filter((t) => t.endsWith(\"-delta\"));\n expect(deltaTypes.length).toBeGreaterThan(0);\n }\n },\n opts.timeout\n );\n });\n}\n",
|
|
8
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { it as vitestIt } from \"vitest\";\n\n/**\n * Live provider tests treat \"the account is out of credits\" as a billing\n * condition rather than a product bug. A key being present is not the same as\n * the account being able to pay; HuggingFace / OpenRouter 402s, Anthropic's\n * credit-balance error, DeepSeek's `Insufficient Balance`, and OpenAI's\n * `insufficient_quota` are all billing.\n *\n * Rate limits (429 + `rate_limit_exceeded`) are NOT this: those are transient\n * and the existing retry policy handles them.\n *\n * Where it skips and where it fails is a deliberate split\n * ({@link creditExhaustionSkipsTest}): on CI it skips for every provider,\n * because a red build tells a reviewer nothing they can act on; locally it\n * fails, because the developer running the suite is the one who can top the\n * account up. `WORKGLOW_CREDIT_EXHAUSTED_SKIP=1` forces the skip locally\n * (and `=0` forces the failure on CI).\n */\n\nconst BILLING_CODES = new Set([\n \"insufficient_quota\",\n \"billing_hard_limit_reached\",\n \"insufficient_credits\",\n \"insufficient_balance\",\n]);\n\nconst CREDIT_MESSAGE =\n /insufficient credits|not enough credits|no credits remaining|out of credits|insufficient[\\s_-]balance|balance is (?:too low|insufficient)|exceeded[\\s\\S]{0,80}credits|monthly included credits|payment required|exceeded your current quota|check your plan and billing|billing_hard_limit/i;\n\n/**\n * A 402 that survives only as text. `classifyProviderError` rebuilds the error\n * as a `PermanentJobError` carrying the message and no `cause`, so by the time\n * a live test sees a DeepSeek billing failure the numeric `status` is gone and\n * the OpenAI-SDK message shape (`402 Insufficient Balance`) is all that is\n * left. Anchored to an HTTP-shaped position — start of a line, or after a\n * summary line's colon — so prose that merely contains the number\n * (\"Item 402(c)\") is not scavenged as a status.\n */\nconst HTTP_402_MESSAGE =\n /\\bHTTP\\/?\\d?(?:\\.\\d)?\\s*402\\b|\\bstatus(?:\\s*code)?\\s*[:=]\\s*402\\b|\\berror code:?\\s*402\\b|(?:^|:\\s+)402\\s+[a-z]/im;\n\nexport function isCreditExhaustedError(err: unknown): boolean {\n const seen = new Set<unknown>();\n let current: unknown = err;\n while (current !== undefined && current !== null && !seen.has(current)) {\n seen.add(current);\n if (matchesCreditExhaustion(current)) return true;\n current = causeOf(current);\n }\n return false;\n}\n\nfunction matchesCreditExhaustion(err: unknown): boolean {\n if (typeof err === \"string\") return messageLooksLikeCreditExhaustion(err);\n\n if (!err || typeof err !== \"object\") return false;\n\n const rec = err as Record<string, unknown>;\n const status = numericStatus(rec);\n if (status === 402) return true;\n\n const code = stringish(rec.code) ?? stringish(nested(rec.error, \"code\"));\n const type = stringish(rec.type) ?? stringish(nested(rec.error, \"type\"));\n if (code && BILLING_CODES.has(code)) return true;\n if (type && BILLING_CODES.has(type)) return true;\n\n const message = [\n stringish(rec.message),\n stringish(nested(rec.error, \"message\")),\n stringifyUnknown(err),\n ]\n .filter((part): part is string => typeof part === \"string\")\n .join(\"\\n\");\n return messageLooksLikeCreditExhaustion(message);\n}\n\nfunction messageLooksLikeCreditExhaustion(message: string): boolean {\n return CREDIT_MESSAGE.test(message) || HTTP_402_MESSAGE.test(message);\n}\n\nfunction numericStatus(rec: Record<string, unknown>): number | undefined {\n for (const key of [\"status\", \"statusCode\"] as const) {\n const value = rec[key];\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n }\n const nestedStatus = nested(rec.error, \"status\");\n if (typeof nestedStatus === \"number\" && Number.isFinite(nestedStatus)) return nestedStatus;\n return undefined;\n}\n\nfunction nested(value: unknown, key: string): unknown {\n if (!value || typeof value !== \"object\") return undefined;\n return (value as Record<string, unknown>)[key];\n}\n\nfunction stringish(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction stringifyUnknown(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\nfunction causeOf(err: unknown): unknown {\n if (!err || typeof err !== \"object\") return undefined;\n return (err as { cause?: unknown }).cause;\n}\n\nexport function creditSkipNote(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err);\n return `skipped: provider out of credits (${message})`;\n}\n\nconst SKIP_OVERRIDE_ENV = \"WORKGLOW_CREDIT_EXHAUSTED_SKIP\";\n\nfunction isTruthyFlag(value: string): boolean {\n return !/^(?:0|false|off|no)$/i.test(value.trim());\n}\n\n/**\n * Whether a billing failure skips the test or fails it.\n *\n * CI skips: nobody watching a build can top an account up, and one exhausted\n * key would otherwise turn every provider suite red for a change that touched\n * none of them — so the leniency is provider-agnostic by construction, keyed on\n * the environment rather than on which vendor ran out.\n *\n * Locally it fails, loudly, because the developer running the suite IS the\n * person who can act on it — and a silently green local run is how an\n * exhausted account goes unnoticed until CI is the only thing exercising the\n * provider at all.\n *\n * `WORKGLOW_CREDIT_EXHAUSTED_SKIP` overrides in both directions.\n */\nexport function creditExhaustionSkipsTest(\n env: Record<string, string | undefined> = process.env\n): boolean {\n const override = env[SKIP_OVERRIDE_ENV];\n if (override !== undefined && override.trim() !== \"\") return isTruthyFlag(override);\n for (const key of [\"CI\", \"GITHUB_ACTIONS\"] as const) {\n const value = env[key];\n if (value !== undefined && value.trim() !== \"\" && isTruthyFlag(value)) return true;\n }\n return false;\n}\n\n/**\n * The local-failure path. Rethrows the provider's own error unchanged — its\n * identity and stack are what makes the failure diagnosable — after one line\n * saying why it was not skipped and how to skip it.\n */\nfunction reportLocalCreditFailure(err: unknown): void {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\n `provider out of credits (${message})\\n` +\n ` Failing because this is not CI. Top the account up, or set ${SKIP_OVERRIDE_ENV}=1 to skip instead.`\n );\n}\n\nexport async function runWithCreditSkip<T>(\n ctx: { skip: (note?: string) => void },\n fn: () => Promise<T> | T\n): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (isCreditExhaustedError(err)) {\n if (!creditExhaustionSkipsTest()) {\n reportLocalCreditFailure(err);\n throw err;\n }\n ctx.skip(creditSkipNote(err));\n // vitest's skip() is `never` (throws). Mocks used in unit tests return.\n return undefined as T;\n }\n throw err;\n }\n}\n\n/**\n * Wraps one test body. This is the seam every conformance assertion actually\n * runs through (the exported {@link it} proxies each callback through it), so\n * the CI-skips / locally-fails policy is applied here rather than at each\n * call site.\n */\nexport function wrapTestBodyForCreditSkip(\n fn: (...args: never[]) => unknown\n): (...args: never[]) => unknown {\n return async function wrapped(this: unknown, ...args: never[]): Promise<unknown> {\n try {\n return await fn.apply(this, args);\n } catch (err) {\n if (!isCreditExhaustedError(err)) throw err;\n if (!creditExhaustionSkipsTest()) {\n reportLocalCreditFailure(err);\n throw err;\n }\n const ctx = args[0] as { skip?: (note?: string) => void } | undefined;\n if (ctx && typeof ctx.skip === \"function\") {\n ctx.skip(creditSkipNote(err));\n return undefined;\n }\n // bun:test has no TestContext.skip; passing is \"don't fail the test\".\n console.warn(creditSkipNote(err));\n return undefined;\n }\n };\n}\n\nfunction wrapCallable(fn: CallableFunction): CallableFunction {\n return new Proxy(fn, {\n apply(target, thisArg, argArray: unknown[]) {\n const wrapped = argArray.map((arg) =>\n typeof arg === \"function\" ? wrapTestBodyForCreditSkip(arg as never) : arg\n );\n const result = Reflect.apply(target, thisArg, wrapped);\n return typeof result === \"function\" ? wrapCallable(result) : result;\n },\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") {\n return wrapCallable((value as CallableFunction).bind(target));\n }\n return value;\n },\n });\n}\n\n/**\n * Drop-in `it` for live provider tests: a credit/billing error skips the test on\n * CI and fails it locally (see {@link creditExhaustionSkipsTest}).\n */\nexport const it = wrapCallable(vitestIt) as typeof vitestIt;\n",
|
|
9
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { isCreditExhaustedError, it } from \"./creditExhaustedSkip\";\n\ntype ItFn = (name: string, fn: () => Promise<void> | void, timeout?: number) => void;\n\n/**\n * Cross-runner polyfill for `it.fails`. Vitest exposes it natively; bun's\n * native `bun test` runner does not. Under bun we wrap the body in a\n * try/catch and assert that it threw — so a passing-when-expected-fail\n * test still surfaces as a CI failure (signalling \"remove from\n * expectedFailures\").\n */\nexport const itExpectFail: ItFn = (name, fn, timeout) => {\n const native = (it as unknown as { fails?: ItFn }).fails;\n if (typeof native === \"function\") {\n native(name, fn, timeout);\n return;\n }\n it(\n `${name} [expected-fail]`,\n async () => {\n let passed = false;\n try {\n await fn();\n passed = true;\n } catch (err) {\n // Billing exhaustion is not the adapter bug this marker records.\n if (isCreditExhaustedError(err)) throw err;\n // expected; the test was supposed to fail\n }\n if (passed) {\n throw new Error(\n `Test \"${name}\" was marked as expected-fail but passed. Remove its name from opts.expectedFailures.`\n );\n }\n },\n timeout\n );\n};\n",
|
|
10
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { Capability } from \"@workglow/ai\";\nimport {\n collectStream,\n createEmitQueue,\n getAiProviderRegistry,\n getGlobalModelRepository,\n textGeneration,\n} from \"@workglow/ai\";\nimport type { StreamEvent, TaskOutput } from \"@workglow/task-graph\";\n\nconst TEXT_GENERATION: readonly Capability[] = [\"text.generation\"];\n\nexport interface CallOpts {\n readonly maxTokens: number;\n readonly timeoutMs: number;\n readonly signal?: AbortSignal;\n}\n\nexport interface CallResult {\n readonly text: string;\n}\n\nexport async function runProviderTextGeneration(\n modelId: string,\n prompt: string,\n callOpts: CallOpts\n): Promise<CallResult> {\n const result = await textGeneration(\n {\n model: modelId,\n prompt,\n maxTokens: callOpts.maxTokens,\n },\n { timeout: callOpts.timeoutMs }\n );\n return { text: (result as { text?: string }).text ?? \"\" };\n}\n\n/**\n * Helper used by worker-proxy conformance tests to drive a streaming\n * text-generation run-fn end-to-end against the dispatched provider. Yields\n * the raw `StreamEvent` objects so callers can assert on the event sequence;\n * use {@link collectStream} when only the final output matters.\n */\nexport async function* streamProviderTextGeneration(\n modelId: string,\n prompt: string,\n callOpts: CallOpts\n): AsyncGenerator<unknown, void, void> {\n const model = await getGlobalModelRepository().findByName(modelId);\n if (!model) throw new Error(`Model not registered: ${modelId}`);\n const registry = getAiProviderRegistry();\n const streamFn = registry.getRunFnFor(model.provider, TEXT_GENERATION);\n if (!streamFn) {\n throw new Error(`No run-fn registered for ${model.provider} serving [\"text.generation\"]`);\n }\n const ac = new AbortController();\n const onCallerAbort = (): void => ac.abort(callOpts.signal?.reason);\n const t = setTimeout(() => ac.abort(new Error(\"timeout\")), callOpts.timeoutMs);\n if (callOpts.signal) {\n if (callOpts.signal.aborted) {\n clearTimeout(t);\n ac.abort(callOpts.signal.reason);\n } else {\n callOpts.signal.addEventListener(\"abort\", onCallerAbort, { once: true });\n }\n }\n // Bridge Promise+emit run-fn back to an async generator for callers that\n // want to inspect the raw event sequence.\n const q = createEmitQueue<StreamEvent<TaskOutput>>();\n const runP = streamFn(\n { prompt, maxTokens: callOpts.maxTokens },\n model,\n ac.signal,\n (e) => q.push(e as StreamEvent<TaskOutput>),\n undefined,\n undefined\n ).then(\n () => q.close(),\n (err) => q.fail(err)\n );\n try {\n for await (const event of q.iterable) {\n yield event;\n }\n } finally {\n clearTimeout(t);\n callOpts.signal?.removeEventListener(\"abort\", onCallerAbort);\n await runP.catch(() => {});\n }\n}\n\n// Re-export collectStream so worker-proxy tests can use it without a second import.\nexport { collectStream };\n",
|
|
11
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, expect, it } from \"vitest\";\n\nimport type { ConformanceHandle } from \"../../ai-provider/types\";\nimport { itExpectFail } from \"../../itExpectFail\";\nimport type { WorkerProxyBoundaryOpts } from \"../types\";\nimport { runProviderTextGeneration } from \"./providerCallHelpers\";\n\nconst FAIL_KEY = \"boundary.disposeTerminatesWorker\";\n\nexport function disposeTerminatesWorkerBlock(\n opts: WorkerProxyBoundaryOpts,\n getHandle: () => ConformanceHandle,\n markDisposed: () => void\n): void {\n const failing = opts.expectedFailures?.includes(FAIL_KEY) ?? false;\n const test = failing ? itExpectFail : it;\n\n describe(\"Dispose terminates worker\", () => {\n test(\n \"after dispose() the worker no longer responds to traffic\",\n async () => {\n const handle = getHandle();\n const modelId = opts.models.textGeneration;\n if (!modelId) {\n throw new Error(`${opts.name}: models.textGeneration is required for boundary tests`);\n }\n\n // Sanity request — must succeed before dispose.\n const before = await runProviderTextGeneration(\n modelId,\n \"Reply with the single word READY.\",\n { maxTokens: 8, timeoutMs: opts.timeout / 4 }\n );\n expect(before.text.length).toBeGreaterThan(0);\n\n await handle.dispose();\n markDisposed();\n\n const after = runProviderTextGeneration(modelId, \"Reply with the single word LATE.\", {\n maxTokens: 8,\n timeoutMs: opts.timeout / 4,\n });\n await expect(after).rejects.toThrow(/.+/);\n },\n opts.timeout\n );\n });\n}\n",
|
|
12
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, expect, it } from \"vitest\";\n\nimport { itExpectFail } from \"../../itExpectFail\";\nimport type { WorkerProxyBoundaryOpts } from \"../types\";\nimport { runProviderTextGeneration } from \"./providerCallHelpers\";\n\nconst FAIL_KEY = \"boundary.errorPropagation\";\n\nexport function errorPropagationBlock(opts: WorkerProxyBoundaryOpts): void {\n const failing = opts.expectedFailures?.includes(FAIL_KEY) ?? false;\n const test = failing ? itExpectFail : it;\n\n describe(\"Worker-side throw surfaces on main thread\", () => {\n test(\n \"provider rejection from inside the worker surfaces as a thrown error\",\n async () => {\n const modelId = opts.models.textGeneration;\n if (!modelId) {\n throw new Error(`${opts.name}: models.textGeneration is required for boundary tests`);\n }\n // Use a registered model so resolution succeeds and the call is\n // dispatched into the worker. The provider's run fn rejects when\n // asked for an absurd token budget — the rejection therefore\n // originates inside the worker and must cross the postMessage\n // boundary to surface here.\n await expect(\n runProviderTextGeneration(modelId, \"ignored\", {\n maxTokens: 1_000_000_000,\n timeoutMs: opts.timeout / 4,\n })\n ).rejects.toThrow(/.+/);\n },\n opts.timeout\n );\n\n if (opts.capabilities.errorPropagation) {\n test(\n \"worker-thrown Error preserves name and non-empty message across postMessage\",\n async () => {\n const modelId = opts.models.textGeneration;\n if (!modelId) {\n throw new Error(`${opts.name}: models.textGeneration is required for boundary tests`);\n }\n let captured: unknown;\n try {\n await runProviderTextGeneration(modelId, \"ignored\", {\n maxTokens: 1_000_000_000,\n timeoutMs: opts.timeout / 4,\n });\n } catch (err) {\n captured = err;\n }\n // WorkerManager reconstructs cross-boundary errors on the main\n // thread and only round-trips `message` + `name` (the worker's\n // stack is not preserved). Verifying these two fields are intact\n // is the strongest structural check the harness can make without\n // assuming internal symbol names appear in stack frames.\n expect(captured).toBeInstanceOf(Error);\n const e = captured as Error;\n expect(e.message.length).toBeGreaterThan(0);\n expect(e.name.length).toBeGreaterThan(0);\n },\n opts.timeout\n );\n }\n });\n}\n"
|
|
13
|
+
],
|
|
14
|
+
"mappings": "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",
|
|
15
|
+
"debugId": "292FD920B04AD20B64756E2164756E21",
|
|
16
|
+
"names": []
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@workglow/test-contract",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.6.1",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/workglow-dev/libs.git",
|
|
9
|
+
"directory": "packages/test-contract"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/workglow-dev/libs/issues"
|
|
13
|
+
},
|
|
14
|
+
"description": "Parameterized conformance suites for the Workglow interfaces, so an adapter outside this repo inherits the same behavioral assertions its in-repo siblings run.",
|
|
15
|
+
"homepage": "https://workglow.dev",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"watch": "concurrently -c 'auto' 'bun:watch-*'",
|
|
18
|
+
"watch-js": "bun build --watch --no-clear-screen --target=node --sourcemap=external --packages=external --root ./src --outdir ./dist ./src/ai-provider.ts ./src/browser-context.ts ./src/entitlement-profile.ts ./src/human-connector.ts ./src/storage-migrations.ts ./src/tabular-migrations.ts ./src/tabular-storage.ts ./src/vector-storage.ts ./src/web-search.ts ./src/worker-proxy.ts ./src/creditExhaustedSkip.ts ./src/itExpectFail.ts",
|
|
19
|
+
"watch-types": "tsc --watch --preserveWatchOutput",
|
|
20
|
+
"build-package": "bun run build-js && bun run build-types",
|
|
21
|
+
"build-js": "bun build --target=node --sourcemap=external --packages=external --root ./src --outdir ./dist ./src/ai-provider.ts ./src/browser-context.ts ./src/entitlement-profile.ts ./src/human-connector.ts ./src/storage-migrations.ts ./src/tabular-migrations.ts ./src/tabular-storage.ts ./src/vector-storage.ts ./src/web-search.ts ./src/worker-proxy.ts ./src/creditExhaustedSkip.ts ./src/itExpectFail.ts",
|
|
22
|
+
"build-clean": "rm -fr dist/* tsconfig.tsbuildinfo",
|
|
23
|
+
"build-types": "rm -f tsconfig.tsbuildinfo && tsc"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
"./ai-provider": {
|
|
27
|
+
"types": "./dist/ai-provider.d.ts",
|
|
28
|
+
"import": "./dist/ai-provider.js"
|
|
29
|
+
},
|
|
30
|
+
"./browser-context": {
|
|
31
|
+
"types": "./dist/browser-context.d.ts",
|
|
32
|
+
"import": "./dist/browser-context.js"
|
|
33
|
+
},
|
|
34
|
+
"./entitlement-profile": {
|
|
35
|
+
"types": "./dist/entitlement-profile.d.ts",
|
|
36
|
+
"import": "./dist/entitlement-profile.js"
|
|
37
|
+
},
|
|
38
|
+
"./human-connector": {
|
|
39
|
+
"types": "./dist/human-connector.d.ts",
|
|
40
|
+
"import": "./dist/human-connector.js"
|
|
41
|
+
},
|
|
42
|
+
"./storage-migrations": {
|
|
43
|
+
"types": "./dist/storage-migrations.d.ts",
|
|
44
|
+
"import": "./dist/storage-migrations.js"
|
|
45
|
+
},
|
|
46
|
+
"./tabular-migrations": {
|
|
47
|
+
"types": "./dist/tabular-migrations.d.ts",
|
|
48
|
+
"import": "./dist/tabular-migrations.js"
|
|
49
|
+
},
|
|
50
|
+
"./tabular-storage": {
|
|
51
|
+
"types": "./dist/tabular-storage.d.ts",
|
|
52
|
+
"import": "./dist/tabular-storage.js"
|
|
53
|
+
},
|
|
54
|
+
"./vector-storage": {
|
|
55
|
+
"types": "./dist/vector-storage.d.ts",
|
|
56
|
+
"import": "./dist/vector-storage.js"
|
|
57
|
+
},
|
|
58
|
+
"./worker-proxy": {
|
|
59
|
+
"types": "./dist/worker-proxy.d.ts",
|
|
60
|
+
"import": "./dist/worker-proxy.js"
|
|
61
|
+
},
|
|
62
|
+
"./credit-exhausted-skip": {
|
|
63
|
+
"types": "./dist/creditExhaustedSkip.d.ts",
|
|
64
|
+
"import": "./dist/creditExhaustedSkip.js"
|
|
65
|
+
},
|
|
66
|
+
"./expect-fail": {
|
|
67
|
+
"types": "./dist/itExpectFail.d.ts",
|
|
68
|
+
"import": "./dist/itExpectFail.js"
|
|
69
|
+
},
|
|
70
|
+
"./web-search": {
|
|
71
|
+
"types": "./dist/web-search.d.ts",
|
|
72
|
+
"import": "./dist/web-search.js"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"@workglow/ai": "0.6.1",
|
|
77
|
+
"@workglow/browser-control": "0.6.1",
|
|
78
|
+
"@workglow/storage": "0.6.1",
|
|
79
|
+
"@workglow/task-graph": "0.6.1",
|
|
80
|
+
"@workglow/util": "0.6.1",
|
|
81
|
+
"@workglow/web-search": "0.6.1",
|
|
82
|
+
"vitest": "^5.0.1"
|
|
83
|
+
},
|
|
84
|
+
"peerDependenciesMeta": {
|
|
85
|
+
"@workglow/ai": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
88
|
+
"@workglow/browser-control": {
|
|
89
|
+
"optional": true
|
|
90
|
+
},
|
|
91
|
+
"@workglow/storage": {
|
|
92
|
+
"optional": true
|
|
93
|
+
},
|
|
94
|
+
"@workglow/task-graph": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
97
|
+
"@workglow/util": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"@workglow/web-search": {
|
|
101
|
+
"optional": true
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"devDependencies": {
|
|
105
|
+
"@workglow/ai": "0.6.1",
|
|
106
|
+
"@workglow/browser-control": "0.6.1",
|
|
107
|
+
"@workglow/storage": "0.6.1",
|
|
108
|
+
"@workglow/task-graph": "0.6.1",
|
|
109
|
+
"@workglow/util": "0.6.1",
|
|
110
|
+
"@workglow/web-search": "0.6.1",
|
|
111
|
+
"vitest": "^5.0.1"
|
|
112
|
+
},
|
|
113
|
+
"files": [
|
|
114
|
+
"dist",
|
|
115
|
+
"README.md"
|
|
116
|
+
],
|
|
117
|
+
"publishConfig": {
|
|
118
|
+
"access": "public"
|
|
119
|
+
}
|
|
120
|
+
}
|