@electric-ax/agents-server-conformance-tests 0.1.10 → 0.1.12
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 +23 -30
- package/dist/index.d.cts +1 -4
- package/dist/index.d.ts +1 -4
- package/dist/index.js +23 -30
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -328,11 +328,8 @@ var ElectricAgentsScenario = class {
|
|
|
328
328
|
});
|
|
329
329
|
return this;
|
|
330
330
|
}
|
|
331
|
-
readStream(
|
|
332
|
-
this.steps.push({
|
|
333
|
-
kind: `readStream`,
|
|
334
|
-
stream
|
|
335
|
-
});
|
|
331
|
+
readStream() {
|
|
332
|
+
this.steps.push({ kind: `readStream` });
|
|
336
333
|
return this;
|
|
337
334
|
}
|
|
338
335
|
list(filter) {
|
|
@@ -624,7 +621,7 @@ async function executeStep(ctx, step) {
|
|
|
624
621
|
method: `POST`,
|
|
625
622
|
body: JSON.stringify(body)
|
|
626
623
|
});
|
|
627
|
-
(0, vitest.expect)(res.status)
|
|
624
|
+
(0, vitest.expect)([200, 204]).toContain(res.status);
|
|
628
625
|
ctx.history.push({
|
|
629
626
|
type: `message_sent`,
|
|
630
627
|
entityUrl,
|
|
@@ -671,7 +668,6 @@ async function executeStep(ctx, step) {
|
|
|
671
668
|
(0, vitest.expect)(entity, `Webhook payload must contain entity context`).toBeDefined();
|
|
672
669
|
(0, vitest.expect)(entity.url).toBeTruthy();
|
|
673
670
|
(0, vitest.expect)(entity.streams.main).toBeTruthy();
|
|
674
|
-
(0, vitest.expect)(entity.streams.error).toBeTruthy();
|
|
675
671
|
if (step.checks?.url) (0, vitest.expect)(entity.url).toBe(step.checks.url);
|
|
676
672
|
if (step.checks?.type) (0, vitest.expect)(entity.type).toBe(step.checks.type);
|
|
677
673
|
if (step.checks?.status) (0, vitest.expect)(entity.status).toBe(step.checks.status);
|
|
@@ -712,7 +708,7 @@ async function executeStep(ctx, step) {
|
|
|
712
708
|
}
|
|
713
709
|
case `readStream`: {
|
|
714
710
|
if (!ctx.currentEntityStreams) throw new Error(`No current entity streams`);
|
|
715
|
-
const streamPath =
|
|
711
|
+
const streamPath = ctx.currentEntityStreams.main;
|
|
716
712
|
const res = await fetch(appendPathToUrl(ctx.baseUrl, `${streamPath}?offset=0000000000000000_0000000000000000`));
|
|
717
713
|
if (res.status === 200) {
|
|
718
714
|
const text = await res.text();
|
|
@@ -1207,14 +1203,11 @@ function checkEntityContextOnWebhook(history) {
|
|
|
1207
1203
|
}
|
|
1208
1204
|
}
|
|
1209
1205
|
/**
|
|
1210
|
-
* Spec S5 — Structural: stream
|
|
1206
|
+
* Spec S5 — Structural: stream path must match {entity.url}/main.
|
|
1211
1207
|
* Soundness: Sound | Completeness: Complete (within trace)
|
|
1212
1208
|
*/
|
|
1213
1209
|
function checkStreamPathsMatchEntityUrl(history) {
|
|
1214
|
-
for (const event of history) if (event.type === `entity_spawned`) {
|
|
1215
|
-
(0, vitest.expect)(event.streams.main).toBe(`${event.entityUrl}/main`);
|
|
1216
|
-
(0, vitest.expect)(event.streams.error).toBe(`${event.entityUrl}/error`);
|
|
1217
|
-
}
|
|
1210
|
+
for (const event of history) if (event.type === `entity_spawned`) (0, vitest.expect)(event.streams.main).toBe(`${event.entityUrl}/main`);
|
|
1218
1211
|
}
|
|
1219
1212
|
/**
|
|
1220
1213
|
* Spec S4 — Safety: entity status transitions must be valid.
|
|
@@ -1276,8 +1269,8 @@ function checkAdditiveSchemaEvolution(history) {
|
|
|
1276
1269
|
}
|
|
1277
1270
|
/**
|
|
1278
1271
|
* Spec R4 — Registry stream consistency: every entity_spawned must refer to an
|
|
1279
|
-
* entity that has not already been killed, and stream
|
|
1280
|
-
* convention {entity.url}/main
|
|
1272
|
+
* entity that has not already been killed, and stream URL must follow the
|
|
1273
|
+
* convention {entity.url}/main.
|
|
1281
1274
|
* Soundness: Sound | Completeness: Complete (within trace)
|
|
1282
1275
|
*/
|
|
1283
1276
|
function checkRegistryStreamConsistency(history) {
|
|
@@ -1287,7 +1280,6 @@ function checkRegistryStreamConsistency(history) {
|
|
|
1287
1280
|
if (event.type === `entity_spawned`) {
|
|
1288
1281
|
(0, vitest.expect)(!killedEntities.has(event.entityUrl), `Registry consistency: entity_spawned for ${event.entityUrl} but entity was already killed`).toBe(true);
|
|
1289
1282
|
(0, vitest.expect)(event.streams.main, `Registry consistency: entity ${event.entityUrl} streams.main must be ${event.entityUrl}/main`).toBe(`${event.entityUrl}/main`);
|
|
1290
|
-
(0, vitest.expect)(event.streams.error, `Registry consistency: entity ${event.entityUrl} streams.error must be ${event.entityUrl}/error`).toBe(`${event.entityUrl}/error`);
|
|
1291
1283
|
}
|
|
1292
1284
|
}
|
|
1293
1285
|
}
|
|
@@ -1832,8 +1824,6 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
1832
1824
|
}).spawn(`spawn-test-agent`, `entity-1`).expectStatus(`running`).custom(async (ctx) => {
|
|
1833
1825
|
const mainRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.main), { method: `HEAD` });
|
|
1834
1826
|
(0, vitest.expect)(mainRes.status).toBe(200);
|
|
1835
|
-
const errorRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.error), { method: `HEAD` });
|
|
1836
|
-
(0, vitest.expect)(errorRes.status).toBe(200);
|
|
1837
1827
|
}).run());
|
|
1838
1828
|
(0, vitest.test)(`spawn at unregistered type returns UNKNOWN_ENTITY_TYPE`, () => electricAgents(config.baseUrl).custom(async (ctx) => {
|
|
1839
1829
|
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/unregistered/entity-1`)), {
|
|
@@ -1906,7 +1896,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
1906
1896
|
method: `POST`,
|
|
1907
1897
|
body: JSON.stringify({ payload: { hello: true } })
|
|
1908
1898
|
});
|
|
1909
|
-
(0, vitest.expect)(res.status)
|
|
1899
|
+
(0, vitest.expect)([200, 204]).toContain(res.status);
|
|
1910
1900
|
}).expectWebhook().respondDone().run());
|
|
1911
1901
|
});
|
|
1912
1902
|
(0, vitest.describe)(`Electric Agents List`, () => {
|
|
@@ -2520,7 +2510,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2520
2510
|
payload: { action: `test` }
|
|
2521
2511
|
})
|
|
2522
2512
|
});
|
|
2523
|
-
(0, vitest.expect)(res.status)
|
|
2513
|
+
(0, vitest.expect)([200, 204]).toContain(res.status);
|
|
2524
2514
|
}).run();
|
|
2525
2515
|
});
|
|
2526
2516
|
});
|
|
@@ -2649,7 +2639,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2649
2639
|
targetIdx
|
|
2650
2640
|
} })
|
|
2651
2641
|
});
|
|
2652
|
-
(0, vitest.expect)(res.status)
|
|
2642
|
+
(0, vitest.expect)([200, 204]).toContain(res.status);
|
|
2653
2643
|
ctx.history.push({
|
|
2654
2644
|
type: `message_sent`,
|
|
2655
2645
|
entityUrl: url,
|
|
@@ -2926,12 +2916,15 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2926
2916
|
const sandbox = await makeSandbox(`/tmp`);
|
|
2927
2917
|
try {
|
|
2928
2918
|
const tool = createBashTool(sandbox);
|
|
2929
|
-
const result = await tool.execute(`test-tc`, {
|
|
2919
|
+
const result = await tool.execute(`test-tc`, {
|
|
2920
|
+
command: `sleep 60`,
|
|
2921
|
+
timeoutMs: 1e3
|
|
2922
|
+
});
|
|
2930
2923
|
(0, vitest.expect)(result.details.timedOut).toBe(true);
|
|
2931
2924
|
} finally {
|
|
2932
2925
|
await sandbox.dispose();
|
|
2933
2926
|
}
|
|
2934
|
-
},
|
|
2927
|
+
}, 5e3);
|
|
2935
2928
|
(0, vitest.test)(`read_file rejects paths outside working directory`, async () => {
|
|
2936
2929
|
const { createReadFileTool } = await import(`../../agents-runtime/src/tools`);
|
|
2937
2930
|
const fs = await import(`node:fs/promises`);
|
|
@@ -3140,7 +3133,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3140
3133
|
(0, vitest.expect)(res.status).toBe(404);
|
|
3141
3134
|
}).run();
|
|
3142
3135
|
});
|
|
3143
|
-
(0, vitest.test)(`tag update on stopped entity is rejected
|
|
3136
|
+
(0, vitest.test)(`tag update on stopped entity is rejected as not running`, () => {
|
|
3144
3137
|
const id = Date.now();
|
|
3145
3138
|
return electricAgents(config.baseUrl).subscription(`/meta-stopped-agent-${id}/**`, `meta-stopped-sub-${id}`).registerType({
|
|
3146
3139
|
name: `meta-stopped-agent-${id}`,
|
|
@@ -3151,7 +3144,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3151
3144
|
method: `POST`,
|
|
3152
3145
|
body: JSON.stringify({ value: `value` })
|
|
3153
3146
|
});
|
|
3154
|
-
(0, vitest.expect)(res.status).toBe(
|
|
3147
|
+
(0, vitest.expect)(res.status).toBe(409);
|
|
3155
3148
|
}).run();
|
|
3156
3149
|
});
|
|
3157
3150
|
});
|
|
@@ -3191,7 +3184,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3191
3184
|
type: `farewell`
|
|
3192
3185
|
})
|
|
3193
3186
|
});
|
|
3194
|
-
(0, vitest.expect)(res.status)
|
|
3187
|
+
(0, vitest.expect)([200, 204]).toContain(res.status);
|
|
3195
3188
|
}).run();
|
|
3196
3189
|
});
|
|
3197
3190
|
(0, vitest.test)(`amend schemas on deleted type returns 404`, async () => {
|
|
@@ -3325,11 +3318,11 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3325
3318
|
(0, vitest.expect)(ctx.currentWriteToken).toBeNull();
|
|
3326
3319
|
}).run();
|
|
3327
3320
|
});
|
|
3328
|
-
(0, vitest.test)(`tag update without token is
|
|
3321
|
+
(0, vitest.test)(`tag update without legacy entity write token is authorized by principal permission`, () => {
|
|
3329
3322
|
const id = Date.now();
|
|
3330
3323
|
return electricAgents(config.baseUrl).subscription(`/auth-meta-notoken-agent-${id}/**`, `auth-meta-notoken-sub-${id}`).registerType({
|
|
3331
3324
|
name: `auth-meta-notoken-agent-${id}`,
|
|
3332
|
-
description: `Test tag update without token`,
|
|
3325
|
+
description: `Test tag update without legacy entity write token`,
|
|
3333
3326
|
creation_schema: { type: `object` }
|
|
3334
3327
|
}).spawn(`auth-meta-notoken-agent-${id}`, `entity-1`).custom(async (ctx) => {
|
|
3335
3328
|
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`${ctx.currentEntityUrl}/tags/key`)), {
|
|
@@ -3337,7 +3330,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3337
3330
|
headers: { "content-type": `application/json` },
|
|
3338
3331
|
body: JSON.stringify({ value: `value` })
|
|
3339
3332
|
});
|
|
3340
|
-
(0, vitest.expect)(res.status).toBe(
|
|
3333
|
+
(0, vitest.expect)(res.status).toBe(200);
|
|
3341
3334
|
}).run();
|
|
3342
3335
|
});
|
|
3343
3336
|
(0, vitest.test)(`spawn does not expose a public tag write token`, () => {
|
|
@@ -3362,7 +3355,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3362
3355
|
headers: { "content-type": `application/json` },
|
|
3363
3356
|
body: JSON.stringify({ payload: `hi` })
|
|
3364
3357
|
});
|
|
3365
|
-
(0, vitest.expect)(res.status)
|
|
3358
|
+
(0, vitest.expect)([200, 204]).toContain(res.status);
|
|
3366
3359
|
}).expectWebhook().respondDone().run();
|
|
3367
3360
|
});
|
|
3368
3361
|
(0, vitest.test)(`GET entity does not leak write_token`, () => {
|
package/dist/index.d.cts
CHANGED
|
@@ -64,7 +64,6 @@ type HistoryEvent = {
|
|
|
64
64
|
status: string;
|
|
65
65
|
streams: {
|
|
66
66
|
main: string;
|
|
67
|
-
error: string;
|
|
68
67
|
};
|
|
69
68
|
parent?: string;
|
|
70
69
|
} | {
|
|
@@ -184,7 +183,6 @@ interface WebhookEntityContext {
|
|
|
184
183
|
url: string;
|
|
185
184
|
streams: {
|
|
186
185
|
main: string;
|
|
187
|
-
error: string;
|
|
188
186
|
};
|
|
189
187
|
tags?: Record<string, string>;
|
|
190
188
|
}
|
|
@@ -273,7 +271,6 @@ interface RunContext {
|
|
|
273
271
|
currentEntityUrl: string | null;
|
|
274
272
|
currentEntityStreams: {
|
|
275
273
|
main: string;
|
|
276
|
-
error: string;
|
|
277
274
|
} | null;
|
|
278
275
|
currentWriteToken: string | null;
|
|
279
276
|
notification: WebhookNotification | null;
|
|
@@ -320,7 +317,7 @@ declare class ElectricAgentsScenario {
|
|
|
320
317
|
expectEntityContext(checks?: EntityContextChecks): this;
|
|
321
318
|
expectStatus(status: string): this;
|
|
322
319
|
expectStreamContains(messageType: string): this;
|
|
323
|
-
readStream(
|
|
320
|
+
readStream(): this;
|
|
324
321
|
list(filter?: {
|
|
325
322
|
type?: string;
|
|
326
323
|
status?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -64,7 +64,6 @@ type HistoryEvent = {
|
|
|
64
64
|
status: string;
|
|
65
65
|
streams: {
|
|
66
66
|
main: string;
|
|
67
|
-
error: string;
|
|
68
67
|
};
|
|
69
68
|
parent?: string;
|
|
70
69
|
} | {
|
|
@@ -184,7 +183,6 @@ interface WebhookEntityContext {
|
|
|
184
183
|
url: string;
|
|
185
184
|
streams: {
|
|
186
185
|
main: string;
|
|
187
|
-
error: string;
|
|
188
186
|
};
|
|
189
187
|
tags?: Record<string, string>;
|
|
190
188
|
}
|
|
@@ -273,7 +271,6 @@ interface RunContext {
|
|
|
273
271
|
currentEntityUrl: string | null;
|
|
274
272
|
currentEntityStreams: {
|
|
275
273
|
main: string;
|
|
276
|
-
error: string;
|
|
277
274
|
} | null;
|
|
278
275
|
currentWriteToken: string | null;
|
|
279
276
|
notification: WebhookNotification | null;
|
|
@@ -320,7 +317,7 @@ declare class ElectricAgentsScenario {
|
|
|
320
317
|
expectEntityContext(checks?: EntityContextChecks): this;
|
|
321
318
|
expectStatus(status: string): this;
|
|
322
319
|
expectStreamContains(messageType: string): this;
|
|
323
|
-
readStream(
|
|
320
|
+
readStream(): this;
|
|
324
321
|
list(filter?: {
|
|
325
322
|
type?: string;
|
|
326
323
|
status?: string;
|
package/dist/index.js
CHANGED
|
@@ -304,11 +304,8 @@ var ElectricAgentsScenario = class {
|
|
|
304
304
|
});
|
|
305
305
|
return this;
|
|
306
306
|
}
|
|
307
|
-
readStream(
|
|
308
|
-
this.steps.push({
|
|
309
|
-
kind: `readStream`,
|
|
310
|
-
stream
|
|
311
|
-
});
|
|
307
|
+
readStream() {
|
|
308
|
+
this.steps.push({ kind: `readStream` });
|
|
312
309
|
return this;
|
|
313
310
|
}
|
|
314
311
|
list(filter) {
|
|
@@ -600,7 +597,7 @@ async function executeStep(ctx, step) {
|
|
|
600
597
|
method: `POST`,
|
|
601
598
|
body: JSON.stringify(body)
|
|
602
599
|
});
|
|
603
|
-
expect(res.status)
|
|
600
|
+
expect([200, 204]).toContain(res.status);
|
|
604
601
|
ctx.history.push({
|
|
605
602
|
type: `message_sent`,
|
|
606
603
|
entityUrl,
|
|
@@ -647,7 +644,6 @@ async function executeStep(ctx, step) {
|
|
|
647
644
|
expect(entity, `Webhook payload must contain entity context`).toBeDefined();
|
|
648
645
|
expect(entity.url).toBeTruthy();
|
|
649
646
|
expect(entity.streams.main).toBeTruthy();
|
|
650
|
-
expect(entity.streams.error).toBeTruthy();
|
|
651
647
|
if (step.checks?.url) expect(entity.url).toBe(step.checks.url);
|
|
652
648
|
if (step.checks?.type) expect(entity.type).toBe(step.checks.type);
|
|
653
649
|
if (step.checks?.status) expect(entity.status).toBe(step.checks.status);
|
|
@@ -688,7 +684,7 @@ async function executeStep(ctx, step) {
|
|
|
688
684
|
}
|
|
689
685
|
case `readStream`: {
|
|
690
686
|
if (!ctx.currentEntityStreams) throw new Error(`No current entity streams`);
|
|
691
|
-
const streamPath =
|
|
687
|
+
const streamPath = ctx.currentEntityStreams.main;
|
|
692
688
|
const res = await fetch(appendPathToUrl(ctx.baseUrl, `${streamPath}?offset=0000000000000000_0000000000000000`));
|
|
693
689
|
if (res.status === 200) {
|
|
694
690
|
const text = await res.text();
|
|
@@ -1183,14 +1179,11 @@ function checkEntityContextOnWebhook(history) {
|
|
|
1183
1179
|
}
|
|
1184
1180
|
}
|
|
1185
1181
|
/**
|
|
1186
|
-
* Spec S5 — Structural: stream
|
|
1182
|
+
* Spec S5 — Structural: stream path must match {entity.url}/main.
|
|
1187
1183
|
* Soundness: Sound | Completeness: Complete (within trace)
|
|
1188
1184
|
*/
|
|
1189
1185
|
function checkStreamPathsMatchEntityUrl(history) {
|
|
1190
|
-
for (const event of history) if (event.type === `entity_spawned`) {
|
|
1191
|
-
expect(event.streams.main).toBe(`${event.entityUrl}/main`);
|
|
1192
|
-
expect(event.streams.error).toBe(`${event.entityUrl}/error`);
|
|
1193
|
-
}
|
|
1186
|
+
for (const event of history) if (event.type === `entity_spawned`) expect(event.streams.main).toBe(`${event.entityUrl}/main`);
|
|
1194
1187
|
}
|
|
1195
1188
|
/**
|
|
1196
1189
|
* Spec S4 — Safety: entity status transitions must be valid.
|
|
@@ -1252,8 +1245,8 @@ function checkAdditiveSchemaEvolution(history) {
|
|
|
1252
1245
|
}
|
|
1253
1246
|
/**
|
|
1254
1247
|
* Spec R4 — Registry stream consistency: every entity_spawned must refer to an
|
|
1255
|
-
* entity that has not already been killed, and stream
|
|
1256
|
-
* convention {entity.url}/main
|
|
1248
|
+
* entity that has not already been killed, and stream URL must follow the
|
|
1249
|
+
* convention {entity.url}/main.
|
|
1257
1250
|
* Soundness: Sound | Completeness: Complete (within trace)
|
|
1258
1251
|
*/
|
|
1259
1252
|
function checkRegistryStreamConsistency(history) {
|
|
@@ -1263,7 +1256,6 @@ function checkRegistryStreamConsistency(history) {
|
|
|
1263
1256
|
if (event.type === `entity_spawned`) {
|
|
1264
1257
|
expect(!killedEntities.has(event.entityUrl), `Registry consistency: entity_spawned for ${event.entityUrl} but entity was already killed`).toBe(true);
|
|
1265
1258
|
expect(event.streams.main, `Registry consistency: entity ${event.entityUrl} streams.main must be ${event.entityUrl}/main`).toBe(`${event.entityUrl}/main`);
|
|
1266
|
-
expect(event.streams.error, `Registry consistency: entity ${event.entityUrl} streams.error must be ${event.entityUrl}/error`).toBe(`${event.entityUrl}/error`);
|
|
1267
1259
|
}
|
|
1268
1260
|
}
|
|
1269
1261
|
}
|
|
@@ -1808,8 +1800,6 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
1808
1800
|
}).spawn(`spawn-test-agent`, `entity-1`).expectStatus(`running`).custom(async (ctx) => {
|
|
1809
1801
|
const mainRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.main), { method: `HEAD` });
|
|
1810
1802
|
expect(mainRes.status).toBe(200);
|
|
1811
|
-
const errorRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.error), { method: `HEAD` });
|
|
1812
|
-
expect(errorRes.status).toBe(200);
|
|
1813
1803
|
}).run());
|
|
1814
1804
|
test(`spawn at unregistered type returns UNKNOWN_ENTITY_TYPE`, () => electricAgents(config.baseUrl).custom(async (ctx) => {
|
|
1815
1805
|
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/unregistered/entity-1`)), {
|
|
@@ -1882,7 +1872,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
1882
1872
|
method: `POST`,
|
|
1883
1873
|
body: JSON.stringify({ payload: { hello: true } })
|
|
1884
1874
|
});
|
|
1885
|
-
expect(res.status)
|
|
1875
|
+
expect([200, 204]).toContain(res.status);
|
|
1886
1876
|
}).expectWebhook().respondDone().run());
|
|
1887
1877
|
});
|
|
1888
1878
|
describe(`Electric Agents List`, () => {
|
|
@@ -2496,7 +2486,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2496
2486
|
payload: { action: `test` }
|
|
2497
2487
|
})
|
|
2498
2488
|
});
|
|
2499
|
-
expect(res.status)
|
|
2489
|
+
expect([200, 204]).toContain(res.status);
|
|
2500
2490
|
}).run();
|
|
2501
2491
|
});
|
|
2502
2492
|
});
|
|
@@ -2625,7 +2615,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2625
2615
|
targetIdx
|
|
2626
2616
|
} })
|
|
2627
2617
|
});
|
|
2628
|
-
expect(res.status)
|
|
2618
|
+
expect([200, 204]).toContain(res.status);
|
|
2629
2619
|
ctx.history.push({
|
|
2630
2620
|
type: `message_sent`,
|
|
2631
2621
|
entityUrl: url,
|
|
@@ -2902,12 +2892,15 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
2902
2892
|
const sandbox = await makeSandbox(`/tmp`);
|
|
2903
2893
|
try {
|
|
2904
2894
|
const tool = createBashTool(sandbox);
|
|
2905
|
-
const result = await tool.execute(`test-tc`, {
|
|
2895
|
+
const result = await tool.execute(`test-tc`, {
|
|
2896
|
+
command: `sleep 60`,
|
|
2897
|
+
timeoutMs: 1e3
|
|
2898
|
+
});
|
|
2906
2899
|
expect(result.details.timedOut).toBe(true);
|
|
2907
2900
|
} finally {
|
|
2908
2901
|
await sandbox.dispose();
|
|
2909
2902
|
}
|
|
2910
|
-
},
|
|
2903
|
+
}, 5e3);
|
|
2911
2904
|
test(`read_file rejects paths outside working directory`, async () => {
|
|
2912
2905
|
const { createReadFileTool } = await import(`../../agents-runtime/src/tools`);
|
|
2913
2906
|
const fs = await import(`node:fs/promises`);
|
|
@@ -3116,7 +3109,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3116
3109
|
expect(res.status).toBe(404);
|
|
3117
3110
|
}).run();
|
|
3118
3111
|
});
|
|
3119
|
-
test(`tag update on stopped entity is rejected
|
|
3112
|
+
test(`tag update on stopped entity is rejected as not running`, () => {
|
|
3120
3113
|
const id = Date.now();
|
|
3121
3114
|
return electricAgents(config.baseUrl).subscription(`/meta-stopped-agent-${id}/**`, `meta-stopped-sub-${id}`).registerType({
|
|
3122
3115
|
name: `meta-stopped-agent-${id}`,
|
|
@@ -3127,7 +3120,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3127
3120
|
method: `POST`,
|
|
3128
3121
|
body: JSON.stringify({ value: `value` })
|
|
3129
3122
|
});
|
|
3130
|
-
expect(res.status).toBe(
|
|
3123
|
+
expect(res.status).toBe(409);
|
|
3131
3124
|
}).run();
|
|
3132
3125
|
});
|
|
3133
3126
|
});
|
|
@@ -3167,7 +3160,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3167
3160
|
type: `farewell`
|
|
3168
3161
|
})
|
|
3169
3162
|
});
|
|
3170
|
-
expect(res.status)
|
|
3163
|
+
expect([200, 204]).toContain(res.status);
|
|
3171
3164
|
}).run();
|
|
3172
3165
|
});
|
|
3173
3166
|
test(`amend schemas on deleted type returns 404`, async () => {
|
|
@@ -3301,11 +3294,11 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3301
3294
|
expect(ctx.currentWriteToken).toBeNull();
|
|
3302
3295
|
}).run();
|
|
3303
3296
|
});
|
|
3304
|
-
test(`tag update without token is
|
|
3297
|
+
test(`tag update without legacy entity write token is authorized by principal permission`, () => {
|
|
3305
3298
|
const id = Date.now();
|
|
3306
3299
|
return electricAgents(config.baseUrl).subscription(`/auth-meta-notoken-agent-${id}/**`, `auth-meta-notoken-sub-${id}`).registerType({
|
|
3307
3300
|
name: `auth-meta-notoken-agent-${id}`,
|
|
3308
|
-
description: `Test tag update without token`,
|
|
3301
|
+
description: `Test tag update without legacy entity write token`,
|
|
3309
3302
|
creation_schema: { type: `object` }
|
|
3310
3303
|
}).spawn(`auth-meta-notoken-agent-${id}`, `entity-1`).custom(async (ctx) => {
|
|
3311
3304
|
const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`${ctx.currentEntityUrl}/tags/key`)), {
|
|
@@ -3313,7 +3306,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3313
3306
|
headers: { "content-type": `application/json` },
|
|
3314
3307
|
body: JSON.stringify({ value: `value` })
|
|
3315
3308
|
});
|
|
3316
|
-
expect(res.status).toBe(
|
|
3309
|
+
expect(res.status).toBe(200);
|
|
3317
3310
|
}).run();
|
|
3318
3311
|
});
|
|
3319
3312
|
test(`spawn does not expose a public tag write token`, () => {
|
|
@@ -3338,7 +3331,7 @@ function runElectricAgentsConformanceTests(config) {
|
|
|
3338
3331
|
headers: { "content-type": `application/json` },
|
|
3339
3332
|
body: JSON.stringify({ payload: `hi` })
|
|
3340
3333
|
});
|
|
3341
|
-
expect(res.status)
|
|
3334
|
+
expect([200, 204]).toContain(res.status);
|
|
3342
3335
|
}).expectWebhook().respondDone().run();
|
|
3343
3336
|
});
|
|
3344
3337
|
test(`GET entity does not leak write_token`, () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@electric-ax/agents-server-conformance-tests",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Conformance test suite for Electric Agents server implementations",
|
|
5
5
|
"author": "Durable Stream contributors",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@durable-streams/client": "^0.2.6",
|
|
38
|
-
"@electric-sql/client": "^1.5.
|
|
38
|
+
"@electric-sql/client": "^1.5.21",
|
|
39
39
|
"fast-check": "^4.6.0",
|
|
40
40
|
"vitest": "^4.1.0"
|
|
41
41
|
},
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@mariozechner/pi-agent-core": "^0.70.2",
|
|
44
44
|
"tsdown": "^0.9.0",
|
|
45
45
|
"typescript": "^5.0.0",
|
|
46
|
-
"@electric-ax/agents-server": "0.4.
|
|
46
|
+
"@electric-ax/agents-server": "0.4.20"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
49
|
"node": ">=18.0.0"
|