@telorun/test 0.2.1 → 0.3.1

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 CHANGED
@@ -1,6 +1,21 @@
1
- # ⚡ Telo
2
-
3
- Runtime for declarative backends.
1
+ <p align="center">
2
+ <img src="./assets/telo.png" alt="Telo" width="200" />
3
+ </p>
4
+
5
+ <h1 align="center">Telo</h1>
6
+
7
+ <p align="center">Runtime for declarative backends.</p>
8
+
9
+ <p align="center">
10
+ <a href="https://github.com/telorun/telo/actions/workflows/test.yml"><img alt="Tests" src="https://github.com/telorun/telo/actions/workflows/test.yml/badge.svg" /></a>
11
+ <a href="https://www.npmjs.com/package/@telorun/cli"><img alt="node" src="https://img.shields.io/node/v/@telorun/cli" /></a>
12
+ <br />
13
+ <a href="https://github.com/telorun/telo/commits/main"><img alt="Last commit" src="https://img.shields.io/github/last-commit/telorun/telo" /></a>
14
+ <a href="https://github.com/telorun/telo/issues"><img alt="Issues" src="https://img.shields.io/github/issues/telorun/telo" /></a>
15
+ <a href="https://github.com/telorun/telo/pulls"><img alt="Pull requests" src="https://img.shields.io/github/issues-pr/telorun/telo" /></a>
16
+ <br />
17
+ <img alt="Changesets" src="https://img.shields.io/badge/maintained%20with-changesets-176de3" />
18
+ </p>
4
19
 
5
20
  Telo is an execution engine (Micro-Kernel) that runs logic defined entirely in YAML manifests. Instead of writing imperative backend code, you define your routes, databases, schemas, and AI workflows as atomic, interconnected YAML documents. Telo takes those manifests and runs them.
6
21
 
package/dist/suite.d.ts CHANGED
@@ -14,6 +14,7 @@ export declare const schema: import("@sinclair/typebox").TObject<{
14
14
  include: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
15
15
  exclude: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
16
16
  filter: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
17
+ concurrency: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
17
18
  }>;
18
19
  type SuiteManifest = Static<typeof schema>;
19
20
  export declare function create(manifest: SuiteManifest, ctx: ResourceContext): Promise<Runnable>;
package/dist/suite.js CHANGED
@@ -4,6 +4,7 @@ import * as fs from "fs";
4
4
  import * as path from "path";
5
5
  import { Writable } from "stream";
6
6
  import { fileURLToPath } from "url";
