@dxos/functions-testing 0.8.4-main.fcfe5033a5 → 0.8.4-staging.60fe92afc8
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/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +7 -9
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/edge-routine.test.d.ts +2 -0
- package/dist/types/src/edge-routine.test.d.ts.map +1 -0
- package/dist/types/src/functions/definitions.d.ts +1 -1
- package/dist/types/src/functions/definitions.d.ts.map +1 -1
- package/dist/types/src/functions/fib.d.ts +1 -1
- package/dist/types/src/functions/fib.d.ts.map +1 -1
- package/dist/types/src/functions/forex-effect.d.ts +1 -1
- package/dist/types/src/functions/forex-effect.d.ts.map +1 -1
- package/dist/types/src/functions/query.d.ts +1 -1
- package/dist/types/src/functions/query.d.ts.map +1 -1
- package/dist/types/src/functions/reply.d.ts +1 -1
- package/dist/types/src/functions/reply.d.ts.map +1 -1
- package/dist/types/src/functions/sleep.d.ts +1 -1
- package/dist/types/src/functions/sleep.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts +1 -1
- package/dist/types/src/testing/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +20 -22
- package/src/cpu-limit.test.ts +7 -7
- package/src/edge-routine.test.ts +110 -0
- package/src/functions/definitions.ts +6 -5
- package/src/functions/fib.ts +1 -1
- package/src/functions/forex-effect.ts +3 -2
- package/src/functions/query.ts +2 -2
- package/src/functions/reply.ts +1 -1
- package/src/functions/sleep.ts +1 -1
- package/src/testing/util.ts +5 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/functions-testing",
|
|
3
|
-
"version": "0.8.4-
|
|
3
|
+
"version": "0.8.4-staging.60fe92afc8",
|
|
4
4
|
"description": "Functions tests",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/dxos/dxos"
|
|
10
10
|
},
|
|
11
|
-
"license": "
|
|
11
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
12
12
|
"author": "info@dxos.org",
|
|
13
13
|
"sideEffects": true,
|
|
14
14
|
"type": "module",
|
|
@@ -25,31 +25,29 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"types": "dist/types/src/index.d.ts",
|
|
28
|
-
"typesVersions": {
|
|
29
|
-
"*": {
|
|
30
|
-
"testing": [
|
|
31
|
-
"dist/types/src/testing/index.d.ts"
|
|
32
|
-
]
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
28
|
"files": [
|
|
36
29
|
"dist",
|
|
37
30
|
"src"
|
|
38
31
|
],
|
|
39
32
|
"dependencies": {
|
|
40
|
-
"@effect/platform": "0.
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/functions-runtime": "0.8.4-
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/functions-runtime-cloudflare": "0.8.4-
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/
|
|
33
|
+
"@effect/platform": "0.96.1",
|
|
34
|
+
"@dxos/assistant-toolkit": "0.8.4-staging.60fe92afc8",
|
|
35
|
+
"@dxos/async": "0.8.4-staging.60fe92afc8",
|
|
36
|
+
"@dxos/client": "0.8.4-staging.60fe92afc8",
|
|
37
|
+
"@dxos/context": "0.8.4-staging.60fe92afc8",
|
|
38
|
+
"@dxos/functions": "0.8.4-staging.60fe92afc8",
|
|
39
|
+
"@dxos/echo": "0.8.4-staging.60fe92afc8",
|
|
40
|
+
"@dxos/functions-runtime": "0.8.4-staging.60fe92afc8",
|
|
41
|
+
"@dxos/config": "0.8.4-staging.60fe92afc8",
|
|
42
|
+
"@dxos/functions-runtime-cloudflare": "0.8.4-staging.60fe92afc8",
|
|
43
|
+
"@dxos/invariant": "0.8.4-staging.60fe92afc8",
|
|
44
|
+
"@dxos/keys": "0.8.4-staging.60fe92afc8",
|
|
45
|
+
"@dxos/operation": "0.8.4-staging.60fe92afc8",
|
|
46
|
+
"@dxos/log": "0.8.4-staging.60fe92afc8",
|
|
47
|
+
"@dxos/schema": "0.8.4-staging.60fe92afc8",
|
|
48
|
+
"@dxos/compute": "0.8.4-staging.60fe92afc8",
|
|
49
|
+
"@dxos/protocols": "0.8.4-staging.60fe92afc8",
|
|
50
|
+
"@dxos/util": "0.8.4-staging.60fe92afc8"
|
|
53
51
|
},
|
|
54
52
|
"publishConfig": {
|
|
55
53
|
"access": "public"
|
package/src/cpu-limit.test.ts
CHANGED
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, test } from 'vitest';
|
|
6
6
|
|
|
7
|
+
import { Trigger } from '@dxos/compute';
|
|
7
8
|
import { configPreset } from '@dxos/config';
|
|
8
9
|
import { Context } from '@dxos/context';
|
|
9
10
|
import { Obj, Ref } from '@dxos/echo';
|
|
10
|
-
import { Trigger } from '@dxos/functions';
|
|
11
11
|
import { FunctionRuntimeKind } from '@dxos/protocols';
|
|
12
12
|
|
|
13
13
|
import { deployFunction, observeInvocations, setup, sync } from './testing';
|
|
14
14
|
|
|
15
15
|
const FIB_FUNCTION_PATH = new URL('./functions/fib.ts', import.meta.url).pathname;
|
|
16
16
|
|
|
17
|
-
// Test suite to explore who CPU time limits influence function execution
|
|
18
|
-
describe
|
|
17
|
+
// Test suite to explore who CPU time limits influence function execution.
|
|
18
|
+
describe('CPU limit', { tags: ['functions-e2e'] }, () => {
|
|
19
19
|
const config = configPreset({ edge: 'dev' });
|
|
20
20
|
|
|
21
21
|
test('invoke directly', { timeout: 120_000 }, async ({ expect }) => {
|
|
@@ -88,7 +88,7 @@ describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit',
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
{
|
|
91
|
-
Obj.
|
|
91
|
+
Obj.update(trigger, (trigger) => {
|
|
92
92
|
trigger.input!.iterations = 100;
|
|
93
93
|
});
|
|
94
94
|
await sync(space);
|
|
@@ -117,7 +117,7 @@ describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit',
|
|
|
117
117
|
await observeInvocations(space, 100);
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
test
|
|
120
|
+
test('break CPU limit through natural exection', { timeout: 520_000 }, async ({ expect }) => {
|
|
121
121
|
const { client, space, functionsServiceClient } = await setup(config);
|
|
122
122
|
const func = await deployFunction(
|
|
123
123
|
space,
|
|
@@ -136,13 +136,13 @@ describe.runIf(process.env.DX_TEST_TAGS?.includes('functions-e2e'))('CPU limit',
|
|
|
136
136
|
await sync(space);
|
|
137
137
|
await observeInvocations(space, 5);
|
|
138
138
|
|
|
139
|
-
Obj.
|
|
139
|
+
Obj.update(trigger, (trigger) => {
|
|
140
140
|
trigger.input!.iterations = 1_000_000_000;
|
|
141
141
|
});
|
|
142
142
|
await sync(space);
|
|
143
143
|
await observeInvocations(space, 10);
|
|
144
144
|
|
|
145
|
-
Obj.
|
|
145
|
+
Obj.update(trigger, (trigger) => {
|
|
146
146
|
trigger.input!.iterations = 100;
|
|
147
147
|
});
|
|
148
148
|
await sync(space);
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
import { describe, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { AgentPrompt, DatabaseBlueprint, Chat } from '@dxos/assistant-toolkit';
|
|
9
|
+
import { Client } from '@dxos/client';
|
|
10
|
+
import { Blueprint, Operation, Routine, Trigger } from '@dxos/compute';
|
|
11
|
+
import { configPreset } from '@dxos/config';
|
|
12
|
+
import { Context } from '@dxos/context';
|
|
13
|
+
import { Feed, Obj, Ref, Type } from '@dxos/echo';
|
|
14
|
+
import { TestSchema } from '@dxos/echo/testing';
|
|
15
|
+
import { dbg, log } from '@dxos/log';
|
|
16
|
+
import { ErrorCodec } from '@dxos/protocols';
|
|
17
|
+
import { EdgeReplicationSetting } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
18
|
+
import { Text } from '@dxos/schema';
|
|
19
|
+
import { trim } from '@dxos/util';
|
|
20
|
+
|
|
21
|
+
import { sync } from './testing';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Cron trigger on EDGE runs {@link AgentPrompt} for a {@link Routine} that uses the Database
|
|
25
|
+
* blueprint Query tool against replicated ECHO documents.
|
|
26
|
+
*
|
|
27
|
+
* Prereq: local EDGE (`configPreset({ edge: 'local' })` → `http://localhost:8787`) with LLM
|
|
28
|
+
* available (API key and/or memo replay under `conversationsCache/`).
|
|
29
|
+
*/
|
|
30
|
+
describe('Edge routine', { tags: ['functions-e2e'] }, () => {
|
|
31
|
+
const config = configPreset({ edge: 'local' });
|
|
32
|
+
|
|
33
|
+
test('timer trigger runs routine with database blueprint on edge', { timeout: 420_000 }, async ({ expect }) => {
|
|
34
|
+
await using client = await new Client({
|
|
35
|
+
config,
|
|
36
|
+
types: [
|
|
37
|
+
Operation.PersistentOperation,
|
|
38
|
+
Trigger.Trigger,
|
|
39
|
+
Routine.Routine,
|
|
40
|
+
Blueprint.Blueprint,
|
|
41
|
+
Feed.Feed,
|
|
42
|
+
Text.Text,
|
|
43
|
+
Chat.Chat,
|
|
44
|
+
TestSchema.Organization,
|
|
45
|
+
],
|
|
46
|
+
}).initialize();
|
|
47
|
+
|
|
48
|
+
await client.halo.createIdentity();
|
|
49
|
+
const space = await client.spaces.create();
|
|
50
|
+
await space.waitUntilReady();
|
|
51
|
+
|
|
52
|
+
await space.internal.setEdgeReplicationPreference(EdgeReplicationSetting.ENABLED);
|
|
53
|
+
|
|
54
|
+
Obj.update(space.properties, (properties) => {
|
|
55
|
+
properties.computeEnvironment = 'edge';
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
space.db.add(Obj.make(TestSchema.Organization, { name: 'Acme Corp' }));
|
|
59
|
+
space.db.add(Obj.make(TestSchema.Organization, { name: 'Globex Industries' }));
|
|
60
|
+
space.db.add(Obj.make(TestSchema.Organization, { name: 'Initech' }));
|
|
61
|
+
|
|
62
|
+
const databaseBlueprint = space.db.add(DatabaseBlueprint.make());
|
|
63
|
+
|
|
64
|
+
const routine = space.db.add(
|
|
65
|
+
Routine.make({
|
|
66
|
+
name: 'edge-e2e-count-orgs-db-blueprint',
|
|
67
|
+
instructions: trim`
|
|
68
|
+
You have access to the Database blueprint tools.
|
|
69
|
+
Use the Query tool exactly once with typename "${Type.getTypename(TestSchema.Organization)}" and no other arguments.
|
|
70
|
+
Then call completeJob with the output object { "count": <number of rows returned by Query> }.
|
|
71
|
+
If you are unable to query -- fail.
|
|
72
|
+
Do not list schemas first.
|
|
73
|
+
`,
|
|
74
|
+
input: Schema.Struct({}),
|
|
75
|
+
output: Schema.Struct({
|
|
76
|
+
count: Schema.Number,
|
|
77
|
+
}),
|
|
78
|
+
blueprints: [Ref.make(databaseBlueprint)],
|
|
79
|
+
context: [],
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
const fn = Operation.serialize(AgentPrompt);
|
|
83
|
+
dbg(Obj.toJSON(fn));
|
|
84
|
+
|
|
85
|
+
const trigger = space.db.add(
|
|
86
|
+
Obj.make(Trigger.Trigger, {
|
|
87
|
+
enabled: true,
|
|
88
|
+
function: Ref.make(fn),
|
|
89
|
+
spec: Trigger.specTimer('* * * * * *'),
|
|
90
|
+
input: {
|
|
91
|
+
prompt: Ref.make(routine),
|
|
92
|
+
input: {},
|
|
93
|
+
model: 'ai.claude.model.claude-haiku-4-5',
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
await sync(space);
|
|
99
|
+
|
|
100
|
+
log('trigger created and synced');
|
|
101
|
+
log.break();
|
|
102
|
+
|
|
103
|
+
const runResult: any = await client.edge.http.forceRunCronTrigger(Context.default(), space.id, trigger.id);
|
|
104
|
+
if (runResult._kind === 'error') {
|
|
105
|
+
throw ErrorCodec.decode(runResult.error);
|
|
106
|
+
}
|
|
107
|
+
log('trigger ran', { runResult });
|
|
108
|
+
expect(runResult.result.count).toBe(3);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
|
+
import { Operation } from '@dxos/compute';
|
|
7
8
|
import { Database } from '@dxos/echo';
|
|
8
|
-
import {
|
|
9
|
+
import { DXN } from '@dxos/keys';
|
|
9
10
|
|
|
10
11
|
export const Fibonacci = Operation.make({
|
|
11
12
|
meta: {
|
|
12
|
-
key: 'example.
|
|
13
|
+
key: DXN.make('org.example.function.fib'),
|
|
13
14
|
name: 'Fibonacci',
|
|
14
15
|
description: 'Function that calculates a Fibonacci number',
|
|
15
16
|
},
|
|
@@ -26,7 +27,7 @@ export const Fibonacci = Operation.make({
|
|
|
26
27
|
|
|
27
28
|
export const Reply = Operation.make({
|
|
28
29
|
meta: {
|
|
29
|
-
key: 'example.
|
|
30
|
+
key: DXN.make('org.example.function.reply'),
|
|
30
31
|
name: 'Reply',
|
|
31
32
|
description: 'Function that echoes the input',
|
|
32
33
|
},
|
|
@@ -36,7 +37,7 @@ export const Reply = Operation.make({
|
|
|
36
37
|
|
|
37
38
|
export const Sleep = Operation.make({
|
|
38
39
|
meta: {
|
|
39
|
-
key: 'example.
|
|
40
|
+
key: DXN.make('org.example.function.sleep'),
|
|
40
41
|
name: 'Sleep',
|
|
41
42
|
description: 'Function that sleeps for a given amount of time',
|
|
42
43
|
},
|
|
@@ -51,7 +52,7 @@ export const Sleep = Operation.make({
|
|
|
51
52
|
|
|
52
53
|
export const QueryDb = Operation.make({
|
|
53
54
|
meta: {
|
|
54
|
-
key: 'example.
|
|
55
|
+
key: DXN.make('org.example.function.query'),
|
|
55
56
|
name: 'Query',
|
|
56
57
|
description: 'Queries the database',
|
|
57
58
|
},
|
package/src/functions/fib.ts
CHANGED
|
@@ -10,11 +10,12 @@ import * as Effect from 'effect/Effect';
|
|
|
10
10
|
import * as Schedule from 'effect/Schedule';
|
|
11
11
|
import * as Schema from 'effect/Schema';
|
|
12
12
|
|
|
13
|
-
import { Operation } from '@dxos/
|
|
13
|
+
import { Operation } from '@dxos/compute';
|
|
14
|
+
import { DXN } from '@dxos/keys';
|
|
14
15
|
|
|
15
16
|
const ForexEffect = Operation.make({
|
|
16
17
|
meta: {
|
|
17
|
-
key: 'org.dxos.script.
|
|
18
|
+
key: DXN.make('org.dxos.script.forexEffect'),
|
|
18
19
|
name: 'Forex Effect',
|
|
19
20
|
description: 'Returns the exchange rate between two currencies.',
|
|
20
21
|
},
|
package/src/functions/query.ts
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
|
|
7
|
+
import { Operation } from '@dxos/compute';
|
|
7
8
|
import { Database, Filter, Query as EchoQuery } from '@dxos/echo';
|
|
8
|
-
import { Operation } from '@dxos/operation';
|
|
9
9
|
|
|
10
10
|
import { QueryDb } from './definitions';
|
|
11
11
|
|
|
12
12
|
export default QueryDb.pipe(
|
|
13
13
|
Operation.withHandler(
|
|
14
14
|
Effect.fn(function* (data) {
|
|
15
|
-
const results = yield* Database.
|
|
15
|
+
const results = yield* Database.query(EchoQuery.select(Filter.everything())).run;
|
|
16
16
|
return { count: results.length };
|
|
17
17
|
}),
|
|
18
18
|
),
|
package/src/functions/reply.ts
CHANGED
package/src/functions/sleep.ts
CHANGED
package/src/testing/util.ts
CHANGED
|
@@ -7,14 +7,13 @@ import fs from 'node:fs';
|
|
|
7
7
|
import { sleep } from '@dxos/async';
|
|
8
8
|
import { Client, type Config } from '@dxos/client';
|
|
9
9
|
import { type Space } from '@dxos/client/echo';
|
|
10
|
+
import { Operation, Trigger } from '@dxos/compute';
|
|
10
11
|
import { Context } from '@dxos/context';
|
|
11
12
|
import { Feed, Obj } from '@dxos/echo';
|
|
12
|
-
import { Trigger } from '@dxos/functions';
|
|
13
13
|
import { InvocationTraceEndEvent, InvocationTraceStartEvent } from '@dxos/functions-runtime';
|
|
14
14
|
import { FunctionsServiceClient } from '@dxos/functions-runtime/edge';
|
|
15
15
|
import { bundleFunction } from '@dxos/functions-runtime/native';
|
|
16
16
|
import type { BundleResult } from '@dxos/functions-runtime/native';
|
|
17
|
-
import { Operation } from '@dxos/operation';
|
|
18
17
|
import { ErrorCodec, type FunctionRuntimeKind } from '@dxos/protocols';
|
|
19
18
|
import { EdgeReplicationSetting } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
20
19
|
|
|
@@ -85,8 +84,10 @@ export const observeInvocations = async (space: Space, maxCount: number | null)
|
|
|
85
84
|
while (true) {
|
|
86
85
|
try {
|
|
87
86
|
const traceFeed = space.properties.invocationTraceFeed?.target;
|
|
88
|
-
const
|
|
89
|
-
const invocations =
|
|
87
|
+
const traceFeedDXN = traceFeed ? Feed.getQueueUri(traceFeed) : undefined;
|
|
88
|
+
const invocations = traceFeedDXN
|
|
89
|
+
? ((await space.queues.get(traceFeedDXN, { subspaceTag: traceFeed!.namespace }).queryObjects()) ?? [])
|
|
90
|
+
: [];
|
|
90
91
|
|
|
91
92
|
for (const invocation of invocations) {
|
|
92
93
|
if (Obj.instanceOf(InvocationTraceStartEvent, invocation)) {
|