@electric-ax/agents-server-conformance-tests 0.1.7 → 0.1.8
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/dist/index.cjs +105 -87
- package/dist/index.d.cts +4 -6
- package/dist/index.d.ts +4 -6
- package/dist/index.js +105 -87
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -28,10 +28,30 @@ const node_http = __toESM(require("node:http"));
|
|
|
28
28
|
const __electric_sql_client = __toESM(require("@electric-sql/client"));
|
|
29
29
|
const node_child_process = __toESM(require("node:child_process"));
|
|
30
30
|
|
|
31
|
+
//#region src/url.ts
|
|
32
|
+
function appendPathToUrl(baseUrl, path) {
|
|
33
|
+
const base = new URL(baseUrl);
|
|
34
|
+
const pathUrl = new URL(path, `http://electric-agents.local`);
|
|
35
|
+
const basePath = base.pathname === `/` ? `` : base.pathname.replace(/\/+$/, ``);
|
|
36
|
+
const suffix = pathUrl.pathname.startsWith(`/`) ? pathUrl.pathname : `/${pathUrl.pathname}`;
|
|
37
|
+
const target = new URL(base);
|
|
38
|
+
target.pathname = `${basePath}${suffix}`;
|
|
39
|
+
target.search = ``;
|
|
40
|
+
target.hash = pathUrl.hash;
|
|
41
|
+
base.searchParams.forEach((value, key) => {
|
|
42
|
+
target.searchParams.append(key, value);
|
|
43
|
+
});
|
|
44
|
+
pathUrl.searchParams.forEach((value, key) => {
|
|
45
|
+
target.searchParams.append(key, value);
|
|
46
|
+
});
|
|
47
|
+
return target.toString();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
31
51
|
//#region src/electric-agents-dsl.ts
|
|
32
52
|
async function fetchShapeRows(baseUrl, table) {
|
|
33
53
|
const stream = new __electric_sql_client.ShapeStream({
|
|
34
|
-
url:
|
|
54
|
+
url: appendPathToUrl(baseUrl, `/_electric/electric/v1/shape`),
|
|
35
55
|
params: { table },
|
|
36
56
|
subscribe: false
|
|
37
57
|
});
|
|
@@ -54,10 +74,8 @@ function toServerEntityTypeRegistration(registration) {
|
|
|
54
74
|
...registration.metadata_schema && { metadata_schema: registration.metadata_schema },
|
|
55
75
|
...registration.serve_endpoint && { serve_endpoint: registration.serve_endpoint }
|
|
56
76
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (inboxSchemas) body.inbox_schemas = inboxSchemas;
|
|
60
|
-
if (stateSchemas) body.state_schemas = stateSchemas;
|
|
77
|
+
if (registration.inbox_schemas) body.inbox_schemas = registration.inbox_schemas;
|
|
78
|
+
if (registration.state_schemas) body.state_schemas = registration.state_schemas;
|
|
61
79
|
return body;
|
|
62
80
|
}
|
|
63
81
|
function normalizeWebhookPayload(body) {
|
|
@@ -198,7 +216,7 @@ var ServeEndpointReceiver = class {
|
|
|
198
216
|
}
|
|
199
217
|
};
|
|
200
218
|
async function electricAgentsFetch$1(baseUrl, path, opts = {}) {
|
|
201
|
-
return fetch(
|
|
219
|
+
return fetch(appendPathToUrl(baseUrl, routeControlPlanePath$1(path)), {
|
|
202
220
|
...opts,
|
|
203
221
|
headers: {
|
|
204
222
|
"content-type": `application/json`,
|
|
@@ -217,7 +235,7 @@ function isEntityStreamPath$1(pathname) {
|
|
|
217
235
|
return segments.length >= 3 && (lastSegment === `main` || lastSegment === `error`);
|
|
218
236
|
}
|
|
219
237
|
function subscriptionEndpoint$1(baseUrl, id) {
|
|
220
|
-
return
|
|
238
|
+
return appendPathToUrl(baseUrl, `/__ds/subscriptions/${encodeURIComponent(id)}`);
|
|
221
239
|
}
|
|
222
240
|
function subscriptionPattern$1(pattern) {
|
|
223
241
|
return pattern.replace(/^\/+/, ``);
|
|
@@ -377,8 +395,8 @@ var ElectricAgentsScenario = class {
|
|
|
377
395
|
this.steps.push({
|
|
378
396
|
kind: `amendSchemas`,
|
|
379
397
|
name,
|
|
380
|
-
|
|
381
|
-
|
|
398
|
+
inbox_schemas: schemas.inbox_schemas,
|
|
399
|
+
state_schemas: schemas.state_schemas
|
|
382
400
|
});
|
|
383
401
|
return this;
|
|
384
402
|
}
|
|
@@ -446,7 +464,7 @@ var ElectricAgentsScenario = class {
|
|
|
446
464
|
typeName,
|
|
447
465
|
instanceId,
|
|
448
466
|
args: opts?.args,
|
|
449
|
-
code: `
|
|
467
|
+
code: `SCHEMA_VALIDATION_FAILED`,
|
|
450
468
|
status: 422
|
|
451
469
|
});
|
|
452
470
|
return this;
|
|
@@ -456,7 +474,7 @@ var ElectricAgentsScenario = class {
|
|
|
456
474
|
kind: `expectSendSchemaError`,
|
|
457
475
|
payload,
|
|
458
476
|
messageType: opts?.type ?? `default`,
|
|
459
|
-
code: `
|
|
477
|
+
code: `SCHEMA_VALIDATION_FAILED`,
|
|
460
478
|
status: 422
|
|
461
479
|
});
|
|
462
480
|
return this;
|
|
@@ -466,7 +484,7 @@ var ElectricAgentsScenario = class {
|
|
|
466
484
|
kind: `expectWriteSchemaError`,
|
|
467
485
|
payload,
|
|
468
486
|
eventType: opts?.type ?? `default`,
|
|
469
|
-
code: `
|
|
487
|
+
code: `SCHEMA_VALIDATION_FAILED`,
|
|
470
488
|
status: 422
|
|
471
489
|
});
|
|
472
490
|
return this;
|
|
@@ -695,7 +713,7 @@ async function executeStep(ctx, step) {
|
|
|
695
713
|
case `readStream`: {
|
|
696
714
|
if (!ctx.currentEntityStreams) throw new Error(`No current entity streams`);
|
|
697
715
|
const streamPath = step.stream === `error` ? ctx.currentEntityStreams.error : ctx.currentEntityStreams.main;
|
|
698
|
-
const res = await fetch(
|
|
716
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, `${streamPath}?offset=0000000000000000_0000000000000000`));
|
|
699
717
|
if (res.status === 200) {
|
|
700
718
|
const text = await res.text();
|
|
701
719
|
const messages = text ? JSON.parse(text) : [];
|
|
@@ -836,8 +854,8 @@ async function executeStep(ctx, step) {
|
|
|
836
854
|
}
|
|
837
855
|
case `amendSchemas`: {
|
|
838
856
|
const body = {};
|
|
839
|
-
if (step.
|
|
840
|
-
if (step.
|
|
857
|
+
if (step.inbox_schemas) body.inbox_schemas = step.inbox_schemas;
|
|
858
|
+
if (step.state_schemas) body.state_schemas = step.state_schemas;
|
|
841
859
|
const res = await electricAgentsFetch$1(ctx.baseUrl, `/_electric/entity-types/${step.name}/schemas`, {
|
|
842
860
|
method: `PATCH`,
|
|
843
861
|
body: JSON.stringify(body)
|
|
@@ -1339,8 +1357,8 @@ function applyElectricAgentsAction(model, action, targetIdx, opts) {
|
|
|
1339
1357
|
entityTypes: [...model.entityTypes, {
|
|
1340
1358
|
name: opts?.typeName ?? `prop-type-${typeNum}`,
|
|
1341
1359
|
hasCreationSchema: false,
|
|
1342
|
-
|
|
1343
|
-
|
|
1360
|
+
hasInboxSchemas: false,
|
|
1361
|
+
hasStateSchemas: false
|
|
1344
1362
|
}]
|
|
1345
1363
|
};
|
|
1346
1364
|
}
|
|
@@ -1498,7 +1516,7 @@ function checkStateProtocolInvariants(events) {
|
|
|
1498
1516
|
//#endregion
|
|
1499
1517
|
//#region src/cli-dsl.ts
|
|
1500
1518
|
function subscriptionEndpoint(baseUrl, id) {
|
|
1501
|
-
return
|
|
1519
|
+
return appendPathToUrl(baseUrl, `/__ds/subscriptions/${encodeURIComponent(id)}`);
|
|
1502
1520
|
}
|
|
1503
1521
|
function subscriptionPattern(pattern) {
|
|
1504
1522
|
return pattern.replace(/^\/+/, ``);
|
|
@@ -1647,7 +1665,7 @@ var CliScenario = class {
|
|
|
1647
1665
|
try {
|
|
1648
1666
|
for (const step of this.steps) switch (step.kind) {
|
|
1649
1667
|
case `setupType`: {
|
|
1650
|
-
const res = await fetch(
|
|
1668
|
+
const res = await fetch(appendPathToUrl(this.baseUrl, `/_electric/entity-types`), {
|
|
1651
1669
|
method: `POST`,
|
|
1652
1670
|
headers: { "content-type": `application/json` },
|
|
1653
1671
|
body: JSON.stringify(step.registration)
|
|
@@ -1776,7 +1794,7 @@ function startNoopReceiver() {
|
|
|
1776
1794
|
//#endregion
|
|
1777
1795
|
//#region src/electric-agents-tests.ts
|
|
1778
1796
|
async function electricAgentsFetch(baseUrl, path, opts = {}) {
|
|
1779
|
-
return fetch(
|
|
1797
|
+
return fetch(appendPathToUrl(baseUrl, routeControlPlanePath(path)), {
|
|
1780
1798
|
...opts,
|
|
1781
1799
|
headers: {
|
|
1782
1800
|
"content-type": `application/json`,
|
|
@@ -1797,7 +1815,7 @@ function isEntityStreamPath(pathname) {
|
|
|
1797
1815
|
async function pollEntityStatus(baseUrl, entityUrl, statuses, timeoutMs = 8e3) {
|
|
1798
1816
|
const deadline = Date.now() + timeoutMs;
|
|
1799
1817
|
while (Date.now() < deadline) {
|
|
1800
|
-
const res = await fetch(
|
|
1818
|
+
const res = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(entityUrl)));
|
|
1801
1819
|
(0, vitest.expect)(res.status).toBe(200);
|
|
1802
1820
|
const entity = await res.json();
|
|
1803
1821
|
if (statuses.includes(String(entity.status))) return entity;
|
|
@@ -1812,13 +1830,13 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
1812
1830
|
description: `Test entity type for spawn`,
|
|
1813
1831
|
creation_schema: { type: `object` }
|
|
1814
1832
|
}).spawn(`spawn-test-agent`, `entity-1`).expectStatus(`running`).custom(async (ctx) => {
|
|
1815
|
-
const mainRes = await fetch(
|
|
1833
|
+
const mainRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.main), { method: `HEAD` });
|
|
1816
1834
|
(0, vitest.expect)(mainRes.status).toBe(200);
|
|
1817
|
-
const errorRes = await fetch(
|
|
1835
|
+
const errorRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.error), { method: `HEAD` });
|
|
1818
1836
|
(0, vitest.expect)(errorRes.status).toBe(200);
|
|
1819
1837
|
}).run());
|
|
1820
1838
|
(0, vitest.test)(`spawn at unregistered type returns UNKNOWN_ENTITY_TYPE`, () => electricAgents(config.baseUrl).custom(async (ctx) => {
|
|
1821
|
-
const res = await fetch(
|
|
1839
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/unregistered/entity-1`)), {
|
|
1822
1840
|
method: `PUT`,
|
|
1823
1841
|
headers: { "content-type": `application/json` },
|
|
1824
1842
|
body: JSON.stringify({})
|
|
@@ -1860,7 +1878,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
1860
1878
|
creation_schema: { type: `object` }
|
|
1861
1879
|
}).spawn(`webhook-ctx-agent`, `entity-1`).send({ ping: true }).expectWebhook().expectEntityContext({ type: `webhook-ctx-agent` }).respondDone().run());
|
|
1862
1880
|
(0, vitest.test)(`send to nonexistent entity returns 404`, () => electricAgents(config.baseUrl).custom(async (ctx) => {
|
|
1863
|
-
const res = await fetch(
|
|
1881
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/nonexistent-type/nonexistent-id/send`)), {
|
|
1864
1882
|
method: `POST`,
|
|
1865
1883
|
headers: { "content-type": `application/json` },
|
|
1866
1884
|
body: JSON.stringify({ payload: {} })
|
|
@@ -1986,12 +2004,12 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
1986
2004
|
type: `object`,
|
|
1987
2005
|
properties: { name: { type: `string` } }
|
|
1988
2006
|
},
|
|
1989
|
-
|
|
2007
|
+
inbox_schemas: { query: {
|
|
1990
2008
|
type: `object`,
|
|
1991
2009
|
properties: { text: { type: `string` } },
|
|
1992
2010
|
required: [`text`]
|
|
1993
2011
|
} },
|
|
1994
|
-
|
|
2012
|
+
state_schemas: { result: {
|
|
1995
2013
|
type: `object`,
|
|
1996
2014
|
properties: { answer: { type: `string` } }
|
|
1997
2015
|
} }
|
|
@@ -2027,7 +2045,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2027
2045
|
type: `object`,
|
|
2028
2046
|
properties: { x: { type: `number` } }
|
|
2029
2047
|
},
|
|
2030
|
-
|
|
2048
|
+
inbox_schemas: { ping: {
|
|
2031
2049
|
type: `object`,
|
|
2032
2050
|
properties: { msg: { type: `string` } }
|
|
2033
2051
|
} }
|
|
@@ -2053,7 +2071,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2053
2071
|
description: `First registration`,
|
|
2054
2072
|
creation_schema: { type: `object` }
|
|
2055
2073
|
}).custom(async (ctx) => {
|
|
2056
|
-
const res = await fetch(
|
|
2074
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, `/_electric/entity-types`), {
|
|
2057
2075
|
method: `POST`,
|
|
2058
2076
|
headers: { "content-type": `application/json` },
|
|
2059
2077
|
body: JSON.stringify({
|
|
@@ -2070,7 +2088,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2070
2088
|
}).run();
|
|
2071
2089
|
});
|
|
2072
2090
|
(0, vitest.test)(`register rejects missing required fields`, () => electricAgents(config.baseUrl).custom(async (ctx) => {
|
|
2073
|
-
const res = await fetch(
|
|
2091
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, `/_electric/entity-types`), {
|
|
2074
2092
|
method: `POST`,
|
|
2075
2093
|
headers: { "content-type": `application/json` },
|
|
2076
2094
|
body: JSON.stringify({})
|
|
@@ -2115,7 +2133,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2115
2133
|
}).expectSpawnSchemaError(typeName, `bad-entity-1`, { args: { invalid: true } }).run();
|
|
2116
2134
|
});
|
|
2117
2135
|
(0, vitest.test)(`typed spawn at unregistered type returns UNKNOWN_ENTITY_TYPE (C9)`, () => electricAgents(config.baseUrl).custom(async (ctx) => {
|
|
2118
|
-
const res = await fetch(
|
|
2136
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/nonexistent/should-fail`)), {
|
|
2119
2137
|
method: `PUT`,
|
|
2120
2138
|
headers: { "content-type": `application/json` },
|
|
2121
2139
|
body: JSON.stringify({})
|
|
@@ -2134,7 +2152,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2134
2152
|
}).spawn(typeName, `parent-entity`).custom(async (ctx) => {
|
|
2135
2153
|
parentUrl = ctx.currentEntityUrl;
|
|
2136
2154
|
}).custom(async (ctx) => {
|
|
2137
|
-
const res = await fetch(
|
|
2155
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/${typeName}/child-entity`)), {
|
|
2138
2156
|
method: `PUT`,
|
|
2139
2157
|
headers: { "content-type": `application/json` },
|
|
2140
2158
|
body: JSON.stringify({ parent: parentUrl })
|
|
@@ -2161,7 +2179,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2161
2179
|
description: `Type for orphan spawn test`,
|
|
2162
2180
|
creation_schema: { type: `object` }
|
|
2163
2181
|
}).custom(async (ctx) => {
|
|
2164
|
-
const res = await fetch(
|
|
2182
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/${typeName}/orphan-entity`)), {
|
|
2165
2183
|
method: `PUT`,
|
|
2166
2184
|
headers: { "content-type": `application/json` },
|
|
2167
2185
|
body: JSON.stringify({ parent: `/nonexistent/parent` })
|
|
@@ -2182,7 +2200,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2182
2200
|
color: `blue`,
|
|
2183
2201
|
count: `42`
|
|
2184
2202
|
} }).custom(async (ctx) => {
|
|
2185
|
-
const res = await fetch(
|
|
2203
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(ctx.currentEntityUrl)));
|
|
2186
2204
|
const entity = await res.json();
|
|
2187
2205
|
const tags = entity.tags;
|
|
2188
2206
|
(0, vitest.expect)(tags.color).toBe(`blue`);
|
|
@@ -2196,7 +2214,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2196
2214
|
name: typeName,
|
|
2197
2215
|
description: `Type with string-only tags`
|
|
2198
2216
|
}).custom(async (ctx) => {
|
|
2199
|
-
const res = await fetch(
|
|
2217
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/${typeName}/should-fail`)), {
|
|
2200
2218
|
method: `PUT`,
|
|
2201
2219
|
headers: { "content-type": `application/json` },
|
|
2202
2220
|
body: JSON.stringify({ tags: { wrong: 123 } })
|
|
@@ -2210,20 +2228,20 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2210
2228
|
name: typeName,
|
|
2211
2229
|
description: `Type with default empty tags`
|
|
2212
2230
|
}).spawn(typeName, `entity-1`).custom(async (ctx) => {
|
|
2213
|
-
const res = await fetch(
|
|
2231
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(ctx.currentEntityUrl)));
|
|
2214
2232
|
const entity = await res.json();
|
|
2215
2233
|
(0, vitest.expect)(entity.tags).toEqual({});
|
|
2216
2234
|
}).run();
|
|
2217
2235
|
});
|
|
2218
2236
|
});
|
|
2219
2237
|
(0, vitest.describe)(`Electric Agents Schema Validation Gates`, () => {
|
|
2220
|
-
(0, vitest.test)(`send validates
|
|
2238
|
+
(0, vitest.test)(`send validates inbox_schemas (C11)`, () => {
|
|
2221
2239
|
const typeName = `send-schema-valid-${Date.now()}`;
|
|
2222
2240
|
return electricAgents(config.baseUrl).subscription(`/${typeName}/**`, `send-schema-sub`).registerType({
|
|
2223
2241
|
name: typeName,
|
|
2224
|
-
description: `Type with
|
|
2242
|
+
description: `Type with inbox schemas`,
|
|
2225
2243
|
creation_schema: { type: `object` },
|
|
2226
|
-
|
|
2244
|
+
inbox_schemas: { query: {
|
|
2227
2245
|
type: `object`,
|
|
2228
2246
|
properties: { text: { type: `string` } },
|
|
2229
2247
|
required: [`text`]
|
|
@@ -2234,9 +2252,9 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2234
2252
|
const typeName = `send-schema-inv-${Date.now()}`;
|
|
2235
2253
|
return electricAgents(config.baseUrl).subscription(`/${typeName}/**`, `send-schema-inv-sub`).registerType({
|
|
2236
2254
|
name: typeName,
|
|
2237
|
-
description: `Type with strict
|
|
2255
|
+
description: `Type with strict inbox schemas`,
|
|
2238
2256
|
creation_schema: { type: `object` },
|
|
2239
|
-
|
|
2257
|
+
inbox_schemas: { query: {
|
|
2240
2258
|
type: `object`,
|
|
2241
2259
|
properties: { text: { type: `string` } },
|
|
2242
2260
|
required: [`text`]
|
|
@@ -2247,30 +2265,30 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2247
2265
|
const typeName = `send-unknown-type-${Date.now()}`;
|
|
2248
2266
|
return electricAgents(config.baseUrl).subscription(`/${typeName}/**`, `send-unknown-sub`).registerType({
|
|
2249
2267
|
name: typeName,
|
|
2250
|
-
description: `Type with defined
|
|
2268
|
+
description: `Type with defined inbox schemas`,
|
|
2251
2269
|
creation_schema: { type: `object` },
|
|
2252
|
-
|
|
2270
|
+
inbox_schemas: { query: {
|
|
2253
2271
|
type: `object`,
|
|
2254
2272
|
properties: { text: { type: `string` } },
|
|
2255
2273
|
required: [`text`]
|
|
2256
2274
|
} }
|
|
2257
2275
|
}).spawn(typeName, `entity-1`).expectSendUnknownType({ text: `hi` }, { type: `unknown_type` }).run();
|
|
2258
2276
|
});
|
|
2259
|
-
(0, vitest.test)(`send without type when no
|
|
2277
|
+
(0, vitest.test)(`send without type when no inbox_schemas accepts any`, () => {
|
|
2260
2278
|
const typeName = `send-no-schemas-${Date.now()}`;
|
|
2261
2279
|
return electricAgents(config.baseUrl).subscription(`/${typeName}/**`, `send-noschema-sub`).registerType({
|
|
2262
2280
|
name: typeName,
|
|
2263
|
-
description: `Type without
|
|
2281
|
+
description: `Type without inbox schemas`,
|
|
2264
2282
|
creation_schema: { type: `object` }
|
|
2265
2283
|
}).spawn(typeName, `entity-1`).send({ anything: `goes` }).expectWebhook().respondDone().run();
|
|
2266
2284
|
});
|
|
2267
|
-
(0, vitest.test)(`send with empty
|
|
2285
|
+
(0, vitest.test)(`send with empty inbox_schemas rejects all`, () => {
|
|
2268
2286
|
const typeName = `send-empty-schemas-${Date.now()}`;
|
|
2269
2287
|
return electricAgents(config.baseUrl).subscription(`/${typeName}/**`, `send-empty-sub`).registerType({
|
|
2270
2288
|
name: typeName,
|
|
2271
|
-
description: `Type with empty
|
|
2289
|
+
description: `Type with empty inbox schemas`,
|
|
2272
2290
|
creation_schema: { type: `object` },
|
|
2273
|
-
|
|
2291
|
+
inbox_schemas: {}
|
|
2274
2292
|
}).spawn(typeName, `entity-1`).expectSendUnknownType({ text: `anything` }, { type: `some_type` }).run();
|
|
2275
2293
|
});
|
|
2276
2294
|
vitest.test.skip(`write appends event to entity stream`, () => {
|
|
@@ -2279,7 +2297,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2279
2297
|
name: typeName,
|
|
2280
2298
|
description: `Type for write test`,
|
|
2281
2299
|
creation_schema: { type: `object` },
|
|
2282
|
-
|
|
2300
|
+
state_schemas: { research_result: {
|
|
2283
2301
|
type: `object`,
|
|
2284
2302
|
properties: { findings: {
|
|
2285
2303
|
type: `array`,
|
|
@@ -2288,13 +2306,13 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2288
2306
|
} }
|
|
2289
2307
|
}).spawn(typeName, `entity-1`).write({ findings: [`test`] }, { type: `research_result` }).readStream().expectStreamContains(`research_result`).run();
|
|
2290
2308
|
});
|
|
2291
|
-
vitest.test.skip(`write validates
|
|
2309
|
+
vitest.test.skip(`write validates state_schemas (C12)`, () => {
|
|
2292
2310
|
const typeName = `write-schema-inv-${Date.now()}`;
|
|
2293
2311
|
return electricAgents(config.baseUrl).subscription(`/${typeName}/**`, `write-schema-sub`).registerType({
|
|
2294
2312
|
name: typeName,
|
|
2295
|
-
description: `Type with strict
|
|
2313
|
+
description: `Type with strict state schemas`,
|
|
2296
2314
|
creation_schema: { type: `object` },
|
|
2297
|
-
|
|
2315
|
+
state_schemas: { result: {
|
|
2298
2316
|
type: `object`,
|
|
2299
2317
|
properties: { value: { type: `number` } },
|
|
2300
2318
|
required: [`value`]
|
|
@@ -2305,19 +2323,19 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2305
2323
|
const typeName = `write-unknown-type-${Date.now()}`;
|
|
2306
2324
|
return electricAgents(config.baseUrl).subscription(`/${typeName}/**`, `write-unknown-sub`).registerType({
|
|
2307
2325
|
name: typeName,
|
|
2308
|
-
description: `Type with defined
|
|
2326
|
+
description: `Type with defined state schemas`,
|
|
2309
2327
|
creation_schema: { type: `object` },
|
|
2310
|
-
|
|
2328
|
+
state_schemas: { result: {
|
|
2311
2329
|
type: `object`,
|
|
2312
2330
|
properties: { value: { type: `number` } }
|
|
2313
2331
|
} }
|
|
2314
2332
|
}).spawn(typeName, `entity-1`).expectWriteUnknownType({ data: `test` }, { type: `unknown_event` }).run();
|
|
2315
2333
|
});
|
|
2316
|
-
vitest.test.skip(`write without type when no
|
|
2334
|
+
vitest.test.skip(`write without type when no state_schemas accepts any`, () => {
|
|
2317
2335
|
const typeName = `write-no-schemas-${Date.now()}`;
|
|
2318
2336
|
return electricAgents(config.baseUrl).subscription(`/${typeName}/**`, `write-noschema-sub`).registerType({
|
|
2319
2337
|
name: typeName,
|
|
2320
|
-
description: `Type without
|
|
2338
|
+
description: `Type without state schemas`,
|
|
2321
2339
|
creation_schema: { type: `object` }
|
|
2322
2340
|
}).spawn(typeName, `entity-1`).write({ anything: `goes` }).run();
|
|
2323
2341
|
});
|
|
@@ -2330,7 +2348,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2330
2348
|
}).spawn(typeName, `entity-1`).kill().custom(async (ctx) => {
|
|
2331
2349
|
const writeHeaders = { "content-type": `application/json` };
|
|
2332
2350
|
if (ctx.currentWriteToken) writeHeaders[`authorization`] = `Bearer ${ctx.currentWriteToken}`;
|
|
2333
|
-
const res = await fetch(
|
|
2351
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, `${ctx.currentEntityUrl}/main`), {
|
|
2334
2352
|
method: `POST`,
|
|
2335
2353
|
headers: writeHeaders,
|
|
2336
2354
|
body: JSON.stringify({
|
|
@@ -2384,7 +2402,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2384
2402
|
}).spawn(typeName, `entity-1`).custom(async (ctx) => {
|
|
2385
2403
|
const tagHeaders = { "content-type": `application/json` };
|
|
2386
2404
|
if (ctx.currentWriteToken) tagHeaders[`authorization`] = `Bearer ${ctx.currentWriteToken}`;
|
|
2387
|
-
const res = await fetch(
|
|
2405
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`${ctx.currentEntityUrl}/tags/owner`)), {
|
|
2388
2406
|
method: `POST`,
|
|
2389
2407
|
headers: tagHeaders,
|
|
2390
2408
|
body: JSON.stringify({ value: 123 })
|
|
@@ -2404,7 +2422,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2404
2422
|
}).custom(async (ctx) => {
|
|
2405
2423
|
const tagHeaders = {};
|
|
2406
2424
|
if (ctx.currentWriteToken) tagHeaders.authorization = `Bearer ${ctx.currentWriteToken}`;
|
|
2407
|
-
const res = await fetch(
|
|
2425
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`${ctx.currentEntityUrl}/tags/priority`)), {
|
|
2408
2426
|
method: `DELETE`,
|
|
2409
2427
|
headers: tagHeaders
|
|
2410
2428
|
});
|
|
@@ -2437,12 +2455,12 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2437
2455
|
name: typeName,
|
|
2438
2456
|
description: `Type for schema amendment`,
|
|
2439
2457
|
creation_schema: { type: `object` },
|
|
2440
|
-
|
|
2458
|
+
inbox_schemas: { query: {
|
|
2441
2459
|
type: `object`,
|
|
2442
2460
|
properties: { text: { type: `string` } },
|
|
2443
2461
|
required: [`text`]
|
|
2444
2462
|
} }
|
|
2445
|
-
}).amendSchemas(typeName, {
|
|
2463
|
+
}).amendSchemas(typeName, { inbox_schemas: { command: {
|
|
2446
2464
|
type: `object`,
|
|
2447
2465
|
properties: { action: { type: `string` } },
|
|
2448
2466
|
required: [`action`]
|
|
@@ -2454,13 +2472,13 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2454
2472
|
name: typeName,
|
|
2455
2473
|
description: `Type for schema conflict test`,
|
|
2456
2474
|
creation_schema: { type: `object` },
|
|
2457
|
-
|
|
2475
|
+
inbox_schemas: { query: {
|
|
2458
2476
|
type: `object`,
|
|
2459
2477
|
properties: { text: { type: `string` } },
|
|
2460
2478
|
required: [`text`]
|
|
2461
2479
|
} }
|
|
2462
2480
|
}).custom(async (ctx) => {
|
|
2463
|
-
const res = await fetch(
|
|
2481
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, `/_electric/entity-types/${typeName}/schemas`), {
|
|
2464
2482
|
method: `PATCH`,
|
|
2465
2483
|
headers: { "content-type": `application/json` },
|
|
2466
2484
|
body: JSON.stringify({ inbox_schemas: { query: {
|
|
@@ -2477,13 +2495,13 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2477
2495
|
name: typeName,
|
|
2478
2496
|
description: `Type for revision pinning test`,
|
|
2479
2497
|
creation_schema: { type: `object` },
|
|
2480
|
-
|
|
2498
|
+
inbox_schemas: { query: {
|
|
2481
2499
|
type: `object`,
|
|
2482
2500
|
properties: { text: { type: `string` } },
|
|
2483
2501
|
required: [`text`]
|
|
2484
2502
|
} }
|
|
2485
2503
|
}).spawn(typeName, `entity-1`).custom(async (ctx) => {
|
|
2486
|
-
const res = await fetch(
|
|
2504
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, `/_electric/entity-types/${typeName}/schemas`), {
|
|
2487
2505
|
method: `PATCH`,
|
|
2488
2506
|
headers: { "content-type": `application/json` },
|
|
2489
2507
|
body: JSON.stringify({ inbox_schemas: { new_command: {
|
|
@@ -2493,7 +2511,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2493
2511
|
});
|
|
2494
2512
|
(0, vitest.expect)(res.status).toBe(200);
|
|
2495
2513
|
}).custom(async (ctx) => {
|
|
2496
|
-
const res = await fetch(
|
|
2514
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`${ctx.currentEntityUrl}/send`)), {
|
|
2497
2515
|
method: `POST`,
|
|
2498
2516
|
headers: { "content-type": `application/json` },
|
|
2499
2517
|
body: JSON.stringify({
|
|
@@ -2524,7 +2542,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2524
2542
|
};
|
|
2525
2543
|
await receiver.start(manifest);
|
|
2526
2544
|
try {
|
|
2527
|
-
const res = await fetch(
|
|
2545
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, `/_electric/entity-types`), {
|
|
2528
2546
|
method: `POST`,
|
|
2529
2547
|
headers: { "content-type": `application/json` },
|
|
2530
2548
|
body: JSON.stringify({
|
|
@@ -3102,7 +3120,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3102
3120
|
name: `rev-multi-agent-${id}`,
|
|
3103
3121
|
description: `Test multi-revision pinning`,
|
|
3104
3122
|
creation_schema: { type: `object` },
|
|
3105
|
-
|
|
3123
|
+
inbox_schemas: { greet: {
|
|
3106
3124
|
type: `object`,
|
|
3107
3125
|
properties: { name: { type: `string` } },
|
|
3108
3126
|
required: [`name`]
|
|
@@ -3143,7 +3161,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3143
3161
|
}).deleteType(`amend-del-agent-${id}`).custom(async (ctx) => {
|
|
3144
3162
|
const res = await electricAgentsFetch(ctx.baseUrl, `/_electric/entity-types/amend-del-agent-${id}/schemas`, {
|
|
3145
3163
|
method: `PATCH`,
|
|
3146
|
-
body: JSON.stringify({
|
|
3164
|
+
body: JSON.stringify({ inbox_schemas: { msg: { type: `object` } } })
|
|
3147
3165
|
});
|
|
3148
3166
|
(0, vitest.expect)(res.status).toBe(404);
|
|
3149
3167
|
}).run();
|
|
@@ -3219,7 +3237,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3219
3237
|
description: `Test write without token`,
|
|
3220
3238
|
creation_schema: { type: `object` }
|
|
3221
3239
|
}).spawn(`auth-notoken-agent-${id}`, `entity-1`).custom(async (ctx) => {
|
|
3222
|
-
const res = await fetch(
|
|
3240
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, `${ctx.currentEntityUrl}/main`), {
|
|
3223
3241
|
method: `POST`,
|
|
3224
3242
|
headers: { "content-type": `application/json` },
|
|
3225
3243
|
body: JSON.stringify({
|
|
@@ -3239,7 +3257,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3239
3257
|
description: `Test write with wrong token`,
|
|
3240
3258
|
creation_schema: { type: `object` }
|
|
3241
3259
|
}).spawn(`auth-wrongtoken-agent-${id}`, `entity-1`).custom(async (ctx) => {
|
|
3242
|
-
const res = await fetch(
|
|
3260
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, `${ctx.currentEntityUrl}/main`), {
|
|
3243
3261
|
method: `POST`,
|
|
3244
3262
|
headers: {
|
|
3245
3263
|
"content-type": `application/json`,
|
|
@@ -3272,7 +3290,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3272
3290
|
description: `Test tag update without token`,
|
|
3273
3291
|
creation_schema: { type: `object` }
|
|
3274
3292
|
}).spawn(`auth-meta-notoken-agent-${id}`, `entity-1`).custom(async (ctx) => {
|
|
3275
|
-
const res = await fetch(
|
|
3293
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`${ctx.currentEntityUrl}/tags/key`)), {
|
|
3276
3294
|
method: `POST`,
|
|
3277
3295
|
headers: { "content-type": `application/json` },
|
|
3278
3296
|
body: JSON.stringify({ value: `value` })
|
|
@@ -3297,7 +3315,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3297
3315
|
description: `Test send without auth`,
|
|
3298
3316
|
creation_schema: { type: `object` }
|
|
3299
3317
|
}).spawn(`auth-send-noauth-agent-${id}`, `entity-1`).custom(async (ctx) => {
|
|
3300
|
-
const res = await fetch(
|
|
3318
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`${ctx.currentEntityUrl}/send`)), {
|
|
3301
3319
|
method: `POST`,
|
|
3302
3320
|
headers: { "content-type": `application/json` },
|
|
3303
3321
|
body: JSON.stringify({ payload: `hi` })
|
|
@@ -3312,7 +3330,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3312
3330
|
description: `Test GET does not leak write_token`,
|
|
3313
3331
|
creation_schema: { type: `object` }
|
|
3314
3332
|
}).spawn(`auth-noleak-agent-${id}`, `entity-1`).custom(async (ctx) => {
|
|
3315
|
-
const res = await fetch(
|
|
3333
|
+
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(ctx.currentEntityUrl)));
|
|
3316
3334
|
(0, vitest.expect)(res.status).toBe(200);
|
|
3317
3335
|
const entity = await res.json();
|
|
3318
3336
|
(0, vitest.expect)(entity.write_token).toBeUndefined();
|
|
@@ -3382,7 +3400,7 @@ function runCliConformanceTests(config) {
|
|
|
3382
3400
|
name: `cli-spawn-type`,
|
|
3383
3401
|
description: `Type for CLI spawn test`
|
|
3384
3402
|
}).setupSubscription(`/cli-spawn-type/**`, `cli-spawn-sub`).exec(`spawn`, `/cli-spawn-type/${id}`).expectExitCode(0).expectStdout(/Spawned/).verifyApi(async (baseUrl) => {
|
|
3385
|
-
const res = await fetch(
|
|
3403
|
+
const res = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(`/cli-spawn-type/${id}`)));
|
|
3386
3404
|
(0, vitest.expect)(res.status).toBe(200);
|
|
3387
3405
|
const entity = await res.json();
|
|
3388
3406
|
(0, vitest.expect)([`running`, `idle`]).toContain(entity.status);
|
|
@@ -3425,11 +3443,11 @@ function runCliConformanceTests(config) {
|
|
|
3425
3443
|
name: `cli-send-type`,
|
|
3426
3444
|
description: `Type for CLI send test`
|
|
3427
3445
|
}).setupSubscription(`/cli-send-type/**`, `cli-send-sub`).exec(`spawn`, `/cli-send-type/${id}`).expectExitCode(0).exec(`send`, `/cli-send-type/${id}`, `hello world`).expectExitCode(0).expectStdout(/Message sent/).verifyApi(async (baseUrl) => {
|
|
3428
|
-
const res = await fetch(
|
|
3446
|
+
const res = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(`/cli-send-type/${id}`)));
|
|
3429
3447
|
(0, vitest.expect)(res.status).toBe(200);
|
|
3430
3448
|
const entity = await res.json();
|
|
3431
3449
|
const streams = entity.streams;
|
|
3432
|
-
const streamRes = await fetch(`${
|
|
3450
|
+
const streamRes = await fetch(appendPathToUrl(baseUrl, `${streams.main}?offset=0000000000000000_0000000000000000`));
|
|
3433
3451
|
const events = await streamRes.json();
|
|
3434
3452
|
(0, vitest.expect)(events.length).toBeGreaterThanOrEqual(1);
|
|
3435
3453
|
const msgEvent = events.find((e) => e.type === `inbox`);
|
|
@@ -3449,7 +3467,7 @@ function runCliConformanceTests(config) {
|
|
|
3449
3467
|
name: `cli-inspect-etype`,
|
|
3450
3468
|
description: `Type for CLI inspect test`
|
|
3451
3469
|
}).setupSubscription(`/cli-inspect-etype/**`, `cli-inspect-sub`).exec(`spawn`, `/cli-inspect-etype/${id}`).expectExitCode(0).exec(`inspect`, `/cli-inspect-etype/${id}`).expectExitCode(0).expectStdout(/running|idle/).verifyApi(async (baseUrl) => {
|
|
3452
|
-
const res = await fetch(
|
|
3470
|
+
const res = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(`/cli-inspect-etype/${id}`)));
|
|
3453
3471
|
(0, vitest.expect)(res.status).toBe(200);
|
|
3454
3472
|
const entity = await res.json();
|
|
3455
3473
|
(0, vitest.expect)([`running`, `idle`]).toContain(entity.status);
|
|
@@ -3481,7 +3499,7 @@ function runCliConformanceTests(config) {
|
|
|
3481
3499
|
name: `cli-lifecycle-type`,
|
|
3482
3500
|
description: `Type for full lifecycle test`
|
|
3483
3501
|
}).setupSubscription(`/cli-lifecycle-type/**`, `cli-lifecycle-sub`).exec(`spawn`, `/cli-lifecycle-type/${id}`).expectExitCode(0).expectStdout(/Spawned/).verifyApi(async (baseUrl) => {
|
|
3484
|
-
const res = await fetch(
|
|
3502
|
+
const res = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(`/cli-lifecycle-type/${id}`)));
|
|
3485
3503
|
(0, vitest.expect)(res.status, `entity should exist after spawn`).toBe(200);
|
|
3486
3504
|
const entity = await res.json();
|
|
3487
3505
|
(0, vitest.expect)([`running`, `idle`]).toContain(entity.status);
|
|
@@ -3512,7 +3530,7 @@ function runCliConformanceTests(config) {
|
|
|
3512
3530
|
}
|
|
3513
3531
|
function runMockAgentTests(config) {
|
|
3514
3532
|
async function spawnEntity(baseUrl, typeName, instanceId) {
|
|
3515
|
-
const res = await fetch(
|
|
3533
|
+
const res = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(`/${encodeURIComponent(typeName)}/${encodeURIComponent(instanceId)}`)), {
|
|
3516
3534
|
method: `PUT`,
|
|
3517
3535
|
headers: { "content-type": `application/json` },
|
|
3518
3536
|
body: JSON.stringify({})
|
|
@@ -3521,7 +3539,7 @@ function runMockAgentTests(config) {
|
|
|
3521
3539
|
return await res.json();
|
|
3522
3540
|
}
|
|
3523
3541
|
async function sendMessage(baseUrl, entityUrl, text) {
|
|
3524
|
-
const res = await fetch(
|
|
3542
|
+
const res = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(`${entityUrl}/send`)), {
|
|
3525
3543
|
method: `POST`,
|
|
3526
3544
|
headers: { "content-type": `application/json` },
|
|
3527
3545
|
body: JSON.stringify({ payload: { text } })
|
|
@@ -3531,11 +3549,11 @@ function runMockAgentTests(config) {
|
|
|
3531
3549
|
async function pollForAgentResponse(baseUrl, entityUrl, timeoutMs = 1e4) {
|
|
3532
3550
|
const start = Date.now();
|
|
3533
3551
|
while (Date.now() - start < timeoutMs) {
|
|
3534
|
-
const entityRes = await fetch(
|
|
3552
|
+
const entityRes = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(entityUrl)));
|
|
3535
3553
|
if (!entityRes.ok) throw new Error(`Entity ${entityUrl} not found`);
|
|
3536
3554
|
const entity = await entityRes.json();
|
|
3537
3555
|
const streams = entity.streams;
|
|
3538
|
-
const streamRes = await fetch(`${
|
|
3556
|
+
const streamRes = await fetch(appendPathToUrl(baseUrl, `${streams.main}?offset=0000000000000000_0000000000000000`));
|
|
3539
3557
|
const events = await streamRes.json();
|
|
3540
3558
|
const hasRunComplete = events.some((e) => e.type === `run` && e.headers?.operation === `update`);
|
|
3541
3559
|
if (hasRunComplete) return events;
|
|
@@ -3588,11 +3606,11 @@ function runMockAgentCliTests(config) {
|
|
|
3588
3606
|
(0, vitest.test)(`spawn → send → agent responds with State Protocol events`, async () => {
|
|
3589
3607
|
const id = `cli-mock-${Date.now()}`;
|
|
3590
3608
|
await cliTest(config.baseUrl, config.cliBin).exec(`spawn`, `/chat/${id}`).expectExitCode(0).expectStdout(/Spawned/).exec(`send`, `/chat/${id}`, `hello from CLI`).expectExitCode(0).expectStdout(/Message sent/).wait(3e3).verifyApi(async (baseUrl) => {
|
|
3591
|
-
const res = await fetch(
|
|
3609
|
+
const res = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(`/chat/${id}`)));
|
|
3592
3610
|
(0, vitest.expect)(res.status, `entity should exist`).toBe(200);
|
|
3593
3611
|
const entity = await res.json();
|
|
3594
3612
|
const streams = entity.streams;
|
|
3595
|
-
const streamRes = await fetch(`${
|
|
3613
|
+
const streamRes = await fetch(appendPathToUrl(baseUrl, `${streams.main}?offset=0000000000000000_0000000000000000`));
|
|
3596
3614
|
const events = await streamRes.json();
|
|
3597
3615
|
(0, vitest.expect)(events.some((e) => e.type === `run`), `stream should contain run events from agent`).toBe(true);
|
|
3598
3616
|
(0, vitest.expect)(events.some((e) => e.type === `text`), `stream should contain text events from agent`).toBe(true);
|
|
@@ -3602,11 +3620,11 @@ function runMockAgentCliTests(config) {
|
|
|
3602
3620
|
(0, vitest.test)(`inspect shows entity after mock agent processes message`, async () => {
|
|
3603
3621
|
const id = `cli-inspect-mock-${Date.now()}`;
|
|
3604
3622
|
await cliTest(config.baseUrl, config.cliBin).exec(`spawn`, `/chat/${id}`).expectExitCode(0).exec(`send`, `/chat/${id}`, `test message`).expectExitCode(0).wait(3e3).exec(`inspect`, `/chat/${id}`).expectExitCode(0).expectStdout(/running|idle/).verifyApi(async (baseUrl) => {
|
|
3605
|
-
const res = await fetch(
|
|
3623
|
+
const res = await fetch(appendPathToUrl(baseUrl, routeControlPlanePath(`/chat/${id}`)));
|
|
3606
3624
|
(0, vitest.expect)(res.status).toBe(200);
|
|
3607
3625
|
const entity = await res.json();
|
|
3608
3626
|
const streams = entity.streams;
|
|
3609
|
-
const streamRes = await fetch(`${
|
|
3627
|
+
const streamRes = await fetch(appendPathToUrl(baseUrl, `${streams.main}?offset=0000000000000000_0000000000000000`));
|
|
3610
3628
|
const events = await streamRes.json();
|
|
3611
3629
|
const hasRunComplete = events.some((e) => e.type === `run` && e.headers?.operation === `update`);
|
|
3612
3630
|
(0, vitest.expect)(hasRunComplete, `mock agent should have written run completion event`).toBe(true);
|