7
+ const DEFAULT_CONCURRENCY = 3;
7
8
  class BufferedWritable extends Writable {
8
9
  chunks = [];
9
10
  _write(chunk, _encoding, cb) {
@@ -24,6 +25,7 @@ export const schema = Type.Object({
24
25
  include: Type.Optional(Type.Array(Type.String())),
25
26
  exclude: Type.Optional(Type.Array(Type.String())),
26
27
  filter: Type.Optional(Type.String()),
28
+ concurrency: Type.Optional(Type.Integer({ minimum: 1 })),
27
29
  });
28
30
  function createColors(stream) {
29
31
  const useColor = stream.isTTY ?? false;
@@ -177,24 +179,36 @@ export async function create(manifest, ctx) {
177
179
  }
178
180
  const singleTest = tests.length === 1;
179
181
  const results = [];
180
- for (const testPath of tests) {
181
- const label = labelFor(testPath, baseDir);
182
- const result = await runOneTest(testPath, !singleTest, ctx.stdout, ctx.stderr);
183
- result.label = label;
184
- results.push(result);
185
- if (result.passed) {
186
- ctx.stdout.write(green("PASS") + " " + dim(label) + " " + dim(`(${result.durationMs}ms)`) + "\n");
187
- }
188
- else {
189
- ctx.stderr.write(red("FAIL") + " " + label + " " + dim(`(${result.durationMs}ms)`) + "\n");
190
- if (result.output) {
191
- ctx.stderr.write(result.output);
182
+ const requestedConcurrency = manifest.concurrency ?? DEFAULT_CONCURRENCY;
183
+ const concurrency = singleTest ? 1 : Math.max(1, Math.min(requestedConcurrency, tests.length));
184
+ let nextIdx = 0;
185
+ const worker = async () => {
186
+ while (true) {
187
+ const i = nextIdx++;
188
+ if (i >= tests.length)
189
+ return;
190
+ const testPath = tests[i];
191
+ const label = labelFor(testPath, baseDir);
192
+ const result = await runOneTest(testPath, !singleTest, ctx.stdout, ctx.stderr);
193
+ result.label = label;
194
+ results.push(result);
195
+ if (result.passed) {
196
+ ctx.stdout.write(green("PASS") + " " + dim(label) + " " + dim(`(${result.durationMs}ms)`) + "\n");
192
197
  }
193
- if (result.error) {
194
- ctx.stderr.write(dim(` ${result.error}`) + "\n");
198
+ else {
199
+ // Compose the whole FAIL block into one write so a parallel
200
+ // worker's output can't interleave between the header, captured
201
+ // output, and error line on stderr.
202
+ let block = red("FAIL") + " " + label + " " + dim(`(${result.durationMs}ms)`) + "\n";
203
+ if (result.output)
204
+ block += result.output;
205
+ if (result.error)
206
+ block += dim(` ${result.error}`) + "\n";
207
+ ctx.stderr.write(block);
195
208
  }
196
209
  }
197
- }
210
+ };
211
+ await Promise.all(Array.from({ length: concurrency }, () => worker()));
198
212
  const passed = results.filter((r) => r.passed);
199
213
  const failed = results.filter((r) => !r.passed);
200
214
  if (!singleTest) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/test",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Telo Test module - Test runner for Telo manifests.",
5
5
  "keywords": [
6
6
  "telo",
@@ -33,8 +33,8 @@
33
33
  ],
34
34
  "dependencies": {
35
35
  "@sinclair/typebox": "^0.34.48",
36
- "@telorun/sdk": "0.6.0",
37
- "@telorun/kernel": "0.6.1"
36
+ "@telorun/kernel": "0.7.1",
37
+ "@telorun/sdk": "0.7.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^20.0.0",
package/src/suite.ts CHANGED
@@ -6,6 +6,8 @@ import * as path from "path";
6
6
  import { Writable } from "stream";
7
7
  import { fileURLToPath } from "url";
8
8
 
9
+ const DEFAULT_CONCURRENCY = 3;
10
+
9
11
  class BufferedWritable extends Writable {
10
12
  private chunks: Buffer[] = [];
11
13
 
@@ -34,6 +36,7 @@ export const schema = Type.Object({
34
36
  Type.Array(Type.String()),
35
37
  ),
36
38
  filter: Type.Optional(Type.String()),
39
+ concurrency: Type.Optional(Type.Integer({ minimum: 1 })),
37
40
  });
38
41
 
39
42
  type SuiteManifest = Static<typeof schema>;
@@ -219,24 +222,35 @@ export async function create(
219
222
  const singleTest = tests.length === 1;
220
223
  const results: TestResult[] = [];
221
224
 
222
- for (const testPath of tests) {
223
- const label = labelFor(testPath, baseDir);
224
- const result = await runOneTest(testPath, !singleTest, ctx.stdout, ctx.stderr);
225
- result.label = label;
226
- results.push(result);
225
+ const requestedConcurrency = manifest.concurrency ?? DEFAULT_CONCURRENCY;
226
+ const concurrency = singleTest ? 1 : Math.max(1, Math.min(requestedConcurrency, tests.length));
227
227
 
228
- if (result.passed) {
229
- ctx.stdout.write(green("PASS") + " " + dim(label) + " " + dim(`(${result.durationMs}ms)`) + "\n");
230
- } else {
231
- ctx.stderr.write(red("FAIL") + " " + label + " " + dim(`(${result.durationMs}ms)`) + "\n");
232
- if (result.output) {
233
- ctx.stderr.write(result.output);
234
- }
235
- if (result.error) {
236
- ctx.stderr.write(dim(` ${result.error}`) + "\n");
228
+ let nextIdx = 0;
229
+ const worker = async () => {
230
+ while (true) {
231
+ const i = nextIdx++;
232
+ if (i >= tests.length) return;
233
+ const testPath = tests[i];
234
+ const label = labelFor(testPath, baseDir);
235
+ const result = await runOneTest(testPath, !singleTest, ctx.stdout, ctx.stderr);
236
+ result.label = label;
237
+ results.push(result);
238
+
239
+ if (result.passed) {
240
+ ctx.stdout.write(green("PASS") + " " + dim(label) + " " + dim(`(${result.durationMs}ms)`) + "\n");
241
+ } else {
242
+ // Compose the whole FAIL block into one write so a parallel
243
+ // worker's output can't interleave between the header, captured
244
+ // output, and error line on stderr.
245
+ let block = red("FAIL") + " " + label + " " + dim(`(${result.durationMs}ms)`) + "\n";
246
+ if (result.output) block += result.output;
247
+ if (result.error) block += dim(` ${result.error}`) + "\n";
248
+ ctx.stderr.write(block);
237
249
  }
238
250
  }
239
- }
251
+ };
252
+
253
+ await Promise.all(Array.from({ length: concurrency }, () => worker()));
240
254
 
241
255
  const passed = results.filter((r) => r.passed);
242
256
  const failed = results.filter((r) => !r.passed);