@zhin.js/testing 1.0.4 → 1.0.6
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 +28 -1
- package/lib/fake-clock.d.ts +7 -2
- package/lib/fake-clock.d.ts.map +1 -1
- package/lib/fake-clock.js +15 -4
- package/lib/fake-clock.js.map +1 -1
- package/lib/fake-hosts.d.ts +1 -1
- package/lib/fake-hosts.d.ts.map +1 -1
- package/lib/fake-plugin.d.ts +37 -4
- package/lib/fake-plugin.d.ts.map +1 -1
- package/lib/fake-plugin.js +57 -4
- package/lib/fake-plugin.js.map +1 -1
- package/lib/fake-runners.d.ts +56 -3
- package/lib/fake-runners.d.ts.map +1 -1
- package/lib/fake-runners.js +97 -2
- package/lib/fake-runners.js.map +1 -1
- package/lib/index.d.ts +16 -8
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +9 -5
- package/lib/index.js.map +1 -1
- package/lib/test-module-runtime.d.ts +1 -1
- package/lib/test-module-runtime.d.ts.map +1 -1
- package/lib/test-module-runtime.js +1 -1
- package/lib/test-module-runtime.js.map +1 -1
- package/package.json +14 -12
- package/src/fake-clock.ts +14 -4
- package/src/fake-hosts.ts +1 -1
- package/src/fake-plugin.ts +71 -5
- package/src/fake-runners.ts +153 -5
- package/src/index.ts +17 -8
- package/src/test-module-runtime.ts +2 -2
package/README.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
The kit has two layers: typed **fakes** for fast unit tests (no `as never`, below), and
|
|
4
4
|
`PluginTestKit` for integration tests through the real Plugin Runtime (the rest of this file).
|
|
5
5
|
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
pnpm add -D @zhin.js/testing
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`zhin.js`, `@zhin.js/runtime` and `@zhin.js/tool` are peer dependencies; the kit ships no
|
|
13
|
+
`@zhin.js/*` copy of its own and resolves core, commands, adapters and the Plugin Runtime through the
|
|
14
|
+
plugin's `zhin.js`, so tests run on the same single stack as a real bot. pnpm and npm install
|
|
15
|
+
missing peers; with yarn 1 run `yarn add -D zhin.js @zhin.js/runtime @zhin.js/tool @zhin.js/testing`.
|
|
16
|
+
Upgrade it together with `zhin.js`.
|
|
17
|
+
|
|
6
18
|
## Fakes for unit tests
|
|
7
19
|
|
|
8
20
|
```ts
|
|
@@ -26,8 +38,18 @@ expect(message.replies).toEqual(['ok']);
|
|
|
26
38
|
`Scope`, `DisposeStack` and `GenerationHandoffStack`, plus the real owner-bound
|
|
27
39
|
`databaseHostToken`, `scheduleHostToken` and `outboundHostToken` facades over in-memory hosts
|
|
28
40
|
(`FakeDatabaseRootHost`, `FakeScheduleRootHost` driven by a `FakeClock`, `FakeOutboundRootHost`).
|
|
41
|
+
- `runPluginSetup` validates `config` against the plugin's declared `config` schema, applies its
|
|
42
|
+
defaults and keeps only declared top-level fields, like the Runtime (throws
|
|
43
|
+
`PluginConfigValidationError`). Pass `validateConfig: false` to hand `config` to `setup()` verbatim.
|
|
44
|
+
`resolvePluginConfig(definition, input)` returns the resolved view without running setup.
|
|
45
|
+
- `runTool(fake, tool, input, { message })` runs an Agent tool (a definition, or a local name
|
|
46
|
+
registered via `addTool()`): executable `inputSchema` parsing and `adapter` binding as in the
|
|
47
|
+
Runtime, with `origin`/`principal` derived from a fake message. No approval or policy enforcement.
|
|
29
48
|
- `runCommand`, `runMiddleware`, `runHandler` execute convention-directory definitions against the
|
|
30
49
|
fake. `runCommand` calls `execute()` directly: no routing, parameter parsing, `permit` or admission.
|
|
50
|
+
Pass `source` (the command's `index.ts` path or `new URL('../commands/echo/[text]/index.ts', import.meta.url)`)
|
|
51
|
+
to apply the Runtime's convention-path check first: a layout the Runtime rejects, such as
|
|
52
|
+
`commands/term/[word]/[...definition]`, throws instead of passing.
|
|
31
53
|
- `fakeOf<T>(partial)` replaces `{ ... } as never` for narrow collaborators and event payloads.
|
|
32
54
|
- The fakes do not run the renderer or outbound middleware; use `PluginTestKit` to verify the send chain.
|
|
33
55
|
|
|
@@ -74,7 +96,8 @@ The example requires the mounted plugin to provide `suite checkin`. Roles are su
|
|
|
74
96
|
the fixture; this verifies command admission, not a platform's real role projection. `permissions` is
|
|
75
97
|
the real PermissionHost used by the IM runtime.
|
|
76
98
|
|
|
77
|
-
|
|
99
|
+
Pass `createConfigDocument(path)` from `@zhin.js/config-file` as `config` to run on a real configuration file;
|
|
100
|
+
edit the file and call `reloadSource(path)` to apply it, as the CLI's file watcher would. Omitting `config` creates an empty v2 application. The kit does not read a user's configuration file
|
|
78
101
|
or infer instance mounts from `package.json#zhin.plugins`; pass the disposable application's v2
|
|
79
102
|
declaration explicitly when testing child instances.
|
|
80
103
|
|
|
@@ -91,6 +114,10 @@ write files or send real messages. Never point it at a production app or include
|
|
|
91
114
|
- `reloadSource(absolutePath)` submits a changed file to the real HMR coordinator and reports `reloaded`,
|
|
92
115
|
`unchanged`, `failed` or `restart-required`, with the committed generation. Edit the test app first.
|
|
93
116
|
A process restart requirement latches the native coordinator; close this kit and create a new one.
|
|
117
|
+
- `messageDiagnostics` is the same bounded, body-free reader the Console serves at
|
|
118
|
+
`GET /api/messages/diagnostics`; use it to assert route, dispatch status and delivery outcomes.
|
|
119
|
+
- `snapshots` is the committed-snapshot authority the Console routes read (task center, permission
|
|
120
|
+
explanation). Mount a real route on it to test a Console flow; do not publish through it.
|
|
94
121
|
- `patchConfig()` uses normal composition/validation. Invalid patches reject and keep the prior generation.
|
|
95
122
|
- `installResources()` installs host-owned public resources, such as a Database Host, Schedule Host or
|
|
96
123
|
a test HTML renderer. The library does not invent storage or a scheduler implementation.
|
package/lib/fake-clock.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PluginId, ScheduleHost, ScheduleJobRegistration } from '
|
|
1
|
+
import type { PluginId, ScheduleHost, ScheduleJobRegistration } from 'zhin.js/plugin-runtime';
|
|
2
2
|
/** Controllable wall clock. It never touches the real `Date` or timers. */
|
|
3
3
|
export declare class FakeClock {
|
|
4
4
|
#private;
|
|
@@ -7,7 +7,12 @@ export declare class FakeClock {
|
|
|
7
7
|
date(): Date;
|
|
8
8
|
/** Jump without firing anything (like setting the system time). */
|
|
9
9
|
set(time: number | Date): void;
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Move forward and run every cron job that became due, in time order.
|
|
12
|
+
*
|
|
13
|
+
* The clock always ends at the target time, even when a job throws. After a failure the error
|
|
14
|
+
* is rethrown and the remaining jobs due inside the same window are NOT run (they are skipped, not deferred).
|
|
15
|
+
*/
|
|
11
16
|
advance(ms: number): Promise<void>;
|
|
12
17
|
/** @internal */
|
|
13
18
|
onAdvance(listener: (from: number, to: number) => Promise<void>): () => void;
|
package/lib/fake-clock.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-clock.d.ts","sourceRoot":"","sources":["../src/fake-clock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"fake-clock.d.ts","sourceRoot":"","sources":["../src/fake-clock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAE9F,2EAA2E;AAC3E,qBAAa,SAAS;;gBAIR,KAAK,GAAE,MAAM,GAAG,IAAoC;IAIhE,GAAG,IAAI,MAAM;IACb,IAAI,IAAI,IAAI;IAEZ,mEAAmE;IACnE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAE9B;;;;;OAKG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxC,gBAAgB;IAChB,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI;CAI7E;AAyBD,6FAA6F;AAC7F,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAY/D;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAID;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,YAAY;;IAG3C,QAAQ,CAAC,KAAK,EAAE,SAAS;gBAAhB,KAAK,EAAE,SAAS;IAarC,QAAQ,CAAC,GAAG,EAAE,uBAAuB,GAAG,MAAM,IAAI;IAMlD,IAAI,IAAI,SAAS,gBAAgB,EAAE;IAKnC,sFAAsF;IAChF,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAKzC"}
|
package/lib/fake-clock.js
CHANGED
|
@@ -9,15 +9,24 @@ export class FakeClock {
|
|
|
9
9
|
date() { return new Date(this.#now); }
|
|
10
10
|
/** Jump without firing anything (like setting the system time). */
|
|
11
11
|
set(time) { this.#now = typeof time === 'number' ? time : time.getTime(); }
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Move forward and run every cron job that became due, in time order.
|
|
14
|
+
*
|
|
15
|
+
* The clock always ends at the target time, even when a job throws. After a failure the error
|
|
16
|
+
* is rethrown and the remaining jobs due inside the same window are NOT run (they are skipped, not deferred).
|
|
17
|
+
*/
|
|
13
18
|
async advance(ms) {
|
|
14
19
|
if (!(ms >= 0))
|
|
15
20
|
throw new RangeError('FakeClock.advance requires a non-negative duration');
|
|
16
21
|
const from = this.#now;
|
|
17
22
|
const to = from + ms;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
try {
|
|
24
|
+
for (const listener of [...this.#listeners])
|
|
25
|
+
await listener(from, to);
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
this.#now = to;
|
|
29
|
+
}
|
|
21
30
|
}
|
|
22
31
|
/** @internal */
|
|
23
32
|
onAdvance(listener) {
|
|
@@ -74,6 +83,8 @@ export class FakeScheduleRootHost {
|
|
|
74
83
|
clock.onAdvance(async (from, to) => {
|
|
75
84
|
for (let t = Math.floor(from / 1000) * 1000 + 1000; t <= to; t += 1000) {
|
|
76
85
|
const date = new Date(t);
|
|
86
|
+
// A job firing at `t` must observe `t` (e.g. to decide what is due), not the advance() start.
|
|
87
|
+
clock.set(t);
|
|
77
88
|
for (const job of [...this.#jobs]) {
|
|
78
89
|
if (this.#jobs.has(job) && job.isActive?.() !== false && job.matches(date))
|
|
79
90
|
await job.execute();
|
package/lib/fake-clock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-clock.js","sourceRoot":"","sources":["../src/fake-clock.ts"],"names":[],"mappings":"AAEA,2EAA2E;AAC3E,MAAM,OAAO,SAAS;IACpB,IAAI,CAAS;IACJ,UAAU,GAAG,IAAI,GAAG,EAA+C,CAAC;IAE7E,YAAY,QAAuB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAClE,CAAC;IAED,GAAG,KAAa,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,IAAI,KAAW,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE5C,mEAAmE;IACnE,GAAG,CAAC,IAAmB,IAAU,IAAI,CAAC,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAEhG
|
|
1
|
+
{"version":3,"file":"fake-clock.js","sourceRoot":"","sources":["../src/fake-clock.ts"],"names":[],"mappings":"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"}
|
package/lib/fake-hosts.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DatabaseHost, DatabaseHostModel, DeliveryTarget, OutboundEditInput, OutboundReactionInput, OutboundRecallInput, OutboundRootHost, OutboundSendInput, OutboundTypingInput, PersistentStatePort, PluginId } from '
|
|
1
|
+
import type { DatabaseHost, DatabaseHostModel, DeliveryTarget, OutboundEditInput, OutboundReactionInput, OutboundRecallInput, OutboundRootHost, OutboundSendInput, OutboundTypingInput, PersistentStatePort, PluginId } from 'zhin.js/plugin-runtime';
|
|
2
2
|
type Row = Record<string, unknown>;
|
|
3
3
|
/**
|
|
4
4
|
* Process-wide in-memory Database Host. Tables appear on `define()` (plugins define in `setup`);
|
package/lib/fake-hosts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-hosts.d.ts","sourceRoot":"","sources":["../src/fake-hosts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EAAE,iBAAiB,EAAyB,cAAc,EACtE,iBAAiB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,iBAAiB,EAClG,mBAAmB,EAAE,mBAAmB,EAAE,QAAQ,EACnD,MAAM,
|
|
1
|
+
{"version":3,"file":"fake-hosts.d.ts","sourceRoot":"","sources":["../src/fake-hosts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EAAE,iBAAiB,EAAyB,cAAc,EACtE,iBAAiB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,iBAAiB,EAClG,mBAAmB,EAAE,mBAAmB,EAAE,QAAQ,EACnD,MAAM,wBAAwB,CAAC;AAEhC,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA0DnC;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,YAAY;;IACvD,QAAQ,CAAC,OAAO,YAAY;IAC5B,OAAO,UAAQ;IACf,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAqB;IAExD,QAAQ,CAAC,MAAM;oBAAiB,MAAM,KAAG,iBAAiB,GAAG,SAAS;MAA6B;IAEnG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAGhE,MAAM,IAAI,SAAS,MAAM,EAAE;IAC3B,cAAc,IAAI,SAAS;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAC3B,qDAAqD;IACrD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,EAAE;CACzC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,aAAa,GAAG,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IACvG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,iFAAiF;IACjF,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;;IAC3D,QAAQ,CAAC,KAAK,EAAE,gBAAgB,EAAE,CAAM;IAIxC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,GAAE,OAA4C,GAAG,IAAI;IAanE,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IACtD,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAGjG,WAAW,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAC3D,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IACvD,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAChD,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IACvD,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CACxD"}
|
package/lib/fake-plugin.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DisposeStack, GenerationHandoffStack, PluginDatabaseHost, PluginOutboundHost, PluginScheduleHost, Scope, type FeatureId, type PluginDefinition, type PluginId, type PluginSetupContext, type Token } from '
|
|
2
|
-
import type { CapabilityContext } from '
|
|
1
|
+
import { DisposeStack, GenerationHandoffStack, PluginDatabaseHost, PluginOutboundHost, PluginScheduleHost, Scope, type FeatureId, type PluginDefinition, type PluginId, type PluginSetupContext, type Token } from 'zhin.js/plugin-runtime';
|
|
2
|
+
import type { CapabilityContext } from 'zhin.js';
|
|
3
3
|
import { FakeClock, FakeScheduleRootHost } from './fake-clock.js';
|
|
4
4
|
import { FakeDatabaseRootHost, FakeOutboundRootHost, type FakeOutboundCall } from './fake-hosts.js';
|
|
5
5
|
/** A typed `token -> value` binding; create with {@link fakeResource}. */
|
|
@@ -78,6 +78,39 @@ export declare class FakePlugin<TConfig = unknown> {
|
|
|
78
78
|
dispose(): Promise<void>;
|
|
79
79
|
}
|
|
80
80
|
export declare function createFakePlugin<TConfig = unknown>(options?: FakePluginOptions<TConfig>): FakePlugin<TConfig>;
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
export interface RunPluginSetupOptions<TConfig = unknown> extends Omit<FakePluginOptions<TConfig>, 'name' | 'config'> {
|
|
82
|
+
/**
|
|
83
|
+
* Raw plugin configuration, as a user would write it under the instance key in `zhin.config.yml`.
|
|
84
|
+
* When the definition declares `config`, it is validated and defaulted like the real Runtime does.
|
|
85
|
+
*/
|
|
86
|
+
readonly config?: Partial<TConfig>;
|
|
87
|
+
/**
|
|
88
|
+
* Validate `config` against `definition.config` and apply its defaults (default `true`).
|
|
89
|
+
* Set `false` to pass `config` to `setup()` verbatim, as earlier versions of `runPluginSetup` did.
|
|
90
|
+
*/
|
|
91
|
+
readonly validateConfig?: boolean;
|
|
92
|
+
}
|
|
93
|
+
/** Thrown by {@link runPluginSetup} / {@link resolvePluginConfig} when config fails the plugin's schema. */
|
|
94
|
+
export declare class PluginConfigValidationError extends TypeError {
|
|
95
|
+
readonly plugin: string;
|
|
96
|
+
/** Human-readable `instancePath message` lines, same wording as Runtime startup errors. */
|
|
97
|
+
readonly issues: readonly string[];
|
|
98
|
+
readonly name = "PluginConfigValidationError";
|
|
99
|
+
constructor(plugin: string,
|
|
100
|
+
/** Human-readable `instancePath message` lines, same wording as Runtime startup errors. */
|
|
101
|
+
issues: readonly string[]);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Resolve the config view `setup()` would receive from the real Runtime: validate `input` against
|
|
105
|
+
* `definition.config`, fill schema defaults, and keep only the top-level fields the schema declares.
|
|
106
|
+
* Without a declared `config` schema the input is returned unchanged (`{}` when omitted).
|
|
107
|
+
*/
|
|
108
|
+
export declare function resolvePluginConfig<TConfig = unknown>(definition: Pick<PluginDefinition<TConfig>, 'name' | 'config'>, input?: unknown): TConfig;
|
|
109
|
+
/**
|
|
110
|
+
* Create a fake and run `definition.setup()` in one step. Call `dispose()` when done.
|
|
111
|
+
*
|
|
112
|
+
* When the plugin declares a `config` schema, `options.config` is validated and defaulted first
|
|
113
|
+
* (throws {@link PluginConfigValidationError}); pass `validateConfig: false` to opt out.
|
|
114
|
+
*/
|
|
115
|
+
export declare function runPluginSetup<TConfig = unknown>(definition: PluginDefinition<TConfig>, options?: RunPluginSetupOptions<TConfig>): Promise<FakePlugin<TConfig>>;
|
|
83
116
|
//# sourceMappingURL=fake-plugin.d.ts.map
|
package/lib/fake-plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-plugin.d.ts","sourceRoot":"","sources":["../src/fake-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAChG,KAAK,EACS,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,QAAQ,EAClE,KAAK,kBAAkB,EAAE,KAAK,KAAK,EACpC,MAAM,
|
|
1
|
+
{"version":3,"file":"fake-plugin.d.ts","sourceRoot":"","sources":["../src/fake-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAChG,KAAK,EACS,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,QAAQ,EAClE,KAAK,kBAAkB,EAAE,KAAK,KAAK,EACpC,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEpG,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAAG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE;AAEzF,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,YAAY,CAEvE;AAED,MAAM,WAAW,iBAAiB,CAAC,OAAO,GAAG,OAAO;IAClD,kEAAkE;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,gGAAgG;IAChG,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC7C,mFAAmF;IACnF,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3G,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3C,uEAAuE;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACvD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,sGAAsG;AACtG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED;;;;GAIG;AACH,qBAAa,UAAU,CAAC,OAAO,GAAG,OAAO;;IACvC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAkB;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;IAC1B,QAAQ,CAAC,SAAS,eAAsB;IACxC,QAAQ,CAAC,OAAO,yBAAgC;IAChD,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACtD,iGAAiG;IACjG,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAM;IAChD,8CAA8C;IAC9C,QAAQ,CAAC,YAAY,uBAA8B;IACnD,QAAQ,CAAC,YAAY,uBAA8B;IACnD,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAC5C,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;gBAI1B,OAAO,GAAE,iBAAiB,CAAC,OAAO,CAAM;IAmBpD,iDAAiD;IACjD,IAAI,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAA0B;IACpE,IAAI,MAAM,IAAI,WAAW,CAA+B;IAExD,wEAAwE;IACxE,IAAI,IAAI,IAAI,SAAS,gBAAgB,EAAE,CAAoC;IAE3E,4EAA4E;IAC5E,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;IAItD,yFAAyF;IACnF,KAAK,CAAC,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjE,oEAAoE;IACpE,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,SAAS,KAAK,CAAC,OAAO,CAAC,EAAE;IAIrF,4FAA4F;IAC5F,iBAAiB,IAAI,iBAAiB,CAAC,OAAO,CAAC;IAgB/C,6DAA6D;IACvD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CA4B/B;AAED,wBAAgB,gBAAgB,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,CAE7G;AAED,MAAM,WAAW,qBAAqB,CAAC,OAAO,GAAG,OAAO,CAAE,SAAQ,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACnH;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,4GAA4G;AAC5G,qBAAa,2BAA4B,SAAQ,SAAS;IAGtD,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE;IAJpC,SAAkB,IAAI,iCAAiC;gBAE5C,MAAM,EAAE,MAAM;IACvB,2FAA2F;IAClF,MAAM,EAAE,SAAS,MAAM,EAAE;CAIrC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAG,OAAO,EACnD,UAAU,EAAE,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,EAC9D,KAAK,CAAC,EAAE,OAAO,GACd,OAAO,CAsBT;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAG,OAAO,EACpD,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,EACrC,OAAO,GAAE,qBAAqB,CAAC,OAAO,CAAM,GAC3C,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAM9B"}
|
package/lib/fake-plugin.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { DisposeStack, GenerationHandoffStack, PluginDatabaseHost, PluginOutboundHost, PluginScheduleHost, Scope, databaseHostToken, outboundHostToken, qualifyPluginResourceName, rootPluginId, scheduleHostToken, } from '
|
|
1
|
+
import { DisposeStack, GenerationHandoffStack, PluginDatabaseHost, PluginOutboundHost, PluginScheduleHost, Scope, databaseHostToken, outboundHostToken, qualifyPluginResourceName, rootPluginId, scheduleHostToken, } from 'zhin.js/plugin-runtime';
|
|
2
|
+
import { parsePluginConfigSchema } from '@zhin.js/runtime';
|
|
2
3
|
import { FakeClock, FakeScheduleRootHost } from './fake-clock.js';
|
|
3
4
|
import { FakeDatabaseRootHost, FakeOutboundRootHost } from './fake-hosts.js';
|
|
4
5
|
/** Bind a value to a token with the token's own type, so tests need no cast. */
|
|
@@ -82,6 +83,7 @@ export class FakePlugin {
|
|
|
82
83
|
signal: this.signal, owner, generation: this.generation,
|
|
83
84
|
config: this.config,
|
|
84
85
|
use: (token) => this.resources.use(token),
|
|
86
|
+
has: (token) => this.resources.has(token),
|
|
85
87
|
project: (feature) => {
|
|
86
88
|
if (!this.projections.has(feature))
|
|
87
89
|
throw new Error(`Missing Feature projection ${feature}`);
|
|
@@ -121,9 +123,60 @@ export class FakePlugin {
|
|
|
121
123
|
export function createFakePlugin(options) {
|
|
122
124
|
return new FakePlugin(options);
|
|
123
125
|
}
|
|
124
|
-
/**
|
|
125
|
-
export
|
|
126
|
-
|
|
126
|
+
/** Thrown by {@link runPluginSetup} / {@link resolvePluginConfig} when config fails the plugin's schema. */
|
|
127
|
+
export class PluginConfigValidationError extends TypeError {
|
|
128
|
+
plugin;
|
|
129
|
+
issues;
|
|
130
|
+
name = 'PluginConfigValidationError';
|
|
131
|
+
constructor(plugin,
|
|
132
|
+
/** Human-readable `instancePath message` lines, same wording as Runtime startup errors. */
|
|
133
|
+
issues) {
|
|
134
|
+
super(`Invalid config for plugin ${plugin}:\n ${issues.join('\n ')}`);
|
|
135
|
+
this.plugin = plugin;
|
|
136
|
+
this.issues = issues;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Resolve the config view `setup()` would receive from the real Runtime: validate `input` against
|
|
141
|
+
* `definition.config`, fill schema defaults, and keep only the top-level fields the schema declares.
|
|
142
|
+
* Without a declared `config` schema the input is returned unchanged (`{}` when omitted).
|
|
143
|
+
*/
|
|
144
|
+
export function resolvePluginConfig(definition, input) {
|
|
145
|
+
if (!definition.config)
|
|
146
|
+
return (input ?? {});
|
|
147
|
+
const schema = definition.config.toJsonSchema();
|
|
148
|
+
const { value, errors } = parsePluginConfigSchema(schema, input);
|
|
149
|
+
if (errors.length) {
|
|
150
|
+
throw new PluginConfigValidationError(definition.name, errors.map((error) => {
|
|
151
|
+
const params = error.params;
|
|
152
|
+
const detail = typeof params?.additionalProperty === 'string'
|
|
153
|
+
? ` (additionalProperty: ${params.additionalProperty})`
|
|
154
|
+
: Array.isArray(params?.allowedValues) ? ` (allowedValues: ${JSON.stringify(params.allowedValues)})` : '';
|
|
155
|
+
const path = error.instancePath.replace(/^\/candidate/, '');
|
|
156
|
+
return `${path || '/'} ${error.message ?? error.keyword}${detail}`;
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
// The Runtime re-picks a plugin's own top-level fields (see config-composer `pickOwnFields`).
|
|
160
|
+
const properties = schema.properties && typeof schema.properties === 'object' && !Array.isArray(schema.properties)
|
|
161
|
+
? Object.keys(schema.properties)
|
|
162
|
+
: [];
|
|
163
|
+
const record = value && typeof value === 'object' ? value : {};
|
|
164
|
+
const view = {};
|
|
165
|
+
for (const key of properties)
|
|
166
|
+
if (Object.hasOwn(record, key))
|
|
167
|
+
view[key] = record[key];
|
|
168
|
+
return view;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Create a fake and run `definition.setup()` in one step. Call `dispose()` when done.
|
|
172
|
+
*
|
|
173
|
+
* When the plugin declares a `config` schema, `options.config` is validated and defaulted first
|
|
174
|
+
* (throws {@link PluginConfigValidationError}); pass `validateConfig: false` to opt out.
|
|
175
|
+
*/
|
|
176
|
+
export async function runPluginSetup(definition, options = {}) {
|
|
177
|
+
const { validateConfig = true, config, ...rest } = options;
|
|
178
|
+
const resolved = validateConfig ? resolvePluginConfig(definition, config) : config;
|
|
179
|
+
const fake = new FakePlugin({ ...rest, ...(resolved === undefined ? {} : { config: resolved }), name: definition.name });
|
|
127
180
|
await fake.setup(definition);
|
|
128
181
|
return fake;
|
|
129
182
|
}
|
package/lib/fake-plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-plugin.js","sourceRoot":"","sources":["../src/fake-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAChG,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,YAAY,EAAE,iBAAiB,GAGxG,MAAM,
|
|
1
|
+
{"version":3,"file":"fake-plugin.js","sourceRoot":"","sources":["../src/fake-plugin.ts"],"names":[],"mappings":"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"}
|
package/lib/fake-runners.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import { type Locale } from 'zhin.js/i18n';
|
|
2
|
+
import type { CommandContext, CommandDefinition, CommandMessage, CommandParameterValue } from 'zhin.js/command';
|
|
3
|
+
import type { HandlerDefinition } from 'zhin.js/handler';
|
|
4
|
+
import type { MiddlewareDefinition } from 'zhin.js/middleware';
|
|
5
|
+
import { type AgentToolDefinition, type ToolExecutionContext, type ToolInvocationOrigin, type ToolInvocationPolicy, type ToolQuestionPort } from '@zhin.js/tool';
|
|
6
|
+
import type { OperationClientPort } from 'zhin.js';
|
|
4
7
|
import type { FakePlugin } from './fake-plugin.js';
|
|
5
8
|
/** Inbound message with recorded replies. */
|
|
6
9
|
export interface FakeMessage extends CommandMessage {
|
|
@@ -8,6 +11,7 @@ export interface FakeMessage extends CommandMessage {
|
|
|
8
11
|
readonly replies: unknown[];
|
|
9
12
|
}
|
|
10
13
|
export interface FakeMessageInit {
|
|
14
|
+
readonly locale?: Locale;
|
|
11
15
|
readonly content?: string;
|
|
12
16
|
readonly sender?: {
|
|
13
17
|
readonly id: string;
|
|
@@ -21,18 +25,29 @@ export interface FakeMessageInit {
|
|
|
21
25
|
}
|
|
22
26
|
export declare function createFakeMessage(init?: FakeMessageInit): FakeMessage;
|
|
23
27
|
export interface RunCommandOptions<TInput extends CommandMessage = FakeMessage> {
|
|
28
|
+
readonly locale?: Locale;
|
|
29
|
+
readonly formatCommand?: CommandContext['formatCommand'];
|
|
24
30
|
readonly input?: TInput;
|
|
25
31
|
readonly args?: readonly string[];
|
|
26
32
|
readonly params?: Readonly<Record<string, CommandParameterValue>>;
|
|
27
33
|
readonly options?: Readonly<Record<string, CommandParameterValue>>;
|
|
28
34
|
readonly segments?: CommandContext['segments'];
|
|
29
35
|
readonly interaction?: CommandContext['interaction'];
|
|
36
|
+
/**
|
|
37
|
+
* Where the command lives: its `commands/.../index.ts` file (or directory), as a path or a
|
|
38
|
+
* `file:` URL such as `new URL('../commands/echo/[text]/index.ts', import.meta.url)`. When set,
|
|
39
|
+
* the definition first goes through the Runtime's convention-path check (`bindCommandSource`
|
|
40
|
+
* from `zhin.js/command`): a layout the Runtime would reject, e.g. `commands/term/[word]/[...rest]`,
|
|
41
|
+
* throws here too, and the directory parameter is bound as in the Runtime.
|
|
42
|
+
*/
|
|
43
|
+
readonly source?: string | URL;
|
|
30
44
|
}
|
|
31
45
|
/** Build the context a command receives from the fake plugin; usable for direct `execute()` calls. */
|
|
32
46
|
export declare function createCommandContext<TConfig, TInput extends CommandMessage = FakeMessage>(fake: FakePlugin<TConfig>, options?: RunCommandOptions<TInput>): CommandContext<TConfig, TInput, string | undefined>;
|
|
33
47
|
/**
|
|
34
48
|
* Execute a command definition against the fake. This calls `execute()` directly: it does NOT run
|
|
35
49
|
* route matching, parameter parsing, `permit`, or admission. Use `PluginTestKit` for those.
|
|
50
|
+
* Pass `source` to apply the Runtime's convention-path check first.
|
|
36
51
|
*/
|
|
37
52
|
export declare function runCommand<TConfig, TResult, TInput extends CommandMessage = FakeMessage>(fake: FakePlugin<TConfig>, definition: CommandDefinition<TConfig, TResult, TInput, string | undefined>, options?: RunCommandOptions<TInput>): Promise<TResult>;
|
|
38
53
|
export interface RunMiddlewareResult {
|
|
@@ -44,4 +59,42 @@ export interface RunMiddlewareResult {
|
|
|
44
59
|
export declare function runMiddleware<TConfig, TInput = FakeMessage>(fake: FakePlugin<TConfig>, definition: MiddlewareDefinition<TInput, TConfig, string | undefined>, input?: TInput): Promise<RunMiddlewareResult>;
|
|
45
60
|
/** Execute a handler's `handle()` with `this` bound to a capability context. */
|
|
46
61
|
export declare function runHandler<TConfig>(fake: FakePlugin<TConfig>, definition: HandlerDefinition, ...args: unknown[]): Promise<void>;
|
|
62
|
+
export interface RunToolOptions {
|
|
63
|
+
readonly locale?: Locale;
|
|
64
|
+
/**
|
|
65
|
+
* Derive `origin` (`kind: 'im'`) and `principal` from an inbound message, so a tool sees the same
|
|
66
|
+
* conversation the command tests use. Explicit `origin` / `principal` win.
|
|
67
|
+
*/
|
|
68
|
+
readonly message?: CommandMessage;
|
|
69
|
+
/** Default: `{ kind: 'im', platform: 'test', endpoint: 'test', scope: 'group', sceneId: 'test-group' }`. */
|
|
70
|
+
readonly origin?: ToolInvocationOrigin;
|
|
71
|
+
readonly principal?: ToolExecutionContext['principal'];
|
|
72
|
+
/** Default: no permissions, attended, network disabled. */
|
|
73
|
+
readonly policy?: Partial<ToolInvocationPolicy>;
|
|
74
|
+
readonly question?: ToolQuestionPort;
|
|
75
|
+
/** Native client port; required when the tool declares `adapter`. */
|
|
76
|
+
readonly client?: OperationClientPort;
|
|
77
|
+
readonly traceId?: string;
|
|
78
|
+
readonly turnId?: string;
|
|
79
|
+
readonly sessionKey?: string;
|
|
80
|
+
readonly signal?: AbortSignal;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Map an inbound message to the Agent tool origin shape (`platform` / `endpoint` / `scope` / `sceneId`),
|
|
84
|
+
* with the same rules as the Agent's IM turn context (`clientAdapter` wins over the endpoint adapter).
|
|
85
|
+
*/
|
|
86
|
+
export declare function toolOriginFromMessage(message: CommandMessage): ToolInvocationOrigin;
|
|
87
|
+
/** Build the context an Agent tool's `execute()` receives, shaped like the real ToolIndex builds it. */
|
|
88
|
+
export declare function createToolContext<TConfig, TAdapter extends string | undefined = undefined>(fake: FakePlugin<TConfig>, options?: RunToolOptions): ToolExecutionContext<TConfig, TAdapter>;
|
|
89
|
+
/**
|
|
90
|
+
* Execute an Agent tool against the fake, the way the Runtime's tool index does: the input is parsed
|
|
91
|
+
* by an executable `inputSchema` (e.g. Zod; JSON Schema inputs pass through unchanged, as in the
|
|
92
|
+
* Runtime), an `adapter`-bound tool requires a matching `client`, then `execute()` runs.
|
|
93
|
+
* It does NOT run model discovery, approval, permission or policy enforcement; use an Agent
|
|
94
|
+
* integration test for those.
|
|
95
|
+
*
|
|
96
|
+
* `tool` is a definition (e.g. the default export of `tools/<name>/index.ts`) or the local name of a
|
|
97
|
+
* tool registered through `addTool()` during `runPluginSetup`.
|
|
98
|
+
*/
|
|
99
|
+
export declare function runTool<TResult = unknown, TConfig = unknown, TInput = unknown>(fake: FakePlugin<TConfig>, tool: AgentToolDefinition<TInput, TResult, TConfig, string | undefined> | string, input?: unknown, options?: RunToolOptions): Promise<TResult>;
|
|
47
100
|
//# sourceMappingURL=fake-runners.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-runners.d.ts","sourceRoot":"","sources":["../src/fake-runners.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EACzE,MAAM,
|
|
1
|
+
{"version":3,"file":"fake-runners.d.ts","sourceRoot":"","sources":["../src/fake-runners.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EACV,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EACzE,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAkB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,KAAK,EACS,oBAAoB,EACxC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,KAAK,oBAAoB,EAC9E,KAAK,oBAAoB,EAAE,KAAK,gBAAgB,EACjD,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,6CAA6C;AAC7C,MAAM,WAAW,WAAY,SAAQ,cAAc;IACjD,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC;IACtG,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAC;IAChE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,eAAoB,GAAG,WAAW,CAczE;AAED,MAAM,WAAW,iBAAiB,CAAC,MAAM,SAAS,cAAc,GAAG,WAAW;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC;IACnE,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,WAAW,CAAC,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC;IACrD;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CAChC;AAED,sGAAsG;AACtG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,SAAS,cAAc,GAAG,WAAW,EACvF,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EACzB,OAAO,GAAE,iBAAiB,CAAC,MAAM,CAAM,GACtC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAoBrD;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,cAAc,GAAG,WAAW,EAC5F,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EACzB,UAAU,EAAE,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC3E,OAAO,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAClC,OAAO,CAAC,OAAO,CAAC,CAGlB;AAcD,MAAM,WAAW,mBAAmB;IAClC,kDAAkD;IAClD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,oGAAoG;AACpG,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAC/D,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EACzB,UAAU,EAAE,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC,EACrE,KAAK,GAAE,MAAsC,GAC5C,OAAO,CAAC,mBAAmB,CAAC,CAU9B;AAED,gFAAgF;AAChF,wBAAsB,UAAU,CAAC,OAAO,EACtC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EACzB,UAAU,EAAE,iBAAiB,EAC7B,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC,4GAA4G;IAC5G,QAAQ,CAAC,MAAM,CAAC,EAAE,oBAAoB,CAAC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,oBAAoB,CAAC,WAAW,CAAC,CAAC;IACvD,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IACrC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,GAAG,oBAAoB,CAWnF;AAED,wGAAwG;AACxG,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,QAAQ,SAAS,MAAM,GAAG,SAAS,GAAG,SAAS,EACxF,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EACzB,OAAO,GAAE,cAAmB,GAC3B,oBAAoB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAgCzC;AAED;;;;;;;;;GASG;AACH,wBAAsB,OAAO,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,MAAM,GAAG,OAAO,EAClF,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EACzB,IAAI,EAAE,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,EAChF,KAAK,CAAC,EAAE,OAAO,EACf,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CASlB"}
|
package/lib/fake-runners.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { resolveLocale } from 'zhin.js/i18n';
|
|
2
|
+
import { parseToolInputSchema, } from '@zhin.js/tool';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
1
4
|
export function createFakeMessage(init = {}) {
|
|
2
5
|
const replies = [];
|
|
3
6
|
const conversation = {
|
|
4
7
|
endpoint: { id: 'test', adapter: 'test' }, kind: 'group', id: 'test-group', ...init.conversation,
|
|
5
8
|
};
|
|
6
9
|
return {
|
|
7
|
-
conversation, content: init.content ?? '',
|
|
10
|
+
conversation, content: init.content ?? '', locale: resolveLocale(init.locale),
|
|
8
11
|
sender: init.sender ?? { id: 'test-user', name: 'Tester', roles: [] },
|
|
9
12
|
...(init.id === undefined ? {} : { id: init.id }),
|
|
10
13
|
...(init.metadata === undefined ? {} : { metadata: init.metadata }),
|
|
@@ -20,6 +23,8 @@ export function createCommandContext(fake, options = {}) {
|
|
|
20
23
|
const sender = input.sender;
|
|
21
24
|
return {
|
|
22
25
|
...fake.capabilityContext(),
|
|
26
|
+
locale: options.locale ?? resolveLocale(input.locale),
|
|
27
|
+
formatCommand: options.formatCommand ?? (() => undefined),
|
|
23
28
|
args: options.args ?? [],
|
|
24
29
|
params: options.params ?? {},
|
|
25
30
|
options: options.options ?? {},
|
|
@@ -36,9 +41,20 @@ export function createCommandContext(fake, options = {}) {
|
|
|
36
41
|
/**
|
|
37
42
|
* Execute a command definition against the fake. This calls `execute()` directly: it does NOT run
|
|
38
43
|
* route matching, parameter parsing, `permit`, or admission. Use `PluginTestKit` for those.
|
|
44
|
+
* Pass `source` to apply the Runtime's convention-path check first.
|
|
39
45
|
*/
|
|
40
46
|
export async function runCommand(fake, definition, options) {
|
|
41
|
-
|
|
47
|
+
const command = options?.source === undefined ? definition : await bindSource(definition, options.source);
|
|
48
|
+
return command.execute(createCommandContext(fake, options));
|
|
49
|
+
}
|
|
50
|
+
async function bindSource(definition, source) {
|
|
51
|
+
const path = source instanceof URL || source.startsWith('file:') ? fileURLToPath(source) : source;
|
|
52
|
+
// Loaded on demand, so an older zhin.js (unmet peer) only fails here, with an actionable message.
|
|
53
|
+
const command = await import('zhin.js/command');
|
|
54
|
+
if (typeof command.bindCommandSource !== 'function') {
|
|
55
|
+
throw new Error('runCommand({ source }) needs a newer zhin.js whose zhin.js/command exports bindCommandSource; upgrade zhin.js');
|
|
56
|
+
}
|
|
57
|
+
return command.bindCommandSource(definition, path).definition;
|
|
42
58
|
}
|
|
43
59
|
/** Execute an inbound or outbound middleware's `handle()`; `next` records whether it was called. */
|
|
44
60
|
export async function runMiddleware(fake, definition, input = createFakeMessage()) {
|
|
@@ -57,4 +73,83 @@ export async function runHandler(fake, definition, ...args) {
|
|
|
57
73
|
const context = fake.capabilityContext();
|
|
58
74
|
await definition.handle.call(context, ...args);
|
|
59
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Map an inbound message to the Agent tool origin shape (`platform` / `endpoint` / `scope` / `sceneId`),
|
|
78
|
+
* with the same rules as the Agent's IM turn context (`clientAdapter` wins over the endpoint adapter).
|
|
79
|
+
*/
|
|
80
|
+
export function toolOriginFromMessage(message) {
|
|
81
|
+
const { conversation } = message;
|
|
82
|
+
return Object.freeze({
|
|
83
|
+
kind: 'im',
|
|
84
|
+
platform: message.clientAdapter ?? conversation.endpoint.adapter,
|
|
85
|
+
endpoint: conversation.endpoint.id,
|
|
86
|
+
scope: conversation.kind,
|
|
87
|
+
sceneId: conversation.id,
|
|
88
|
+
...(message.id === undefined ? {} : { messageId: message.id }),
|
|
89
|
+
...(conversation.threadId === undefined ? {} : { threadId: conversation.threadId }),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/** Build the context an Agent tool's `execute()` receives, shaped like the real ToolIndex builds it. */
|
|
93
|
+
export function createToolContext(fake, options = {}) {
|
|
94
|
+
const message = options.message;
|
|
95
|
+
const sender = message?.sender;
|
|
96
|
+
const policy = options.policy ?? {};
|
|
97
|
+
const context = {
|
|
98
|
+
...fake.capabilityContext(),
|
|
99
|
+
locale: options.locale ?? resolveLocale(message?.locale),
|
|
100
|
+
signal: options.signal ?? fake.signal,
|
|
101
|
+
traceId: options.traceId ?? 'test-trace',
|
|
102
|
+
turnId: options.turnId ?? 'test-turn',
|
|
103
|
+
sessionKey: options.sessionKey ?? (message
|
|
104
|
+
? `${message.conversation.endpoint.adapter}:${message.conversation.endpoint.id}:${message.conversation.kind}:${message.conversation.id}`
|
|
105
|
+
: 'test-session'),
|
|
106
|
+
origin: Object.freeze({ ...(options.origin ?? (message ? toolOriginFromMessage(message) : {
|
|
107
|
+
kind: 'im', platform: 'test', endpoint: 'test', scope: 'group', sceneId: 'test-group',
|
|
108
|
+
})) }),
|
|
109
|
+
principal: Object.freeze({ ...(options.principal ?? {
|
|
110
|
+
subjectId: sender?.id ?? 'test-user',
|
|
111
|
+
...(sender?.name === undefined ? {} : { displayName: sender.name }),
|
|
112
|
+
roles: sender?.roles ?? [],
|
|
113
|
+
}) }),
|
|
114
|
+
policy: Object.freeze({
|
|
115
|
+
permissions: Object.freeze([...(policy.permissions ?? [])]),
|
|
116
|
+
unattended: policy.unattended ?? false,
|
|
117
|
+
network: Object.freeze({ enabled: false, ...policy.network }),
|
|
118
|
+
...(policy.shell ? { shell: Object.freeze({ ...policy.shell }) } : {}),
|
|
119
|
+
...(policy.filesystem ? { filesystem: Object.freeze({ ...policy.filesystem }) } : {}),
|
|
120
|
+
}),
|
|
121
|
+
...(options.question ? { question: options.question } : {}),
|
|
122
|
+
};
|
|
123
|
+
Object.defineProperty(context, '$client', { enumerable: true, get: () => options.client?.get() });
|
|
124
|
+
return Object.freeze(context);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Execute an Agent tool against the fake, the way the Runtime's tool index does: the input is parsed
|
|
128
|
+
* by an executable `inputSchema` (e.g. Zod; JSON Schema inputs pass through unchanged, as in the
|
|
129
|
+
* Runtime), an `adapter`-bound tool requires a matching `client`, then `execute()` runs.
|
|
130
|
+
* It does NOT run model discovery, approval, permission or policy enforcement; use an Agent
|
|
131
|
+
* integration test for those.
|
|
132
|
+
*
|
|
133
|
+
* `tool` is a definition (e.g. the default export of `tools/<name>/index.ts`) or the local name of a
|
|
134
|
+
* tool registered through `addTool()` during `runPluginSetup`.
|
|
135
|
+
*/
|
|
136
|
+
export async function runTool(fake, tool, input, options = {}) {
|
|
137
|
+
const definition = typeof tool === 'string' ? registeredTool(fake, tool) : tool;
|
|
138
|
+
const name = typeof tool === 'string' ? tool : 'tool';
|
|
139
|
+
const parsed = parseToolInputSchema(definition.inputSchema, input ?? {});
|
|
140
|
+
if (!parsed.ok)
|
|
141
|
+
throw new TypeError(`Invalid Agent Tool input for ${name}: ${parsed.error}`);
|
|
142
|
+
if (definition.adapter && options.client?.adapter !== definition.adapter) {
|
|
143
|
+
throw new Error(`Agent Tool ${name} requires adapter ${definition.adapter}`);
|
|
144
|
+
}
|
|
145
|
+
return await definition.execute(parsed.data, createToolContext(fake, options));
|
|
146
|
+
}
|
|
147
|
+
function registeredTool(fake, localName) {
|
|
148
|
+
const found = fake.features.find(feature => feature.kind === 'tool' && feature.localName === localName);
|
|
149
|
+
if (!found) {
|
|
150
|
+
const known = fake.features.filter(feature => feature.kind === 'tool').map(feature => feature.localName);
|
|
151
|
+
throw new Error(`No tool "${localName}" was registered through addTool()${known.length ? `; registered: ${known.join(', ')}` : ''}`);
|
|
152
|
+
}
|
|
153
|
+
return found.definition;
|
|
154
|
+
}
|
|
60
155
|
//# sourceMappingURL=fake-runners.js.map
|