@mamund/tram 0.0.0-stage → 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.
- package/CHANGELOG.md +11 -0
- package/LICENSE +15 -0
- package/README.md +996 -2
- package/bin/tram +1117 -0
- package/docs/behavioral-modeling-for-apis.md +749 -0
- package/docs/beta-status.md +29 -0
- package/docs/explainer.md +256 -0
- package/docs/images/tram-logo.png +0 -0
- package/docs/images/tram-test-run.png +0 -0
- package/docs/manifest-spec.md +1410 -0
- package/docs/quick-start.md +92 -0
- package/docs/roadmap.md +557 -0
- package/docs/tasks-api-tutorial.md +115 -0
- package/lib/assertions.js +918 -0
- package/package.json +47 -4
package/README.md
CHANGED
|
@@ -1,3 +1,997 @@
|
|
|
1
|
-
#
|
|
1
|
+
# TRAM
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**TRAM** (Test Runner for Assertion Manifests) is a framework for creating executable behavioral models of HTTP APIs. It validates those models and gathers evidence from running APIs to verify that observable behavior matches the intended design.
|
|
4
|
+
|
|
5
|
+
<table>
|
|
6
|
+
<tr>
|
|
7
|
+
<td valign="top">
|
|
8
|
+
|
|
9
|
+
Rather than focusing on implementation details, TRAM focuses on what can be observed at the API surface: the resources, actions, workflows, and rules that define how a system behaves. Assertions are organized into progressively richer layers, moving from endpoint availability and response structure to business behavior, workflows, and governance constraints. This allows teams to express operational intent as a durable behavioral model that remains valuable even as implementations evolve.
|
|
10
|
+
|
|
11
|
+
</td>
|
|
12
|
+
<td width="240" valign="top">
|
|
13
|
+
|
|
14
|
+
<div style="text-align: center; display: block; margin: auto;">
|
|
15
|
+
<img src="./docs/images/tram-logo.png" width="200" alt="TRAM (Test Runner for Assertion Manifests)" />
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
</td>
|
|
19
|
+
</tr>
|
|
20
|
+
</table>
|
|
21
|
+
|
|
22
|
+
<div style="text-align: center; display: block; margin: auto;">
|
|
23
|
+
<img src="./docs/images/tram-test-run.png" alt="TRAM screenshot of test run" />
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
## Documentation
|
|
27
|
+
|
|
28
|
+
* [Beta status and limitations](docs/beta-status.md)
|
|
29
|
+
* [Changelog](CHANGELOG.md)
|
|
30
|
+
|
|
31
|
+
* [Quick Start](docs/quick-start.md)
|
|
32
|
+
* [Tasks API tutorial](docs/tasks-api-tutorial.md)
|
|
33
|
+
* [Explainer](docs/explainer.md)
|
|
34
|
+
* [Manifest Specification](docs/manifest-spec.md)
|
|
35
|
+
* [Behavioral Modeling for APIs](docs/behavioral-modeling-for-apis.md)
|
|
36
|
+
* [Roadmap](docs/roadmap.md)
|
|
37
|
+
|
|
38
|
+
<p>TRAM combines:</p>
|
|
39
|
+
|
|
40
|
+
<ul>
|
|
41
|
+
<li>a manifest-driven test format</li>
|
|
42
|
+
<li>a reusable assertion engine</li>
|
|
43
|
+
<li>a portable HTTP test runner</li>
|
|
44
|
+
<li>stable runtime interpolation support</li>
|
|
45
|
+
<li>native type assertions</li>
|
|
46
|
+
<li>optional property assertions</li>
|
|
47
|
+
<li>object-map and collection assertions</li>
|
|
48
|
+
<li>layered behavioral modeling</li>
|
|
49
|
+
<li>workflow-oriented behavioral validation</li>
|
|
50
|
+
<li>an AI Coaching workflow focused on learning and augmentation rather than pure automation</li>
|
|
51
|
+
</ul>
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## What's New in Manifest 0.2
|
|
56
|
+
|
|
57
|
+
Manifest version **0.2** introduces response capture.
|
|
58
|
+
|
|
59
|
+
Capture allows a test to extract values from an HTTP response (such as resource identifiers or hypermedia links) and reuse those values in subsequent requests. This makes it possible to write behavioral tests for APIs that generate identifiers dynamically or expose navigational affordances.
|
|
60
|
+
|
|
61
|
+
Earlier (`0.1`) manifests remain supported and continue to execute without modification.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Output artifacts
|
|
66
|
+
|
|
67
|
+
A TRAM run can produce several complementary artifacts.
|
|
68
|
+
|
|
69
|
+
| Artifact | Purpose |
|
|
70
|
+
|---|---|
|
|
71
|
+
| Manifest | Defines the expected API behavior. |
|
|
72
|
+
| HTTP Transcript | Records the observed HTTP request/response conversation. |
|
|
73
|
+
| Report | Evaluates the observed behavior against the manifest. |
|
|
74
|
+
| Evidence | The complete collection of artifacts from a test run. |
|
|
75
|
+
|
|
76
|
+
The transcript and report serve different purposes.
|
|
77
|
+
|
|
78
|
+
The HTTP Transcript records what happened during execution. The Report evaluates whether the observed behavior satisfied the behavioral expectations expressed in the manifest.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Smallest complete TRAM manifest
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"name": "Smallest TRAM manifest",
|
|
87
|
+
"config": {
|
|
88
|
+
"baseUrl": "http://localhost:3000"
|
|
89
|
+
},
|
|
90
|
+
"tests": [
|
|
91
|
+
{
|
|
92
|
+
"name": "GET /tasks returns 200",
|
|
93
|
+
"method": "GET",
|
|
94
|
+
"path": "/tasks",
|
|
95
|
+
"expect": {
|
|
96
|
+
"status": 200
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This is the smallest useful complete TRAM manifest:
|
|
104
|
+
|
|
105
|
+
* one manifest
|
|
106
|
+
* one test
|
|
107
|
+
* one request
|
|
108
|
+
* one behavioral assertion
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Capturing Values
|
|
113
|
+
|
|
114
|
+
The `capture` property records values observed in an HTTP response and makes them available to later requests.
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"id": "task-create",
|
|
119
|
+
"method": "POST",
|
|
120
|
+
"path": "/tasks",
|
|
121
|
+
"bodyType": "json",
|
|
122
|
+
"body": "$data.task.capture.valid",
|
|
123
|
+
"expect": {
|
|
124
|
+
"status": 201
|
|
125
|
+
},
|
|
126
|
+
"capture": {
|
|
127
|
+
"createdTaskId": "body.id"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Later tests can reference the captured value:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"id": "task-get",
|
|
137
|
+
"method": "GET",
|
|
138
|
+
"path": "/tasks/${capture.createdTaskId}",
|
|
139
|
+
"expect": {
|
|
140
|
+
"status": 200
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Capture works with response bodies, headers, and other observable response values. See the Manifest Specification for the complete syntax.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
## Why TRAM exists
|
|
149
|
+
|
|
150
|
+
TRAM explores a narrow problem:
|
|
151
|
+
|
|
152
|
+
_**How do we make behavioral expectations directly visible,
|
|
153
|
+
portable, executable, and reviewable?**_
|
|
154
|
+
|
|
155
|
+
The core artifact is the manifest:
|
|
156
|
+
|
|
157
|
+
[`api-tests.json`](https://github.com/mamund/tram/blob/main/api-tests.json)
|
|
158
|
+
|
|
159
|
+
The manifest defines:
|
|
160
|
+
|
|
161
|
+
* requests
|
|
162
|
+
* request bodies
|
|
163
|
+
* assertions
|
|
164
|
+
* expected behaviors
|
|
165
|
+
* shared test data
|
|
166
|
+
* runtime interpolation values
|
|
167
|
+
|
|
168
|
+
Assertions become directly inspectable operational statements.
|
|
169
|
+
|
|
170
|
+
Simple behavioral assertion:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"path": "$.status",
|
|
175
|
+
"equals": "active"
|
|
176
|
+
}
|
|
177
|
+
````
|
|
178
|
+
|
|
179
|
+
Meaning:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
The resource status must be "active".
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Optional property assertion for evolving representations:
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"path": "$",
|
|
190
|
+
"each": {
|
|
191
|
+
"property": "description",
|
|
192
|
+
"optional": true,
|
|
193
|
+
"type": "string"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Meaning:
|
|
199
|
+
|
|
200
|
+
```text
|
|
201
|
+
"description" may be absent.
|
|
202
|
+
If present, it must be a string.
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Hypermedia affordance assertion:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"path": "$._links",
|
|
210
|
+
"eachProperty": {
|
|
211
|
+
"hasProperties": ["href", "method"]
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Meaning:
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
Every affordance must define both a target URL and an HTTP method.
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Collection behavioral assertion:
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"path": "$",
|
|
227
|
+
"each": {
|
|
228
|
+
"property": "status",
|
|
229
|
+
"oneOf": ["active", "pending", "completed"]
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Meaning:
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
Every returned resource must have a recognized workflow state.
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
Nested affordance traversal assertion:
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"path": "$",
|
|
247
|
+
"each": {
|
|
248
|
+
"path": "$._links",
|
|
249
|
+
"eachProperty": {
|
|
250
|
+
"hasProperties": ["href", "method"]
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
Meaning:
|
|
256
|
+
|
|
257
|
+
```text
|
|
258
|
+
Every returned resource must expose affordances
|
|
259
|
+
that define both a target URL and an HTTP method.
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
TRAM supports partial and evolving representations while preserving explicit behavioral validation.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
## Behavioral layering
|
|
268
|
+
|
|
269
|
+
TRAM organizes behavioral testing into six progressive layers.
|
|
270
|
+
|
|
271
|
+
| Level | Focus | Question |
|
|
272
|
+
|---|---|---|
|
|
273
|
+
| 0 | Surface | Can the API be reached? |
|
|
274
|
+
| 1 | Shape | Do resources and affordances appear correctly? |
|
|
275
|
+
| 2 | Safe behavior | Do navigation, lookup, filtering, and query interactions behave correctly? |
|
|
276
|
+
| 3 | Unsafe behavior | Do isolated state-changing actions behave correctly? |
|
|
277
|
+
| 4 | Workflow | Can meaningful operational narratives be completed successfully? |
|
|
278
|
+
| 5 | Governance | Are policies, constraints, and semantic rules enforced correctly? |
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
See [Behavioral Modeling for APIs](docs/behavioral-modeling-for-apis.md) for the six-layer model.
|
|
282
|
+
|
|
283
|
+
The layers are additive rather than replacement-oriented. Each layer narrows debugging scope while preserving readable behavioral intent.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Project goals
|
|
288
|
+
|
|
289
|
+
TRAM is designed around several principles:
|
|
290
|
+
|
|
291
|
+
* behavioral tests over implementation tests
|
|
292
|
+
* portable manifests over framework lock-in
|
|
293
|
+
* readable intent over clever abstractions
|
|
294
|
+
* explicitness over hidden runtime behavior
|
|
295
|
+
* low-noise reporting
|
|
296
|
+
* augmentation and learning over one-shot generation
|
|
297
|
+
|
|
298
|
+
The long-term direction is an AI Coach that helps users learn behavioral API testing while collaboratively constructing executable manifests.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Current implementation
|
|
303
|
+
|
|
304
|
+
Current implementation includes:
|
|
305
|
+
|
|
306
|
+
* manifest specification (`api-tests.json`)
|
|
307
|
+
* dependency-free assertion engine
|
|
308
|
+
* dependency-free HTTP runner
|
|
309
|
+
* body/header/status assertions
|
|
310
|
+
* collection assertions (`each`)
|
|
311
|
+
* object-map assertions (`eachProperty`)
|
|
312
|
+
* native type assertions (`type`)
|
|
313
|
+
* optional property assertions (`optional`)
|
|
314
|
+
* range assertions (`range`)
|
|
315
|
+
* stable run-scoped variables
|
|
316
|
+
* runtime interpolation (`${data.*}`)
|
|
317
|
+
* object injection (`$data.*`)
|
|
318
|
+
* capture values from responses and reuse them in later requests
|
|
319
|
+
* happy-path and sad-path testing
|
|
320
|
+
* JSON, form, and text request body support
|
|
321
|
+
* workflow-oriented behavioral modeling
|
|
322
|
+
* machine-readable reporting
|
|
323
|
+
* real API validation against a sample CRUD-style task API
|
|
324
|
+
* HTTP transcript generation
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Project structure
|
|
329
|
+
|
|
330
|
+
```text
|
|
331
|
+
.
|
|
332
|
+
├── README.md
|
|
333
|
+
├── package.json
|
|
334
|
+
├── api-tests.json
|
|
335
|
+
├── bin/
|
|
336
|
+
│ └── tram
|
|
337
|
+
├── lib/
|
|
338
|
+
│ └── assertions.js
|
|
339
|
+
├── docs/
|
|
340
|
+
└── sample-api/
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
## CLI usage
|
|
347
|
+
|
|
348
|
+
The CLI accepts a manifest filename followed by options; `validate` and `run` are not separate subcommands.
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
tram <manifest-file> [options]
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Options:
|
|
355
|
+
|
|
356
|
+
```text
|
|
357
|
+
-v, --verbose Print passing assertion details
|
|
358
|
+
-r, --report <file> Write behavioral report (JSON)
|
|
359
|
+
-t, --transcript <file> Write HTTP transcript
|
|
360
|
+
-c, --validate Validate the manifest without making HTTP requests
|
|
361
|
+
-h, --help Show help
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## CLI installation
|
|
367
|
+
|
|
368
|
+
TRAM 0.1.0 beta is distributed through npm as @mamund/tram using the beta distribution tag. The following installation commands apply after publication.
|
|
369
|
+
|
|
370
|
+
### Install from npm
|
|
371
|
+
|
|
372
|
+
TRAM requires Node.js 18 or later and has no runtime dependencies.
|
|
373
|
+
|
|
374
|
+
Install the command globally:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
npm install --global @mamund/tram@beta
|
|
378
|
+
tram --help
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Alternatively, add TRAM to an existing Node.js project:
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
npm install --save-dev @mamund/tram@beta
|
|
385
|
+
npx tram --help
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Validate a manifest without sending HTTP requests:
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
tram api-tests.json --validate
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Execute a manifest and save the resulting evidence:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
tram api-tests.json --report results.json --transcript transcript.http
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
The manifest is supplied by your project; the npm package does not install a sample API or sample manifest. Start with the [public API quick start](docs/quick-start.md). The repository also contains a local Tasks API example for more advanced testing.
|
|
401
|
+
|
|
402
|
+
### Local development setup
|
|
403
|
+
|
|
404
|
+
To work on TRAM itself, clone the source repository:
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
git clone https://github.com/mamund/tram.git
|
|
408
|
+
cd tram
|
|
409
|
+
npm test
|
|
410
|
+
npm link
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
The `npm link` command is for developing TRAM from source. It is not required for npm consumers.
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## Core concepts
|
|
418
|
+
|
|
419
|
+
### Manifest-driven testing
|
|
420
|
+
|
|
421
|
+
Tests are defined declaratively in a manifest:
|
|
422
|
+
|
|
423
|
+
```json
|
|
424
|
+
{
|
|
425
|
+
"name": "Create task",
|
|
426
|
+
"method": "POST",
|
|
427
|
+
"path": "/tasks/${data.stableId}",
|
|
428
|
+
"body": "$data.task.valid",
|
|
429
|
+
"expect": {
|
|
430
|
+
"status": 201,
|
|
431
|
+
"body": [
|
|
432
|
+
{
|
|
433
|
+
"path": "$.status",
|
|
434
|
+
"equals": "active"
|
|
435
|
+
}
|
|
436
|
+
]
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
The manifest acts as both:
|
|
442
|
+
|
|
443
|
+
* executable configuration
|
|
444
|
+
* behavioral operational artifact
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
### Shared runtime data
|
|
449
|
+
|
|
450
|
+
The `data` section stores reusable request and runtime values.
|
|
451
|
+
|
|
452
|
+
Example:
|
|
453
|
+
|
|
454
|
+
```json
|
|
455
|
+
{
|
|
456
|
+
"data": {
|
|
457
|
+
"stableId": "${randomId}"
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
The generated value remains stable throughout the current test run.
|
|
463
|
+
|
|
464
|
+
Later requests can reference the same value:
|
|
465
|
+
|
|
466
|
+
```json
|
|
467
|
+
{
|
|
468
|
+
"path": "/tasks/${data.stableId}"
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
TRAM also supports response capture.
|
|
473
|
+
|
|
474
|
+
Values observed in one response may be reused later in the same test run.
|
|
475
|
+
|
|
476
|
+
Example:
|
|
477
|
+
|
|
478
|
+
```json
|
|
479
|
+
"capture": {
|
|
480
|
+
"taskId": "body.id"
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Later requests can reference the captured value:
|
|
485
|
+
|
|
486
|
+
```json
|
|
487
|
+
"path": "/tasks/${capture.taskId}"
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
This enables coordinated multi-step behavioral flows without introducing custom scripting.
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
### Runtime interpolation semantics
|
|
495
|
+
|
|
496
|
+
Use:
|
|
497
|
+
|
|
498
|
+
```json
|
|
499
|
+
"$data.someObject"
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
when injecting structured runtime objects.
|
|
503
|
+
|
|
504
|
+
Use:
|
|
505
|
+
|
|
506
|
+
```json
|
|
507
|
+
"${data.someValue}"
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
when interpolating values inside strings.
|
|
511
|
+
|
|
512
|
+
Examples:
|
|
513
|
+
|
|
514
|
+
Correct object injection:
|
|
515
|
+
|
|
516
|
+
```json
|
|
517
|
+
"body": "$data.createTask"
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Correct string interpolation:
|
|
521
|
+
|
|
522
|
+
```json
|
|
523
|
+
"path": "/tasks/${data.knownTaskId}"
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Captured response values use:
|
|
527
|
+
|
|
528
|
+
```json
|
|
529
|
+
"${capture.taskId}"
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
These values are populated during test execution from earlier HTTP responses.
|
|
533
|
+
|
|
534
|
+
### Capture Example
|
|
535
|
+
|
|
536
|
+
The repository includes a dedicated capture example:
|
|
537
|
+
|
|
538
|
+
```text
|
|
539
|
+
See the capture examples in the [repository](https://github.com/mamund/tram).
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
The example demonstrates:
|
|
543
|
+
|
|
544
|
+
- creating a resource
|
|
545
|
+
- capturing values from the response
|
|
546
|
+
- reusing those values in subsequent requests
|
|
547
|
+
- following captured hypermedia links
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
### Assertion engine
|
|
552
|
+
|
|
553
|
+
The assertion library currently supports:
|
|
554
|
+
|
|
555
|
+
```text
|
|
556
|
+
exists
|
|
557
|
+
equals
|
|
558
|
+
contains
|
|
559
|
+
oneOf
|
|
560
|
+
type
|
|
561
|
+
range
|
|
562
|
+
isArray
|
|
563
|
+
hasProperties
|
|
564
|
+
length
|
|
565
|
+
minLength (deprecated)
|
|
566
|
+
each
|
|
567
|
+
eachProperty
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
Native type assertions support:
|
|
571
|
+
|
|
572
|
+
```text
|
|
573
|
+
string
|
|
574
|
+
number
|
|
575
|
+
boolean
|
|
576
|
+
array
|
|
577
|
+
object
|
|
578
|
+
null
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
Example native type assertion:
|
|
582
|
+
|
|
583
|
+
```json
|
|
584
|
+
{
|
|
585
|
+
"path": "$.priority",
|
|
586
|
+
"type": "number"
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
Example optional property assertion:
|
|
591
|
+
|
|
592
|
+
```json
|
|
593
|
+
{
|
|
594
|
+
"path": "$",
|
|
595
|
+
"each": {
|
|
596
|
+
"property": "description",
|
|
597
|
+
"optional": true,
|
|
598
|
+
"type": "string"
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
This assertion means:
|
|
604
|
+
|
|
605
|
+
```text
|
|
606
|
+
"description" may be absent
|
|
607
|
+
if present, it must still validate as a string
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
### Traversal semantics
|
|
613
|
+
|
|
614
|
+
TRAM distinguishes between arrays and object maps.
|
|
615
|
+
|
|
616
|
+
Use:
|
|
617
|
+
|
|
618
|
+
* `each` for arrays
|
|
619
|
+
* `eachProperty` for object maps
|
|
620
|
+
|
|
621
|
+
Examples:
|
|
622
|
+
|
|
623
|
+
```json
|
|
624
|
+
[
|
|
625
|
+
{...},
|
|
626
|
+
{...}
|
|
627
|
+
]
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
```text
|
|
631
|
+
=> each
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
```json
|
|
635
|
+
{
|
|
636
|
+
"self": {...},
|
|
637
|
+
"edit": {...}
|
|
638
|
+
}
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
```text
|
|
642
|
+
=> eachProperty
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
TRAM also distinguishes between:
|
|
646
|
+
|
|
647
|
+
* `path` for structural traversal
|
|
648
|
+
* `property` for scalar leaf checks
|
|
649
|
+
|
|
650
|
+
Example structural traversal:
|
|
651
|
+
|
|
652
|
+
```json
|
|
653
|
+
{
|
|
654
|
+
"path": "$",
|
|
655
|
+
"each": {
|
|
656
|
+
"path": "$._links",
|
|
657
|
+
"eachProperty": {
|
|
658
|
+
"hasProperties": ["href", "method"]
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
Example scalar leaf assertion:
|
|
665
|
+
|
|
666
|
+
```json
|
|
667
|
+
{
|
|
668
|
+
"path": "$",
|
|
669
|
+
"each": {
|
|
670
|
+
"property": "status",
|
|
671
|
+
"equals": "active"
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
The assertion model supports:
|
|
677
|
+
|
|
678
|
+
* collection traversal
|
|
679
|
+
* nested traversal
|
|
680
|
+
* object-map iteration
|
|
681
|
+
* native value validation
|
|
682
|
+
* optional property validation
|
|
683
|
+
* hypermedia affordance validation
|
|
684
|
+
|
|
685
|
+
while remaining declarative and inspectable.
|
|
686
|
+
|
|
687
|
+
TRAM intentionally limits type assertions to native value categories.
|
|
688
|
+
|
|
689
|
+
The following are currently out of scope:
|
|
690
|
+
|
|
691
|
+
```text
|
|
692
|
+
uuid
|
|
693
|
+
email
|
|
694
|
+
uri
|
|
695
|
+
date-time
|
|
696
|
+
schema validation
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
---
|
|
700
|
+
|
|
701
|
+
### Workflow-oriented behavioral modeling
|
|
702
|
+
|
|
703
|
+
TRAM manifests can model operational workflows rather than isolated endpoint checks.
|
|
704
|
+
|
|
705
|
+
TRAM models workflows through declarative sequencing rather than embedded scripting.
|
|
706
|
+
|
|
707
|
+
A workflow manifest may:
|
|
708
|
+
|
|
709
|
+
* create resources
|
|
710
|
+
* retrieve intermediate state
|
|
711
|
+
* apply mutations
|
|
712
|
+
* verify accumulated final state
|
|
713
|
+
|
|
714
|
+
This allows manifests to function as executable operational narratives.
|
|
715
|
+
|
|
716
|
+
Example workflow sequence:
|
|
717
|
+
|
|
718
|
+
```text
|
|
719
|
+
create
|
|
720
|
+
read after create
|
|
721
|
+
edit
|
|
722
|
+
update status
|
|
723
|
+
assign user
|
|
724
|
+
set due date
|
|
725
|
+
read final accumulated state
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
---
|
|
729
|
+
|
|
730
|
+
### Header assertion semantics
|
|
731
|
+
|
|
732
|
+
Header assertions use:
|
|
733
|
+
|
|
734
|
+
```json
|
|
735
|
+
{
|
|
736
|
+
"name": "content-type",
|
|
737
|
+
"contains": "application/json"
|
|
738
|
+
}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
Do not use `path` for header assertions.
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
### Request body support
|
|
746
|
+
|
|
747
|
+
TRAM supports multiple request body encodings:
|
|
748
|
+
|
|
749
|
+
```text
|
|
750
|
+
json
|
|
751
|
+
form
|
|
752
|
+
text
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
Example:
|
|
756
|
+
|
|
757
|
+
```json
|
|
758
|
+
{
|
|
759
|
+
"method": "PUT",
|
|
760
|
+
"path": "/tasks/task-1/status",
|
|
761
|
+
"bodyType": "form",
|
|
762
|
+
"body": "$data.task.updateStatus"
|
|
763
|
+
}
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
---
|
|
767
|
+
|
|
768
|
+
## Running the sample project
|
|
769
|
+
|
|
770
|
+
Start the sample API:
|
|
771
|
+
|
|
772
|
+
```bash
|
|
773
|
+
node sample-api/index.js
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
Run the test suite:
|
|
777
|
+
|
|
778
|
+
```bash
|
|
779
|
+
tram api-tests.json
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
Verbose mode:
|
|
783
|
+
|
|
784
|
+
```bash
|
|
785
|
+
tram api-tests.json --verbose
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
Generate an HTTP transcript:
|
|
789
|
+
|
|
790
|
+
```bash
|
|
791
|
+
tram api-tests.json --transcript transcript.http
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
Generate a machine-readable report:
|
|
795
|
+
|
|
796
|
+
```bash
|
|
797
|
+
tram api-tests.json --report report.json
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
Generate both artifacts:
|
|
801
|
+
|
|
802
|
+
```bash
|
|
803
|
+
tram api-tests.json \
|
|
804
|
+
--report report.json \
|
|
805
|
+
--transcript transcript.http
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
---
|
|
809
|
+
|
|
810
|
+
## Documentation
|
|
811
|
+
|
|
812
|
+
* [Beta status and limitations](docs/beta-status.md)
|
|
813
|
+
* [Changelog](CHANGELOG.md)
|
|
814
|
+
|
|
815
|
+
### Quick Start
|
|
816
|
+
|
|
817
|
+
[Practical walkthrough](docs/quick-start.md) for:
|
|
818
|
+
|
|
819
|
+
* running the sample project
|
|
820
|
+
* inspecting manifests
|
|
821
|
+
* understanding assertions
|
|
822
|
+
* understanding runtime interpolation
|
|
823
|
+
* exploring behavioral API testing workflows
|
|
824
|
+
|
|
825
|
+
### Manifest Specification
|
|
826
|
+
|
|
827
|
+
Authoritative executable [manifest model](docs/manifest-spec.md).
|
|
828
|
+
|
|
829
|
+
Defines:
|
|
830
|
+
|
|
831
|
+
* manifest structure
|
|
832
|
+
* request configuration
|
|
833
|
+
* assertion syntax
|
|
834
|
+
* optional property assertions
|
|
835
|
+
* traversal behavior
|
|
836
|
+
* runtime interpolation
|
|
837
|
+
* stable run-scoped variables
|
|
838
|
+
* collection assertions
|
|
839
|
+
* object-map assertions
|
|
840
|
+
* native type assertions
|
|
841
|
+
* body handling
|
|
842
|
+
|
|
843
|
+
### Explainer
|
|
844
|
+
|
|
845
|
+
Architectural [discussion](docs/explainer.md) of:
|
|
846
|
+
|
|
847
|
+
* behavioral assertions
|
|
848
|
+
* operational artifacts
|
|
849
|
+
* hypermedia-oriented testing
|
|
850
|
+
* generated systems
|
|
851
|
+
* workflow-oriented behavioral modeling
|
|
852
|
+
* AI-assisted workflows
|
|
853
|
+
|
|
854
|
+
---
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
## Validation pipeline
|
|
858
|
+
|
|
859
|
+
TRAM validates manifests before executing HTTP requests. Validation may also be invoked directly from the command line using the `--validate` option.
|
|
860
|
+
|
|
861
|
+
Validation currently includes:
|
|
862
|
+
|
|
863
|
+
* manifest file existence
|
|
864
|
+
* manifest JSON parsing
|
|
865
|
+
* top-level manifest structure
|
|
866
|
+
* required test fields
|
|
867
|
+
* supported HTTP methods
|
|
868
|
+
* supported request body types
|
|
869
|
+
* duplicate test IDs
|
|
870
|
+
* capture declarations
|
|
871
|
+
* capture path syntax
|
|
872
|
+
* capture identifier syntax
|
|
873
|
+
|
|
874
|
+
Example:
|
|
875
|
+
|
|
876
|
+
```bash
|
|
877
|
+
tram api-tests.json --validate
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
If the manifest is valid, TRAM reports success and exits without executing any requests. If validation fails, TRAM reports the validation errors and exits with a non-zero status.
|
|
881
|
+
|
|
882
|
+
Invalid manifests fail before execution begins.
|
|
883
|
+
|
|
884
|
+
TRAM reports multiple manifest validation problems in a single pass when possible.
|
|
885
|
+
|
|
886
|
+
TRAM distinguishes between:
|
|
887
|
+
|
|
888
|
+
* manifest authoring failures
|
|
889
|
+
* request/runtime failures
|
|
890
|
+
* behavioral assertion failures
|
|
891
|
+
|
|
892
|
+
---
|
|
893
|
+
|
|
894
|
+
## Reporting philosophy
|
|
895
|
+
|
|
896
|
+
TRAM can produce several complementary views of a test run.
|
|
897
|
+
|
|
898
|
+
* concise console output
|
|
899
|
+
* HTTP transcript of the observed conversation
|
|
900
|
+
* machine-readable behavioral report
|
|
901
|
+
|
|
902
|
+
The transcript records the observed HTTP conversation.
|
|
903
|
+
|
|
904
|
+
The report evaluates that conversation against the behavioral expectations expressed in the manifest.
|
|
905
|
+
|
|
906
|
+
Manifest validation is treated as a first-class operation, allowing behavioral models to be reviewed independently of execution.
|
|
907
|
+
|
|
908
|
+
---
|
|
909
|
+
|
|
910
|
+
## Design philosophy
|
|
911
|
+
|
|
912
|
+
TRAM is intentionally conservative.
|
|
913
|
+
|
|
914
|
+
Current releases avoid:
|
|
915
|
+
|
|
916
|
+
* framework dependencies
|
|
917
|
+
* custom scripting
|
|
918
|
+
* setup/teardown orchestration
|
|
919
|
+
* schema engines
|
|
920
|
+
* plugin systems
|
|
921
|
+
* hidden runtime behavior
|
|
922
|
+
|
|
923
|
+
The current emphasis is:
|
|
924
|
+
|
|
925
|
+
* clarity
|
|
926
|
+
* predictability
|
|
927
|
+
* behavior visibility
|
|
928
|
+
* manifest ergonomics
|
|
929
|
+
* reviewability
|
|
930
|
+
|
|
931
|
+
---
|
|
932
|
+
|
|
933
|
+
## AI Coaching direction
|
|
934
|
+
|
|
935
|
+
The AI Coaching direction includes:
|
|
936
|
+
|
|
937
|
+
* layered manifest generation
|
|
938
|
+
* traversal-aware assertion guidance
|
|
939
|
+
* workflow modeling support
|
|
940
|
+
* governance distinction guidance
|
|
941
|
+
* collaborative review cycles
|
|
942
|
+
* behavioral decomposition assistance
|
|
943
|
+
|
|
944
|
+
The eventual AI Coach layer will:
|
|
945
|
+
|
|
946
|
+
1. inspect `server.js` and/or API Story documents
|
|
947
|
+
2. identify API behaviors
|
|
948
|
+
3. propose candidate tests
|
|
949
|
+
4. distinguish happy and sad paths
|
|
950
|
+
5. review assertions collaboratively
|
|
951
|
+
6. generate plausible first-pass manifests
|
|
952
|
+
|
|
953
|
+
The goal is not automatic test generation alone.
|
|
954
|
+
|
|
955
|
+
The goal is helping users understand behavioral API testing while collaboratively constructing executable manifests.
|
|
956
|
+
|
|
957
|
+
---
|
|
958
|
+
|
|
959
|
+
## Example layer progression
|
|
960
|
+
|
|
961
|
+
Typical TRAM progression:
|
|
962
|
+
|
|
963
|
+
```text
|
|
964
|
+
Level 0 — endpoint availability
|
|
965
|
+
Level 1 — representation structure
|
|
966
|
+
Level 2 — lookup and filtering behavior
|
|
967
|
+
Level 3 — isolated mutation behavior
|
|
968
|
+
Level 4 — workflow continuity
|
|
969
|
+
Level 5 — governance and constraints
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
---
|
|
973
|
+
|
|
974
|
+
## Related ideas
|
|
975
|
+
|
|
976
|
+
TRAM draws inspiration from:
|
|
977
|
+
|
|
978
|
+
* behavioral testing
|
|
979
|
+
* executable specifications
|
|
980
|
+
* hypermedia-oriented design
|
|
981
|
+
* affordance-centric APIs
|
|
982
|
+
* augmentation-oriented AI systems
|
|
983
|
+
* coaching-based human/machine collaboration
|
|
984
|
+
|
|
985
|
+
---
|
|
986
|
+
|
|
987
|
+
## Status
|
|
988
|
+
|
|
989
|
+
Early experimental project.
|
|
990
|
+
|
|
991
|
+
Interfaces and manifest formats will evolve during v0.x development.
|
|
992
|
+
|
|
993
|
+
Project repository:
|
|
994
|
+
|
|
995
|
+
```text
|
|
996
|
+
https://github.com/mamund/tram
|
|
997
|
+
```
|