mmt-testlight 0.4.3 → 0.4.5
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/cli.js +26899 -9530
- package/dist/guides/agent-workflow.md +75 -0
- package/dist/guides/general.md +64 -0
- package/dist/guides/generate-api.md +221 -0
- package/dist/guides/generate-doc.md +103 -0
- package/dist/guides/generate-env.md +147 -0
- package/dist/guides/generate-loadtest.md +55 -0
- package/dist/guides/generate-suite.md +167 -0
- package/dist/guides/generate-test-skill.md +60 -0
- package/dist/guides/generate-test.md +335 -0
- package/dist/guides/generate.md +60 -0
- package/dist/guides/golden-smoke.md +52 -0
- package/dist/guides/min/api.md +29 -0
- package/dist/guides/min/constraints.md +9 -0
- package/dist/guides/min/doc.md +17 -0
- package/dist/guides/min/env.md +26 -0
- package/dist/guides/min/loadtest.md +17 -0
- package/dist/guides/min/overview.md +25 -0
- package/dist/guides/min/suite.md +18 -0
- package/dist/guides/min/test.md +42 -0
- package/dist/guides/min/workflow.md +16 -0
- package/dist/guides/offline-agent.md +46 -0
- package/esbuild.mjs +25 -0
- package/package.json +2 -1
- package/src/aiDocs.ts +88 -0
- package/src/cli.ts +315 -24
- package/src/pathNormalize.cjs +16 -0
- package/src/pathNormalize.test.ts +8 -0
- package/src/pkg-entry.cjs +75 -40
- package/src/runArgs.ts +4 -4
- package/src/selfUpdate.test.ts +79 -0
- package/src/selfUpdate.ts +471 -0
- package/src/validateMmt.ts +45 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
This file tells the AI how to generate **`type: suite`** `.mmt` files.
|
|
2
|
+
|
|
3
|
+
Always follow these rules:
|
|
4
|
+
- Output must be valid YAML.
|
|
5
|
+
- The first non-comment line must be `type: suite`.
|
|
6
|
+
- Suites should primarily **list other mmt files** (tests, APIs, or other suites) to be executed.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Schema (mental model for the AI)
|
|
11
|
+
|
|
12
|
+
These fields match `SuiteData` in `core/src/SuiteData.ts` and the public docs in `docs/files/suite/index.md`.
|
|
13
|
+
|
|
14
|
+
Top-level keys and types:
|
|
15
|
+
|
|
16
|
+
```yaml
|
|
17
|
+
type: suite # REQUIRED, must be exactly "suite"
|
|
18
|
+
|
|
19
|
+
title: string # optional, human-readable suite name
|
|
20
|
+
tags: # optional, for grouping
|
|
21
|
+
- string
|
|
22
|
+
|
|
23
|
+
description: string # optional, short explanation
|
|
24
|
+
|
|
25
|
+
servers: # optional, root-only: mock servers started before items
|
|
26
|
+
- path/to/server.mmt # type: server files; kept running for the entire suite
|
|
27
|
+
|
|
28
|
+
items: # REQUIRED, array of files to run
|
|
29
|
+
- path/to/file1.mmt
|
|
30
|
+
- path/to/file2.mmt
|
|
31
|
+
- then
|
|
32
|
+
- path/to/file3.mmt
|
|
33
|
+
|
|
34
|
+
environment: # optional, root-only: configure env for suite runs
|
|
35
|
+
preset?: string # preset name from multimeter.mmt (or from file)
|
|
36
|
+
file?: string # path to an env file to load
|
|
37
|
+
variables?: # inline key-value environment variables
|
|
38
|
+
<name>: <value>
|
|
39
|
+
|
|
40
|
+
export: # optional, root-only: generate reports after completion
|
|
41
|
+
- path/to/report.xml # supported: .xml (JUnit), .html, .md, .mmt
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> **Root-only fields**: `servers`, `environment`, and `export` only take effect when the suite is run directly. When imported by another suite, these fields are ignored.
|
|
45
|
+
|
|
46
|
+
> **Legacy alias**: `tests` is still accepted as an alias for `items`, but new suites should use `items`.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Execution Flow (`items` array)
|
|
51
|
+
|
|
52
|
+
The `items` array defines the execution flow.
|
|
53
|
+
- All files listed between `then` separators (or before the first one) are run in parallel.
|
|
54
|
+
- The groups of files separated by `then` are run sequentially.
|
|
55
|
+
- **Server files** (`type: server`) can be included — they start before items in the same stage and stop when the suite completes.
|
|
56
|
+
|
|
57
|
+
Example: `[a, b, then, c]` will run `a` and `b` in parallel, and once both are finished, it will run `c`.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Common patterns the AI should generate
|
|
62
|
+
|
|
63
|
+
### 1. Simple suite running all items in parallel
|
|
64
|
+
|
|
65
|
+
User asks: "Create a suite to run smoke tests `test1.mmt` and `test2.mmt`."
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
type: suite
|
|
69
|
+
title: Smoke Tests
|
|
70
|
+
tags: [smoke]
|
|
71
|
+
items:
|
|
72
|
+
- ./tests/test1.mmt
|
|
73
|
+
- ./tests/test2.mmt
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 2. Sequential execution of items
|
|
77
|
+
|
|
78
|
+
User asks: "Create a suite that first runs `login.mmt`, and then `get_user.mmt`."
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
type: suite
|
|
82
|
+
title: Login and Get User
|
|
83
|
+
tags: [smoke, auth]
|
|
84
|
+
items:
|
|
85
|
+
- ./tests/login.mmt
|
|
86
|
+
- then
|
|
87
|
+
- ./tests/get_user.mmt
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Mixed parallel and sequential execution
|
|
91
|
+
|
|
92
|
+
User asks: "Create a suite that runs `test1.mmt` and `test2.mmt` in parallel, and after they are done, runs `test3.mmt`."
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
type: suite
|
|
96
|
+
title: Mixed Execution Suite
|
|
97
|
+
tags: [regression]
|
|
98
|
+
items:
|
|
99
|
+
- ./tests/test1.mmt
|
|
100
|
+
- ./tests/test2.mmt
|
|
101
|
+
- then
|
|
102
|
+
- ./tests/test3.mmt
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 4. Suite with mock servers
|
|
106
|
+
|
|
107
|
+
User asks: "Create a suite that starts a mock server before running integration tests."
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
type: suite
|
|
111
|
+
title: Integration with Mock Server
|
|
112
|
+
tags: [integration]
|
|
113
|
+
items:
|
|
114
|
+
- ./mocks/user-service.mmt # type: server — starts first
|
|
115
|
+
- then
|
|
116
|
+
- ./tests/user_crud.mmt
|
|
117
|
+
- ./tests/user_auth.mmt
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Servers start before items in the same stage and stop when the suite finishes.
|
|
121
|
+
|
|
122
|
+
### 5. Suite with top-level servers and environment
|
|
123
|
+
|
|
124
|
+
User asks: "Create an integration suite with a mock server, using the staging preset."
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
type: suite
|
|
128
|
+
title: Integration Suite
|
|
129
|
+
tags: [integration]
|
|
130
|
+
servers:
|
|
131
|
+
- ./mocks/user-service.mmt
|
|
132
|
+
- ./mocks/auth-service.mmt
|
|
133
|
+
environment:
|
|
134
|
+
preset: staging
|
|
135
|
+
items:
|
|
136
|
+
- ./tests/login.mmt
|
|
137
|
+
- ./tests/profile.mmt
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Using the `servers` field is recommended over placing servers in `items` — it keeps them running for the entire suite and avoids ordering issues.
|
|
141
|
+
|
|
142
|
+
### 6. Suite with report exports
|
|
143
|
+
|
|
144
|
+
User asks: "Create a CI suite that exports JUnit XML and HTML reports."
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
type: suite
|
|
148
|
+
title: CI Suite
|
|
149
|
+
export:
|
|
150
|
+
- ./reports/results.xml
|
|
151
|
+
- ./reports/results.html
|
|
152
|
+
items:
|
|
153
|
+
- ./tests/login.mmt
|
|
154
|
+
- ./tests/profile.mmt
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Style rules for the AI
|
|
160
|
+
|
|
161
|
+
- Use 2-space indentation.
|
|
162
|
+
- Prefer clear and descriptive titles.
|
|
163
|
+
- Always include a `title`.
|
|
164
|
+
- Use `tags` to categorize suites where appropriate.
|
|
165
|
+
- Ensure file paths in the `items` array are plausible.
|
|
166
|
+
|
|
167
|
+
When unsure, generate a **minimal valid suite** that includes the requested files.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Auto Test Generation Skill
|
|
2
|
+
|
|
3
|
+
Use this skill when the user wants a Multimeter test generated from an API file, an OpenAPI/Postman spec, or a natural-language scenario.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Produce a valid Multimeter `type: test` YAML file that is deterministic, minimal, and easy to validate — with **low token cost**.
|
|
8
|
+
|
|
9
|
+
## Required workflow (API → test)
|
|
10
|
+
|
|
11
|
+
1. Optional few-shot: `list_examples` → mirror **`goldenSmoke`** (`examples/ai/golden_smoke/`).
|
|
12
|
+
2. Identify the target API `.mmt` path (`discover_api` / `api_card` only if needed).
|
|
13
|
+
3. Call MCP **`scaffold_test({ workspaceRoot, apiPath })`** — **required**. Do not invent a blank test.
|
|
14
|
+
- Offline: `testlight scaffold test --from <api.mmt>`
|
|
15
|
+
4. Write the returned YAML to `suggestedPath` (or the user path).
|
|
16
|
+
5. Apply **only minimal** edits (title, expects, inputs). Prefer smoke unless asked for more.
|
|
17
|
+
6. **`validate` until valid, then `format`** — both required before finishing.
|
|
18
|
+
7. Stop — no polish rewrite.
|
|
19
|
+
8. If the user wants stronger asserts after a run: **`suggest_assertions` → patch → validate → format**.
|
|
20
|
+
|
|
21
|
+
## Modify discipline
|
|
22
|
+
|
|
23
|
+
- **Patch only.** Never replace the whole file unless the user explicitly says rewrite/regenerate.
|
|
24
|
+
- Prefer surgical edits (one step, one expect, one input).
|
|
25
|
+
|
|
26
|
+
## Rules
|
|
27
|
+
|
|
28
|
+
- Output only valid YAML for the generated test.
|
|
29
|
+
- Start the file with `type: test`.
|
|
30
|
+
- Never add YAML comments (`#`).
|
|
31
|
+
- Never web-search Multimeter syntax.
|
|
32
|
+
- Use tokens such as `e:`, `i:`, `r:`, and `c:` when appropriate.
|
|
33
|
+
- If the source is ambiguous, ask a short clarifying question before generating.
|
|
34
|
+
|
|
35
|
+
## Golden smoke shape (mirror this)
|
|
36
|
+
|
|
37
|
+
See `docs/AI/golden-smoke.md` / `list_examples.goldenSmoke`. Scaffold already produces:
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
type: test
|
|
41
|
+
title: <API title> smoke test
|
|
42
|
+
tags: [smoke, ...]
|
|
43
|
+
import:
|
|
44
|
+
<alias>: <relative-api-path>
|
|
45
|
+
inputs:
|
|
46
|
+
<name>: i:<name>
|
|
47
|
+
steps:
|
|
48
|
+
- call: <alias>
|
|
49
|
+
id: <step_id>
|
|
50
|
+
inputs:
|
|
51
|
+
<name>: i:<name>
|
|
52
|
+
expect:
|
|
53
|
+
status: 200
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## When to use this skill
|
|
57
|
+
|
|
58
|
+
- “Generate a test for this API.”
|
|
59
|
+
- “Create a smoke test from this OpenAPI spec.”
|
|
60
|
+
- “Turn this endpoint into a Multimeter test.”
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
This file tells the AI how to generate **`type: test`** `.mmt` files.
|
|
2
|
+
|
|
3
|
+
**Prefer MCP `scaffold_test` (or `testlight scaffold test --from`) for new tests from an API.** Use this guide for fields and step types beyond the scaffold baseline — not as an excuse to invent a full file from scratch.
|
|
4
|
+
|
|
5
|
+
Always follow these rules:
|
|
6
|
+
- Output must be valid YAML.
|
|
7
|
+
- The first non-comment line must be `type: test`.
|
|
8
|
+
- Tests should primarily **call APIs or other tests**, assert results, and optionally loop or branch.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Schema (mental model for the AI)
|
|
13
|
+
|
|
14
|
+
These fields match `TestData` in `core/src/TestData.ts` and the public docs in `docs/files/test/index.md`.
|
|
15
|
+
|
|
16
|
+
Top-level keys and types:
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
type: test # REQUIRED, must be exactly "test"
|
|
20
|
+
|
|
21
|
+
title: string # REQUIRED, human-readable test name
|
|
22
|
+
tags: # REQUIRED (can be empty array) for grouping
|
|
23
|
+
- string
|
|
24
|
+
|
|
25
|
+
description: string # optional, short explanation
|
|
26
|
+
|
|
27
|
+
import: # alias -> file path (apis, tests, CSV)
|
|
28
|
+
<alias>: path/to/file.mmt
|
|
29
|
+
|
|
30
|
+
inputs: # input variables with default values
|
|
31
|
+
<name>: string # or number/boolean/null (JSON-compatible)
|
|
32
|
+
|
|
33
|
+
outputs: # top-level outputs for this test
|
|
34
|
+
<name>: string # expression or literal
|
|
35
|
+
|
|
36
|
+
cache: 5m # optional; when this test is imported+called again
|
|
37
|
+
# in the same root run with same title+inputs,
|
|
38
|
+
# reuse outputs (skip callee body). Duration like
|
|
39
|
+
# repeat/delay, epoch number, or datetime with ":"
|
|
40
|
+
|
|
41
|
+
steps: # linear flow of steps (no stages)
|
|
42
|
+
- <step>
|
|
43
|
+
|
|
44
|
+
stages: # parallel/grouped flows
|
|
45
|
+
- id: string
|
|
46
|
+
title?: string
|
|
47
|
+
condition?: string # optional condition string
|
|
48
|
+
after?: string | string[]
|
|
49
|
+
steps: # steps inside this stage
|
|
50
|
+
- <step>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`steps` and `stages` are mutually exclusive in typical usage — use **one** or the other unless you have a very deliberate reason.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Step types
|
|
58
|
+
|
|
59
|
+
All steps are objects with a `type` (implicit from the key). They map to `TestFlowStep` in `TestData.ts`.
|
|
60
|
+
|
|
61
|
+
Supported step forms:
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
# 1) call: invoke another test or an API (imported by alias)
|
|
65
|
+
- call: <alias>
|
|
66
|
+
id: <stepId> # REQUIRED, used to reference outputs later
|
|
67
|
+
title?: string # optional human-readable label
|
|
68
|
+
inputs?: # passed as JSON object
|
|
69
|
+
<name>: <value>
|
|
70
|
+
expect?: # inline assertions (shorthand for assert)
|
|
71
|
+
<field>: <value>
|
|
72
|
+
debug?: # inline debug inspections (same syntax as expect, never fails)
|
|
73
|
+
<field>: <value>
|
|
74
|
+
report?: all | fails | none # controls reporting level
|
|
75
|
+
|
|
76
|
+
# 2) run: start an imported mock server (type: server file)
|
|
77
|
+
- run: <alias> # alias of an imported server file
|
|
78
|
+
|
|
79
|
+
# 3) check: soft assertion (logs failure, continues)
|
|
80
|
+
- check: <comparison>
|
|
81
|
+
|
|
82
|
+
# 4) assert: hard assertion (stops on failure)
|
|
83
|
+
- assert: <comparison>
|
|
84
|
+
|
|
85
|
+
# 5) if: conditional block (optional else)
|
|
86
|
+
- if: <comparison>
|
|
87
|
+
steps:
|
|
88
|
+
- <step>
|
|
89
|
+
else:
|
|
90
|
+
- <step>
|
|
91
|
+
|
|
92
|
+
# 6) repeat: repeat a block N times or by string
|
|
93
|
+
- repeat: <number | string>
|
|
94
|
+
steps:
|
|
95
|
+
- <step>
|
|
96
|
+
|
|
97
|
+
# 7) for: loop header (JS-style or shorthand)
|
|
98
|
+
- for: <header or expression>
|
|
99
|
+
steps:
|
|
100
|
+
- <step>
|
|
101
|
+
|
|
102
|
+
# 8) js: inline JavaScript
|
|
103
|
+
- js: |
|
|
104
|
+
// javascript code
|
|
105
|
+
|
|
106
|
+
# 9) print: log a message
|
|
107
|
+
- print: "text"
|
|
108
|
+
|
|
109
|
+
# 10) delay: sleep
|
|
110
|
+
- delay: <number | string> # e.g. 200, "2s", "1m"
|
|
111
|
+
|
|
112
|
+
# 11) set: assign/update variables
|
|
113
|
+
- set:
|
|
114
|
+
<name>: <value>
|
|
115
|
+
|
|
116
|
+
# 12) var / const / let: JS-style declarations
|
|
117
|
+
- var:
|
|
118
|
+
<name>: <value>
|
|
119
|
+
- const:
|
|
120
|
+
<name>: <value>
|
|
121
|
+
- let:
|
|
122
|
+
<name>: <value>
|
|
123
|
+
|
|
124
|
+
# 13) data: bind CSV alias (from import)
|
|
125
|
+
- data: <alias>
|
|
126
|
+
|
|
127
|
+
# 14) setenv: promote values into environment variables
|
|
128
|
+
- setenv:
|
|
129
|
+
<env_name>: <value> # e.g. token: ${loginStep.token}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Additional `call` fields
|
|
133
|
+
|
|
134
|
+
Beyond the basic `call`, `id`, and `inputs`, a call step also supports:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
- call: <alias>
|
|
138
|
+
id: <stepId>
|
|
139
|
+
title?: string # optional human-readable label for logs/reports
|
|
140
|
+
inputs?: { ... }
|
|
141
|
+
expect?: # inline assertions on the call result
|
|
142
|
+
<field>: <value> # e.g. status: 200, body.name: "John"
|
|
143
|
+
debug?: # inline debug inspections (same syntax as expect, never fails)
|
|
144
|
+
<field>: <value> # e.g. status: 200, body: ${someVar}
|
|
145
|
+
report?: all | fails | none # controls pass/fail reporting level
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`expect` is a shorthand for common assertions — each key is a dotted path into the response, and the value is compared with `==` by default. You can prefix with an operator (e.g. `>= 1`, `!= null`).
|
|
149
|
+
|
|
150
|
+
`debug` uses the same syntax as `expect` but never fails the test — it always logs results with a debug icon for troubleshooting. Debug entries are excluded from exported reports.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
`<comparison>` is a string expression using operators from `opsList` in `TestData.ts`:
|
|
155
|
+
|
|
156
|
+
- `<`, `>`, `<=`, `>=`, `==`, `!=`
|
|
157
|
+
- `=@` (is in: left is contained in right), `!@` (is not in)
|
|
158
|
+
- `=C` (contains: left contains right), `!C` (does not contain)
|
|
159
|
+
- `=^` (starts with), `!^` (not starts with)
|
|
160
|
+
- `=$` (ends with), `!$` (not ends with)
|
|
161
|
+
- `=*` (regex match), `!*` (regex not match)
|
|
162
|
+
- `=~` (equal as string / type-unsafe), `!~` (not equal as string) — for XML/text string outputs vs bare YAML booleans/numbers
|
|
163
|
+
- `=#` (string/number character length equals), `!#` (not equal)
|
|
164
|
+
- `>N%`(fuzzy match at least N% similar), `<N%` (fuzzy match less than N%). Any whole percent from 0 to 100 can be used, for example `>80%`. In the visual UI these appear as `>%` and `<%` with a separate percentage selector.
|
|
165
|
+
|
|
166
|
+
Example comparisons:
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
- assert: ${login.status} == 200
|
|
170
|
+
- check: ${profile.email} =* /@example.com$/
|
|
171
|
+
- assert: ${response.body.total} >= 1
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Naming and references
|
|
177
|
+
|
|
178
|
+
- Every `call` step **must** have a unique `id` within the test.
|
|
179
|
+
- Use that `id` to reference its outputs in later steps (the exact JS shape comes from the imported file, but following patterns from the docs is enough):
|
|
180
|
+
|
|
181
|
+
```yaml
|
|
182
|
+
- call: login
|
|
183
|
+
id: loginStep
|
|
184
|
+
inputs:
|
|
185
|
+
username: i:username
|
|
186
|
+
password: i:password
|
|
187
|
+
|
|
188
|
+
- assert: ${loginStep.status} == 200
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
- Use `set` to place values into `outputs` if you want the test itself to expose results:
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
- set:
|
|
195
|
+
outputs.token: ${loginStep.token}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Tokens the AI can use in tests
|
|
201
|
+
|
|
202
|
+
You can use the same token syntaxes as for APIs:
|
|
203
|
+
|
|
204
|
+
- Environment `variables:` `e:api_url`, `e:auth_token`, etc., or `<<e:api_url>>`
|
|
205
|
+
- Test `inputs:` `i:user_id` or `<<i:user_id>>`
|
|
206
|
+
- Random values: `r:name` or `<<r:name>>`
|
|
207
|
+
- Current/time values: `c:name` or `<<c:name>>`
|
|
208
|
+
|
|
209
|
+
Guidelines:
|
|
210
|
+
- Use bare tokens when they are the **entire value**.
|
|
211
|
+
- Use `<< >>` when embedding in text.
|
|
212
|
+
|
|
213
|
+
Example:
|
|
214
|
+
|
|
215
|
+
```yaml
|
|
216
|
+
inputs:
|
|
217
|
+
username: user@example.com
|
|
218
|
+
password: string
|
|
219
|
+
|
|
220
|
+
steps:
|
|
221
|
+
- call: login
|
|
222
|
+
id: loginStep
|
|
223
|
+
inputs:
|
|
224
|
+
username: i:username
|
|
225
|
+
password: i:password
|
|
226
|
+
- assert: ${loginStep.status} == 200
|
|
227
|
+
- set:
|
|
228
|
+
outputs.token: ${loginStep.token}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Common patterns the AI should generate
|
|
234
|
+
|
|
235
|
+
### 1. Simple smoke test for a single API
|
|
236
|
+
|
|
237
|
+
User asks: "Create a test that logs in and checks status 200. The API alias is `login`."
|
|
238
|
+
|
|
239
|
+
```yaml
|
|
240
|
+
type: test
|
|
241
|
+
title: Login smoke test
|
|
242
|
+
tags: [smoke, auth]
|
|
243
|
+
inputs:
|
|
244
|
+
username: user@example.com
|
|
245
|
+
password: string
|
|
246
|
+
steps:
|
|
247
|
+
- call: login
|
|
248
|
+
id: loginStep
|
|
249
|
+
inputs:
|
|
250
|
+
username: i:username
|
|
251
|
+
password: i:password
|
|
252
|
+
- assert: ${loginStep.status} == 200
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### 2. Chained calls: login then get profile
|
|
256
|
+
|
|
257
|
+
```yaml
|
|
258
|
+
type: test
|
|
259
|
+
title: Login and get profile
|
|
260
|
+
tags: [smoke, auth, profile]
|
|
261
|
+
import:
|
|
262
|
+
login: ./login.mmt
|
|
263
|
+
get_profile: ./get_profile.mmt
|
|
264
|
+
inputs:
|
|
265
|
+
username: user@example.com
|
|
266
|
+
password: string
|
|
267
|
+
steps:
|
|
268
|
+
- call: login
|
|
269
|
+
id: loginStep
|
|
270
|
+
inputs:
|
|
271
|
+
username: i:username
|
|
272
|
+
password: i:password
|
|
273
|
+
- assert: ${loginStep.status} == 200
|
|
274
|
+
|
|
275
|
+
- call: get_profile
|
|
276
|
+
id: profileStep
|
|
277
|
+
inputs:
|
|
278
|
+
token: ${loginStep.token}
|
|
279
|
+
- assert: ${profileStep.status} == 200
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### 3. Data-driven loop over CSV
|
|
283
|
+
|
|
284
|
+
```yaml
|
|
285
|
+
type: test
|
|
286
|
+
title: Login for multiple users
|
|
287
|
+
tags: [load, auth]
|
|
288
|
+
import:
|
|
289
|
+
users: ./users.csv
|
|
290
|
+
login: ./login.mmt
|
|
291
|
+
|
|
292
|
+
steps:
|
|
293
|
+
- data: users
|
|
294
|
+
- for: const user of users
|
|
295
|
+
steps:
|
|
296
|
+
- call: login
|
|
297
|
+
id: loginStep
|
|
298
|
+
inputs:
|
|
299
|
+
username: ${user.username}
|
|
300
|
+
password: ${user.password}
|
|
301
|
+
- check: ${loginStep.status} == 200
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 4. Conditional flows
|
|
305
|
+
|
|
306
|
+
```yaml
|
|
307
|
+
type: test
|
|
308
|
+
title: Conditional retry on failure
|
|
309
|
+
tags: [resilience]
|
|
310
|
+
import:
|
|
311
|
+
login: ./login.mmt
|
|
312
|
+
|
|
313
|
+
steps:
|
|
314
|
+
- call: login
|
|
315
|
+
id: login1
|
|
316
|
+
- if: ${login1.status} != 200
|
|
317
|
+
steps:
|
|
318
|
+
- print: "Retrying login"
|
|
319
|
+
- call: login
|
|
320
|
+
id: login2
|
|
321
|
+
- assert: ${login2.status} == 200
|
|
322
|
+
else:
|
|
323
|
+
- print: "Login succeeded on first attempt"
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Style rules for the AI
|
|
329
|
+
|
|
330
|
+
- Use 2-space indentation.
|
|
331
|
+
- Prefer small, readable tests focused on a single behavior.
|
|
332
|
+
- Prefer `steps` for simple linear flows; only use `stages` when explicit parallelism is needed.
|
|
333
|
+
- Always include `title` and `tags` (even if tags is a small list like `[smoke]`).
|
|
334
|
+
- Avoid adding `js` steps unless the user needs custom logic that can’t be expressed with other constructs. YAML comments (`#`) are fine — Format Document preserves them. Prefer `title` / `description` for structured documentation.
|
|
335
|
+
When unsure, generate a **minimal valid test** that clearly calls the described APIs and asserts the most important property (typically HTTP status or a key field in the response).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
This file is the **single entry point** when the AI needs to **generate or modify** any `.mmt` file.
|
|
2
|
+
|
|
3
|
+
**Copilot / MCP:** Before editing YAML, call `read_documentation` for the file type, then `validate` after every edit. See `AI/agent-workflow.md`.
|
|
4
|
+
|
|
5
|
+
The AI must:
|
|
6
|
+
- Always output valid **YAML**.
|
|
7
|
+
- Always put `type` as the **first key** in the file.
|
|
8
|
+
- Choose the correct `type` based on the user request (rules below).
|
|
9
|
+
- Use the exact structures outlined and never deviate them.
|
|
10
|
+
- Use the fence language identifier yaml (i.e. ```yaml).
|
|
11
|
+
- Do not use stray backticks inside YAML.
|
|
12
|
+
- YAML comments (`#`) are preserved by Format Document. Prefer `description` / `title` for structured docs that also survive UI edits.
|
|
13
|
+
|
|
14
|
+
The only file types are:
|
|
15
|
+
- `type: api` – describe a single HTTP/WebSocket endpoint.
|
|
16
|
+
- `type: test` – describe a test flow that calls APIs/tests.
|
|
17
|
+
- `type: env` – define environment variables and presets.
|
|
18
|
+
- `type: doc` – describe API documentation over a set of `.mmt` files.
|
|
19
|
+
- `type: suite` – group and run multiple tests, APIs, or other suites.
|
|
20
|
+
- `type: loadtest` – run one `type: test` file with load configuration.
|
|
21
|
+
- `type: server` – define a mock server with endpoints, matching, and responses.
|
|
22
|
+
- `type: report` – structured test results generated by runs (viewable in the editor; usually not hand-authored).
|
|
23
|
+
|
|
24
|
+
When the user asks you to **create or change** a `.mmt` file, follow this order:
|
|
25
|
+
|
|
26
|
+
1. **Decide the type**
|
|
27
|
+
- If the user talks about an endpoint, URL, method, headers, body, or examples → use `type: api`.
|
|
28
|
+
- If the user talks about scenarios, flows, assertions, retries, loops, or "tests" → use `type: test`.
|
|
29
|
+
- If the user talks about base URLs, credentials, modes, feature flags, presets → use `type: env`.
|
|
30
|
+
- If the user talks about documentation, catalogs, grouping APIs → use `type: doc`.
|
|
31
|
+
- If the user talks about running multiple tests, orchestration, collections of tests → use `type: suite`.
|
|
32
|
+
- If the user talks about concurrency, ramp-up, repeated execution, or load against one test flow → use `type: loadtest`.
|
|
33
|
+
- If the user talks about mocking, fake servers, stubbing responses, or "mock server" → use `type: server`.
|
|
34
|
+
- If the user talks about test reports, CI results, or exporting run results → `type: report` files are generated automatically. Explain the `--report` flag (see `docs/files/report/index.md`) rather than generating a report file.
|
|
35
|
+
|
|
36
|
+
2. **Jump to the detailed generator**
|
|
37
|
+
- For `type: api`, use **`AI/generate-api.md`** to shape fields and examples.
|
|
38
|
+
- For `type: test`, use **`AI/generate-test.md`**.
|
|
39
|
+
- For `type: env`, use **`AI/generate-env.md`**.
|
|
40
|
+
- For `type: doc`, use **`AI/generate-doc.md`**.
|
|
41
|
+
- For `type: suite`, use **`AI/generate-suite.md`**.
|
|
42
|
+
- For `type: loadtest`, use **`AI/generate-loadtest.md`**.
|
|
43
|
+
|
|
44
|
+
3. **Token syntax (always snake_case)**
|
|
45
|
+
- Environment `variables:` `e:api_url`, `e:auth_token`, etc.
|
|
46
|
+
- Embedded in strings/URLs: `<<e:api_url>>/users`, `Bearer <<e:auth_token>>`.
|
|
47
|
+
- Test/API `inputs:` `i:user_id`, `<<i:user_id>>`.
|
|
48
|
+
- Random: `r:uuid`, `r:first_name`, etc.
|
|
49
|
+
- Current/time: `c:date`, `c:epoch_ms`, etc.
|
|
50
|
+
|
|
51
|
+
Rules:
|
|
52
|
+
- Prefer **snake_case** for names inside tokens: `e:api_url`, `i:user_id`, not `e:api_url`.
|
|
53
|
+
- Use bare tokens as the **entire YAML value** when possible.
|
|
54
|
+
- Use `<< >>` only when embedding tokens inside a larger string.
|
|
55
|
+
|
|
56
|
+
4. **Editing vs. creating**
|
|
57
|
+
- When creating a new `file:` start from the minimal valid skeleton in the corresponding `generate-*.md`.
|
|
58
|
+
- When editing: keep the existing structure, only add/change what the user asked, and do not remove unrelated fields.
|
|
59
|
+
|
|
60
|
+
If you only need to **reason** about a user file (and not generate new YAML), prefer reading **only the relevant `AI/generate-*.md`** file to save tokens.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Golden smoke pair (AI)
|
|
2
|
+
|
|
3
|
+
Canonical low-token example. Mirror this shape; prefer `scaffold_test` over inventing YAML.
|
|
4
|
+
|
|
5
|
+
## API — `examples/ai/golden_smoke/apis/echo.mmt`
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
type: api
|
|
9
|
+
title: Echo message
|
|
10
|
+
description: Golden smoke API for AI agents — minimal POST with outputs
|
|
11
|
+
method: post
|
|
12
|
+
url: https://test.mmt.dev/echo
|
|
13
|
+
format: json
|
|
14
|
+
inputs:
|
|
15
|
+
message: hello
|
|
16
|
+
outputs:
|
|
17
|
+
echoed: body.message
|
|
18
|
+
body:
|
|
19
|
+
message: i:message
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Test — `examples/ai/golden_smoke/tests/echo-smoke.mmt`
|
|
23
|
+
|
|
24
|
+
Produced by `scaffold_test` / `testlight scaffold test --from` (do not hand-author from scratch):
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
type: test
|
|
28
|
+
title: Echo message smoke test
|
|
29
|
+
description: Smoke test for API "Echo message".
|
|
30
|
+
tags:
|
|
31
|
+
- smoke
|
|
32
|
+
- post
|
|
33
|
+
import:
|
|
34
|
+
echo: ../apis/echo.mmt
|
|
35
|
+
inputs:
|
|
36
|
+
message: i:message
|
|
37
|
+
steps:
|
|
38
|
+
- call: echo
|
|
39
|
+
id: iEcho
|
|
40
|
+
inputs:
|
|
41
|
+
message: i:message
|
|
42
|
+
expect:
|
|
43
|
+
status: 200
|
|
44
|
+
echoed: != null
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Agent rules
|
|
48
|
+
|
|
49
|
+
1. New API test → `scaffold_test` first.
|
|
50
|
+
2. Then `validate` → `format`.
|
|
51
|
+
3. Modify → patch only (never full-file rewrite unless the user asks).
|
|
52
|
+
4. After a run, to tighten asserts → `suggest_assertions` → patch → `validate` → `format`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Generate `type: api` (min)
|
|
2
|
+
|
|
3
|
+
```yaml
|
|
4
|
+
type: api
|
|
5
|
+
title: Login
|
|
6
|
+
method: post
|
|
7
|
+
url: <<e:api_url>>/login
|
|
8
|
+
format: json
|
|
9
|
+
inputs:
|
|
10
|
+
username: i:username
|
|
11
|
+
password: i:password
|
|
12
|
+
body:
|
|
13
|
+
username: i:username
|
|
14
|
+
password: i:password
|
|
15
|
+
outputs:
|
|
16
|
+
token: body.token
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Essentials
|
|
20
|
+
|
|
21
|
+
- First line: `type: api`
|
|
22
|
+
- `url` required; `method` required for HTTP
|
|
23
|
+
- `protocol`: `http` or `ws` (often inferred)
|
|
24
|
+
- `format`: `json` | `text` | `xml` | `urlencoded` | …
|
|
25
|
+
- Optional: `headers`, `query`, `cookies`, `examples`, `setenv`
|
|
26
|
+
- Tokens: `e:`, `i:`, `r:`, `c:`
|
|
27
|
+
|
|
28
|
+
For tests against this API use `scaffold_test` / `api_card`, not a full OpenAPI dump.
|
|
29
|
+
Request `pack: full` for WS, GraphQL, gRPC, and rare fields.
|