@matimo/bruno 0.1.0

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.
@@ -0,0 +1,69 @@
1
+ name: bruno_run_request
2
+ description: Execute a single request from a Bruno collection for targeted debugging and validation. Returns detailed request/response data and assertion results.
3
+ version: '1.0.0'
4
+ status: approved
5
+
6
+ parameters:
7
+ collection_path:
8
+ type: string
9
+ required: true
10
+ description: Path to Bruno collection directory
11
+
12
+ request_name:
13
+ type: string
14
+ required: true
15
+ description: Name of the request to execute (exact match)
16
+
17
+ environment:
18
+ type: string
19
+ required: false
20
+ description: Environment name to use (overrides collection default)
21
+
22
+ env_file:
23
+ type: string
24
+ required: false
25
+ description: Path to environment file to override variables
26
+
27
+ sandbox_mode:
28
+ type: string
29
+ required: false
30
+ default: safe
31
+ description: JavaScript execution mode - 'safe' or 'developer'
32
+
33
+ execution:
34
+ type: function
35
+ code: index.ts
36
+ timeout: 60000
37
+
38
+ output_schema:
39
+ type: object
40
+ properties:
41
+ success:
42
+ type: boolean
43
+ request:
44
+ type: string
45
+ description: Name of the request that was executed
46
+ status:
47
+ type: number
48
+ description: HTTP status code parsed from bru output
49
+ duration_ms:
50
+ type: number
51
+ description: Total execution time in milliseconds
52
+ errors:
53
+ type: array
54
+ items:
55
+ type: string
56
+
57
+
58
+ examples:
59
+ - name: "Debug single authentication request"
60
+ params:
61
+ collection_path: "./collections/auth"
62
+ request_name: "Login"
63
+ environment: "staging"
64
+
65
+ - name: "Test specific endpoint with custom environment"
66
+ params:
67
+ collection_path: "./collections/api"
68
+ request_name: "Get User Profile"
69
+ env_file: "./envs/test.json"
@@ -0,0 +1,104 @@
1
+ import { getGlobalMatimoLogger } from '@matimo/core';
2
+ import { execFileSync } from 'child_process';
3
+ import { promises as fs } from 'fs';
4
+ import * as path from 'path';
5
+ import { checkBruVersion } from '../bru-utils';
6
+
7
+ const logger = getGlobalMatimoLogger();
8
+
9
+ /** Search recursively for a .bru file matching the given slug, return path relative to root. */
10
+ async function findBruFile(root: string, slug: string): Promise<string | null> {
11
+ let entries: string[];
12
+ try {
13
+ entries = await fs.readdir(root);
14
+ } catch {
15
+ return null;
16
+ }
17
+ if (entries.includes(`${slug}.bru`)) {
18
+ return `${slug}.bru`;
19
+ }
20
+ for (const entry of entries) {
21
+ const fullPath = path.join(root, entry);
22
+ try {
23
+ const stat = await fs.stat(fullPath);
24
+ if (stat.isDirectory() && entry !== 'node_modules') {
25
+ const found = await findBruFile(fullPath, slug);
26
+ if (found) return path.join(entry, found);
27
+ }
28
+ } catch {
29
+ // skip
30
+ }
31
+ }
32
+ return null;
33
+ }
34
+
35
+ export default async function execute(params: Record<string, unknown>): Promise<unknown> {
36
+ const collectionPath = params.collection_path as string;
37
+ const requestName = params.request_name as string;
38
+
39
+ if (!collectionPath || !requestName) {
40
+ return {
41
+ success: false,
42
+ request: requestName ?? '',
43
+ status: 0,
44
+ duration_ms: 0,
45
+ errors: ['collection_path and request_name parameters are required'],
46
+ };
47
+ }
48
+
49
+ checkBruVersion();
50
+
51
+ const absolutePath = path.resolve(collectionPath);
52
+ const slug = requestName.toLowerCase().replace(/\s+/g, '-');
53
+
54
+ try {
55
+ logger.info(`Running request: ${requestName} from ${absolutePath}`);
56
+
57
+ // Locate the .bru file (may be in a requests/ subfolder)
58
+ const bruRelPath = (await findBruFile(absolutePath, slug)) ?? `${slug}.bru`;
59
+
60
+ const args: string[] = ['run', bruRelPath];
61
+
62
+ if (params.environment) args.push('--env', params.environment as string);
63
+ if (params.env_file) args.push('--env-file', params.env_file as string);
64
+ args.push('--sandbox', (params.sandbox_mode as string) || 'safe');
65
+
66
+ logger.debug(`Executing: bru ${args.join(' ')}`);
67
+
68
+ const start = Date.now();
69
+ let success = true;
70
+ let output = '';
71
+ try {
72
+ output = execFileSync('bru', args, { encoding: 'utf-8', stdio: 'pipe', cwd: absolutePath });
73
+ } catch (execError) {
74
+ success = false;
75
+ // Extract stdout/stderr from the error so assertion failures are visible
76
+ const err = execError as NodeJS.ErrnoException & { stdout?: string; stderr?: string };
77
+ output = [err.stdout, err.stderr, err.message].filter(Boolean).join('\n');
78
+ }
79
+ const durationMs = Date.now() - start;
80
+
81
+ // Parse status code from bru run output (e.g. "200 OK")
82
+ const statusMatch = output.match(/\b([1-5]\d{2})\b/);
83
+ const status = statusMatch ? parseInt(statusMatch[1], 10) : (success ? 200 : 0);
84
+
85
+ return {
86
+ success,
87
+ request: requestName,
88
+ status,
89
+ duration_ms: durationMs,
90
+ errors: success ? [] : [output],
91
+ };
92
+ } catch (error) {
93
+ logger.error(
94
+ `Request execution failed: ${error instanceof Error ? error.message : String(error)}`
95
+ );
96
+ return {
97
+ success: false,
98
+ request: requestName,
99
+ status: 0,
100
+ duration_ms: 0,
101
+ errors: [error instanceof Error ? error.message : String(error)],
102
+ };
103
+ }
104
+ }