@akterjs/conformance 0.1.1 → 0.1.3
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 +1 -1
- package/dist/guarantee-titles.pure.d.mts +1 -1
- package/dist/guarantee-titles.pure.mjs +1 -1
- package/dist/workbench/computer-conformance.suite.mjs +10 -33
- package/dist/workbench/workbench.actor.mjs +3 -7
- package/package.json +7 -7
- package/src/guarantee-titles.pure.ts +2 -2
- package/src/workbench/computer-conformance.suite.ts +11 -22
- package/src/workbench/workbench.actor.ts +4 -11
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ The computer guarantees are a second suite, `computerConformanceOf({ engine, ski
|
|
|
29
29
|
- a computer step runs once by its key: sent again, even after the engine restarts, it reads back the outcome it recorded
|
|
30
30
|
- a computer keeps its files across pause, resume, stop and an engine restart
|
|
31
31
|
- a held computer stays paused, or stopped if it was stopped: an ensure, a step, and a resume or pause without its hold are refused until its hold resumes it
|
|
32
|
-
-
|
|
32
|
+
- an actor keeps as many computers as it ensures: five all start and are listed, and all five remove
|
|
33
33
|
- a removed computer is gone with its disk: calls to it fail with ComputerNotFoundError, and ensuring it again starts it empty
|
|
34
34
|
|
|
35
35
|
The fork guarantees are a third suite, `forkConformanceOf({ engine, skip })` from `@akterjs/conformance/fork`, which forks the suite's `acct-1` Ledger into `acct-5`, and its `bench-1` Workbench, with a running and a stopped computer, into `bench-250`, through `ActorHost.fork`. An engine under test serves all four keys, with the Workbench's computers bound both to its jobs and to its fork, and an engine that places an actor by its key puts each fork's key on its parent's shard, because Akter Cloud forks only within a shard (`acct-5` and `bench-250` hash to their parents' shards there). The local engine runs it with `AKTER_LOCAL_COMPUTERS=true` in `tests/engine-conformance.suite.test.ts`.
|
|
@@ -17,7 +17,7 @@ declare const guaranteeTitles: {
|
|
|
17
17
|
declare const computerGuaranteeTitles: {
|
|
18
18
|
readonly computerHeldStaysPaused: "a held computer stays paused, or stopped if it was stopped: an ensure, a step, and a resume or pause without its hold are refused until its hold resumes it";
|
|
19
19
|
readonly computerKeepsFiles: "a computer keeps its files across pause, resume, stop and an engine restart";
|
|
20
|
-
readonly
|
|
20
|
+
readonly computerManyKept: "an actor keeps as many computers as it ensures: five all start and are listed, and all five remove";
|
|
21
21
|
readonly computerRemovedIsGone: "a removed computer is gone with its disk: calls to it fail with ComputerNotFoundError, and ensuring it again starts it empty";
|
|
22
22
|
readonly computerStepJournaled: "a durable computer step journals its start and outcome in the actor, and its steps read back newest first, one with its output";
|
|
23
23
|
readonly computerStepRunsOnce: "a computer step runs once by its key: sent again, even after the engine restarts, it reads back the outcome it recorded";
|
|
@@ -18,7 +18,7 @@ const guaranteeTitles = {
|
|
|
18
18
|
const computerGuaranteeTitles = {
|
|
19
19
|
computerHeldStaysPaused: "a held computer stays paused, or stopped if it was stopped: an ensure, a step, and a resume or pause without its hold are refused until its hold resumes it",
|
|
20
20
|
computerKeepsFiles: "a computer keeps its files across pause, resume, stop and an engine restart",
|
|
21
|
-
|
|
21
|
+
computerManyKept: "an actor keeps as many computers as it ensures: five all start and are listed, and all five remove",
|
|
22
22
|
computerRemovedIsGone: "a removed computer is gone with its disk: calls to it fail with ComputerNotFoundError, and ensuring it again starts it empty",
|
|
23
23
|
computerStepJournaled: "a durable computer step journals its start and outcome in the actor, and its steps read back newest first, one with its output",
|
|
24
24
|
computerStepRunsOnce: "a computer step runs once by its key: sent again, even after the engine restarts, it reads back the outcome it recorded",
|
|
@@ -132,41 +132,18 @@ const heldStaysPaused = (storage) => onBench(storage, (host) => Effect.gen(funct
|
|
|
132
132
|
]);
|
|
133
133
|
yield* stoppedStaysStopped(host);
|
|
134
134
|
}));
|
|
135
|
-
const
|
|
136
|
-
const none = 0;
|
|
137
|
-
const first = 1;
|
|
138
|
-
/** `value` `count` times, for a count from none to `tried`. */
|
|
139
|
-
const times = (value, count) => Array.take(Array.replicate(value, tried), count);
|
|
135
|
+
const many = 5;
|
|
140
136
|
const listed = (names) => `[${Array.join(Array.sort(names, String$1.Order), ",")}]`;
|
|
141
|
-
const
|
|
142
|
-
const names = Array.makeBy(
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const refused = `ComputerLimitError store ${String(made)}`;
|
|
146
|
-
const kept = Array.take(names, made);
|
|
147
|
-
const freed = yield* ran(host, "l-2", [
|
|
148
|
-
remove("c0"),
|
|
149
|
-
ensure("again"),
|
|
150
|
-
ensure("more"),
|
|
151
|
-
list
|
|
152
|
-
]);
|
|
137
|
+
const manyKept = (storage) => onBench(storage, (host) => Effect.gen(function* ensureMany() {
|
|
138
|
+
const names = Array.makeBy(many, (index) => `c${String(index)}`);
|
|
139
|
+
const kept = yield* ran(host, "m-1", [...Array.map(names, ensure), list]);
|
|
140
|
+
const removed = yield* ran(host, "m-2", [...Array.map(names, remove), list]);
|
|
153
141
|
expect({
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
made: made > none && made < tried
|
|
142
|
+
kept,
|
|
143
|
+
removed
|
|
157
144
|
}).toStrictEqual({
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
...times(refused, tried - made),
|
|
161
|
-
listed(kept)
|
|
162
|
-
],
|
|
163
|
-
freed: [
|
|
164
|
-
"removed",
|
|
165
|
-
"up",
|
|
166
|
-
refused,
|
|
167
|
-
listed([...Array.drop(kept, first), "again"])
|
|
168
|
-
],
|
|
169
|
-
made: true
|
|
145
|
+
kept: [...Array.replicate("up", many), listed(names)],
|
|
146
|
+
removed: [...Array.replicate("removed", many), listed([])]
|
|
170
147
|
});
|
|
171
148
|
}));
|
|
172
149
|
const removedIsGone = (storage) => onBench(storage, (host) => Effect.gen(function* removeThenEnsure() {
|
|
@@ -301,7 +278,7 @@ const stepSurvivesRestart = (storage) => Effect.gen(function* cutThenReplay() {
|
|
|
301
278
|
const computerGuarantees = {
|
|
302
279
|
computerHeldStaysPaused: heldStaysPaused,
|
|
303
280
|
computerKeepsFiles: keepsFiles,
|
|
304
|
-
|
|
281
|
+
computerManyKept: manyKept,
|
|
305
282
|
computerRemovedIsGone: removedIsGone,
|
|
306
283
|
computerStepJournaled: stepJournaled,
|
|
307
284
|
computerStepRunsOnce: stepRunsOnce,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ComputerOp, Workbench } from "./workbench.contract.mjs";
|
|
2
|
-
import { ComputerLimitError } from "@akterjs/effect/contract/errors";
|
|
3
2
|
import { Array, Cause, Effect, Exit, Layer, Option, Predicate, Record, Schema, String as String$1 } from "effect";
|
|
4
3
|
import { Akter } from "@akterjs/effect/contract";
|
|
5
4
|
import { Akter as Akter$1 } from "@akterjs/effect";
|
|
@@ -12,6 +11,7 @@ const failures = [
|
|
|
12
11
|
"ActorUnavailableError",
|
|
13
12
|
"CallerNotAllowedError",
|
|
14
13
|
"ComputerHeldError",
|
|
14
|
+
"ComputerLimitError",
|
|
15
15
|
"ComputerLostError",
|
|
16
16
|
"ComputerNotFoundError",
|
|
17
17
|
"ComputersUnavailableError",
|
|
@@ -19,7 +19,6 @@ const failures = [
|
|
|
19
19
|
"SecretUnavailableError",
|
|
20
20
|
"UnauthenticatedError"
|
|
21
21
|
];
|
|
22
|
-
const isLimit = Schema.is(ComputerLimitError);
|
|
23
22
|
const isRan = Schema.is(ExecRan);
|
|
24
23
|
const specOf = (computer) => ({
|
|
25
24
|
egress: [],
|
|
@@ -57,11 +56,8 @@ const called = (client, op) => ComputerOp.match(op, {
|
|
|
57
56
|
Step: () => Effect.die("a durable step runs in the job, not in an activity"),
|
|
58
57
|
Stop: ({ computer }) => Effect.map(client.stop(computer), stateOf)
|
|
59
58
|
});
|
|
60
|
-
/** A failure by its tag
|
|
61
|
-
const failureOf = (failure) =>
|
|
62
|
-
if (isLimit(failure)) return `ComputerLimitError ${failure.limit} ${String(failure.maximum)}`;
|
|
63
|
-
return Option.getOrElse(Array.findFirst(failures, (tag) => Predicate.isTagged(failure, tag)), () => "unexpected failure");
|
|
64
|
-
};
|
|
59
|
+
/** A failure by its tag. */
|
|
60
|
+
const failureOf = (failure) => Option.getOrElse(Array.findFirst(failures, (tag) => Predicate.isTagged(failure, tag)), () => "unexpected failure");
|
|
65
61
|
const attempted = (key, op) => Effect.flatMap(Workbench.computer(key), (client) => called(client, op));
|
|
66
62
|
const answered = (key, op) => Effect.match(attempted(key, op), {
|
|
67
63
|
onFailure: failureOf,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akterjs/conformance",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The test suite every Akter engine must pass: the local engine here and the production engine on Akter Cloud.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"provenance": true
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@akterjs/client": "0.1.
|
|
52
|
-
"@akterjs/effect": "0.1.
|
|
53
|
-
"@akterjs/sdk": "0.1.
|
|
51
|
+
"@akterjs/client": "0.1.3",
|
|
52
|
+
"@akterjs/effect": "0.1.3",
|
|
53
|
+
"@akterjs/sdk": "0.1.3",
|
|
54
54
|
"@effect/platform-node": "4.1.2",
|
|
55
55
|
"@effect/vitest": "4.1.2",
|
|
56
56
|
"effect": "4.1.2",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"zod": "4.6.7"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@akterjs/client": "0.1.
|
|
64
|
-
"@akterjs/effect": "0.1.
|
|
65
|
-
"@akterjs/sdk": "0.1.
|
|
63
|
+
"@akterjs/client": "0.1.3",
|
|
64
|
+
"@akterjs/effect": "0.1.3",
|
|
65
|
+
"@akterjs/sdk": "0.1.3",
|
|
66
66
|
"@effect/vitest": "4.1.2",
|
|
67
67
|
"effect": "4.1.2",
|
|
68
68
|
"vitest": "5.1.0",
|
|
@@ -27,8 +27,8 @@ const computerGuaranteeTitles = {
|
|
|
27
27
|
computerHeldStaysPaused:
|
|
28
28
|
"a held computer stays paused, or stopped if it was stopped: an ensure, a step, and a resume or pause without its hold are refused until its hold resumes it",
|
|
29
29
|
computerKeepsFiles: "a computer keeps its files across pause, resume, stop and an engine restart",
|
|
30
|
-
|
|
31
|
-
"
|
|
30
|
+
computerManyKept:
|
|
31
|
+
"an actor keeps as many computers as it ensures: five all start and are listed, and all five remove",
|
|
32
32
|
computerRemovedIsGone:
|
|
33
33
|
"a removed computer is gone with its disk: calls to it fail with ComputerNotFoundError, and ensuring it again starts it empty",
|
|
34
34
|
computerStepJournaled:
|
|
@@ -161,31 +161,20 @@ const heldStaysPaused = <Failure>(storage: Storage<Failure>): Checked<Failure> =
|
|
|
161
161
|
}),
|
|
162
162
|
);
|
|
163
163
|
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
const none = 0;
|
|
167
|
-
|
|
168
|
-
const first = 1;
|
|
169
|
-
|
|
170
|
-
/** `value` `count` times, for a count from none to `tried`. */
|
|
171
|
-
const times = (value: string, count: number): readonly string[] => Arr.take(Arr.replicate(value, tried), count);
|
|
164
|
+
const many = 5;
|
|
172
165
|
|
|
173
166
|
const listed = (names: readonly string[]): string => `[${Arr.join(Arr.sort(names, Str.Order), ",")}]`;
|
|
174
167
|
|
|
175
|
-
const
|
|
168
|
+
const manyKept = <Failure>(storage: Storage<Failure>): Checked<Failure> =>
|
|
176
169
|
onBench(storage, (host) =>
|
|
177
|
-
Effect.gen(function*
|
|
178
|
-
const names = Arr.makeBy(
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
expect({ filled, freed, made: made > none && made < tried }).toStrictEqual({
|
|
186
|
-
filled: [...times("up", made), ...times(refused, tried - made), listed(kept)],
|
|
187
|
-
freed: ["removed", "up", refused, listed([...Arr.drop(kept, first), "again"])],
|
|
188
|
-
made: true,
|
|
170
|
+
Effect.gen(function* ensureMany() {
|
|
171
|
+
const names = Arr.makeBy(many, (index) => `c${String(index)}`);
|
|
172
|
+
const kept = yield* ran(host, "m-1", [...Arr.map(names, ensure), list]);
|
|
173
|
+
const removed = yield* ran(host, "m-2", [...Arr.map(names, remove), list]);
|
|
174
|
+
|
|
175
|
+
expect({ kept, removed }).toStrictEqual({
|
|
176
|
+
kept: [...Arr.replicate("up", many), listed(names)],
|
|
177
|
+
removed: [...Arr.replicate("removed", many), listed([])],
|
|
189
178
|
});
|
|
190
179
|
}),
|
|
191
180
|
);
|
|
@@ -332,7 +321,7 @@ type Guarantee = <Failure>(storage: Storage<Failure>) => Checked<Failure>;
|
|
|
332
321
|
const computerGuarantees = {
|
|
333
322
|
computerHeldStaysPaused: heldStaysPaused,
|
|
334
323
|
computerKeepsFiles: keepsFiles,
|
|
335
|
-
|
|
324
|
+
computerManyKept: manyKept,
|
|
336
325
|
computerRemovedIsGone: removedIsGone,
|
|
337
326
|
computerStepJournaled: stepJournaled,
|
|
338
327
|
computerStepRunsOnce: stepRunsOnce,
|
|
@@ -3,7 +3,6 @@ import { Akter } from "@akterjs/effect/contract";
|
|
|
3
3
|
import { ComputerName, ExecRan } from "@akterjs/effect/contract/computer";
|
|
4
4
|
import type { Computer, ComputerSpec, ExecResult } from "@akterjs/effect/contract/computer";
|
|
5
5
|
import type { ActorComputerClients } from "@akterjs/effect/contract/computer-clients";
|
|
6
|
-
import { ComputerLimitError } from "@akterjs/effect/contract/errors";
|
|
7
6
|
import {
|
|
8
7
|
Array as Arr,
|
|
9
8
|
Cause,
|
|
@@ -36,6 +35,7 @@ const failures = [
|
|
|
36
35
|
"ActorUnavailableError",
|
|
37
36
|
"CallerNotAllowedError",
|
|
38
37
|
"ComputerHeldError",
|
|
38
|
+
"ComputerLimitError",
|
|
39
39
|
"ComputerLostError",
|
|
40
40
|
"ComputerNotFoundError",
|
|
41
41
|
"ComputersUnavailableError",
|
|
@@ -44,8 +44,6 @@ const failures = [
|
|
|
44
44
|
"UnauthenticatedError",
|
|
45
45
|
] as const;
|
|
46
46
|
|
|
47
|
-
const isLimit = Schema.is(ComputerLimitError);
|
|
48
|
-
|
|
49
47
|
const isRan = Schema.is(ExecRan);
|
|
50
48
|
|
|
51
49
|
const specOf = (computer: string): ComputerSpec => ({
|
|
@@ -102,17 +100,12 @@ const called = (client: ComputerClient, op: ComputerOp): Effect.Effect<string, C
|
|
|
102
100
|
Stop: ({ computer }) => Effect.map(client.stop(computer), stateOf),
|
|
103
101
|
});
|
|
104
102
|
|
|
105
|
-
/** A failure by its tag
|
|
106
|
-
const failureOf = (failure: Failed): string =>
|
|
107
|
-
|
|
108
|
-
return `ComputerLimitError ${failure.limit} ${String(failure.maximum)}`;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return Option.getOrElse(
|
|
103
|
+
/** A failure by its tag. */
|
|
104
|
+
const failureOf = (failure: Failed): string =>
|
|
105
|
+
Option.getOrElse(
|
|
112
106
|
Arr.findFirst(failures, (tag) => Predicate.isTagged(failure, tag)),
|
|
113
107
|
() => "unexpected failure",
|
|
114
108
|
);
|
|
115
|
-
};
|
|
116
109
|
|
|
117
110
|
const attempted = (key: string, op: ComputerOp): Effect.Effect<string, Failed> =>
|
|
118
111
|
Effect.flatMap(Workbench.computer(key), (client) => called(client, op));
|