mmt-mcp 1.42.4 → 1.43.0-pre

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.
Files changed (51) hide show
  1. package/dist/examples/README.md +1 -1
  2. package/dist/examples/examples-index.json +178 -4
  3. package/dist/examples/intermediate/12_dynamic_values/README.md +89 -0
  4. package/dist/examples/intermediate/12_dynamic_values/api/current.mmt +29 -0
  5. package/dist/examples/intermediate/12_dynamic_values/api/random_identity.mmt +29 -0
  6. package/dist/examples/intermediate/12_dynamic_values/api/random_network.mmt +21 -0
  7. package/dist/examples/intermediate/12_dynamic_values/api/random_time.mmt +27 -0
  8. package/dist/examples/intermediate/12_dynamic_values/api/random_types.mmt +25 -0
  9. package/dist/examples/intermediate/12_dynamic_values/current_test.mmt +22 -0
  10. package/dist/examples/intermediate/12_dynamic_values/random_identity_test.mmt +20 -0
  11. package/dist/examples/intermediate/12_dynamic_values/random_network_test.mmt +16 -0
  12. package/dist/examples/intermediate/12_dynamic_values/random_time_test.mmt +24 -0
  13. package/dist/examples/intermediate/12_dynamic_values/random_types_test.mmt +21 -0
  14. package/dist/examples/intermediate/12_dynamic_values/suite.mmt +9 -0
  15. package/dist/examples/intermediate/13_stages_and_conditions/stages_test.mmt +1 -1
  16. package/dist/examples/intermediate/23_check_operators/README.md +2 -1
  17. package/dist/examples/intermediate/23_check_operators/api/sample.mmt +14 -0
  18. package/dist/examples/intermediate/23_check_operators/operators_test.mmt +22 -0
  19. package/dist/examples/intermediate/24_test_call_cache/create_session.mmt +1 -1
  20. package/dist/examples/intermediate/28_http_body_formats/README.md +57 -0
  21. package/dist/examples/intermediate/28_http_body_formats/body_formats_test.mmt +63 -0
  22. package/dist/examples/intermediate/28_http_body_formats/post_binary.mmt +10 -0
  23. package/dist/examples/intermediate/28_http_body_formats/post_json.mmt +13 -0
  24. package/dist/examples/intermediate/28_http_body_formats/post_multipart.mmt +16 -0
  25. package/dist/examples/intermediate/28_http_body_formats/post_text.mmt +12 -0
  26. package/dist/examples/intermediate/28_http_body_formats/post_urlencoded.mmt +16 -0
  27. package/dist/examples/intermediate/28_http_body_formats/post_xml.mmt +15 -0
  28. package/dist/examples/intermediate/28_http_body_formats/post_xmle.mmt +16 -0
  29. package/dist/examples/intermediate/29_suite_tag_filter/README.md +56 -0
  30. package/dist/examples/intermediate/29_suite_tag_filter/nested/auth.mmt +8 -0
  31. package/dist/examples/intermediate/29_suite_tag_filter/nested/login.mmt +11 -0
  32. package/dist/examples/intermediate/29_suite_tag_filter/nested/profile.mmt +9 -0
  33. package/dist/examples/intermediate/29_suite_tag_filter/suite.mmt +11 -0
  34. package/dist/examples/intermediate/29_suite_tag_filter/suite_ci.mmt +17 -0
  35. package/dist/examples/intermediate/29_suite_tag_filter/suite_smoke.mmt +14 -0
  36. package/dist/examples/intermediate/29_suite_tag_filter/tests/api_echo.mmt +14 -0
  37. package/dist/examples/intermediate/29_suite_tag_filter/tests/flaky_echo.mmt +14 -0
  38. package/dist/examples/intermediate/29_suite_tag_filter/tests/slow_status.mmt +11 -0
  39. package/dist/examples/intermediate/29_suite_tag_filter/tests/smoke_get.mmt +12 -0
  40. package/dist/examples/intermediate/29_suite_tag_filter/tests/smoke_post.mmt +14 -0
  41. package/dist/examples/intermediate/29_suite_tag_filter/tests/untagged.mmt +9 -0
  42. package/dist/examples/professional/09_ci_pipelines/README.md +1 -1
  43. package/dist/guides/generate-suite.md +21 -2
  44. package/dist/guides/generate-test.md +2 -1
  45. package/dist/guides/min/suite.md +1 -1
  46. package/dist/server.cjs +2626 -728
  47. package/dist/server.js +2626 -728
  48. package/package.json +1 -1
  49. package/dist/examples/intermediate/12_random_values/README.md +0 -59
  50. package/dist/examples/intermediate/12_random_values/api/random_echo.mmt +0 -21
  51. package/dist/examples/intermediate/12_random_values/random_values_test.mmt +0 -15
@@ -12,7 +12,7 @@ The examples are grouped by complexity so new users can start simple and add pow
12
12
 
13
13
  ## Intermediate
14
14
 
15
- Use these when you are ready for reusable APIs, authentication, mock servers, richer environment variables, inputs and outputs, suites, imports, data-driven tests, environment presets, documentation options/ranges, control flow, comparison operators (`expect` / `check` / `assert`), helper JavaScript, additional protocol or file formats, converting a small OpenAPI / Postman / WSDL / HTTP / Bruno file to `.mmt`, and the `omit` keyword.
15
+ Use these when you are ready for reusable APIs, authentication, mock servers, richer environment variables, inputs and outputs, suites, tag filters (`filter.only` / `filter.skip`), imports, data-driven tests, environment presets, documentation options/ranges, control flow, comparison operators (`expect` / `check` / `assert`), helper JavaScript, additional protocol or file formats, converting a small OpenAPI / Postman / WSDL / HTTP / Bruno file to `.mmt`, and the `omit` keyword.
16
16
 
17
17
  ## Professional
18
18
 
@@ -333,16 +333,70 @@
333
333
  "title": "Control Flow — repeat and delay"
334
334
  },
335
335
  {
336
- "path": "intermediate/12_random_values/api/random_echo.mmt",
336
+ "path": "intermediate/12_dynamic_values/api/current.mmt",
337
337
  "type": "api",
338
338
  "category": "intermediate",
339
- "title": "Random Echo"
339
+ "title": "Current values"
340
340
  },
341
341
  {
342
- "path": "intermediate/12_random_values/random_values_test.mmt",
342
+ "path": "intermediate/12_dynamic_values/api/random_identity.mmt",
343
+ "type": "api",
344
+ "category": "intermediate",
345
+ "title": "Random identity"
346
+ },
347
+ {
348
+ "path": "intermediate/12_dynamic_values/api/random_network.mmt",
349
+ "type": "api",
350
+ "category": "intermediate",
351
+ "title": "Random network"
352
+ },
353
+ {
354
+ "path": "intermediate/12_dynamic_values/api/random_time.mmt",
355
+ "type": "api",
356
+ "category": "intermediate",
357
+ "title": "Random time"
358
+ },
359
+ {
360
+ "path": "intermediate/12_dynamic_values/api/random_types.mmt",
361
+ "type": "api",
362
+ "category": "intermediate",
363
+ "title": "Random types"
364
+ },
365
+ {
366
+ "path": "intermediate/12_dynamic_values/current_test.mmt",
367
+ "type": "test",
368
+ "category": "intermediate",
369
+ "title": "Current values"
370
+ },
371
+ {
372
+ "path": "intermediate/12_dynamic_values/random_identity_test.mmt",
373
+ "type": "test",
374
+ "category": "intermediate",
375
+ "title": "Random identity"
376
+ },
377
+ {
378
+ "path": "intermediate/12_dynamic_values/random_network_test.mmt",
379
+ "type": "test",
380
+ "category": "intermediate",
381
+ "title": "Random network"
382
+ },
383
+ {
384
+ "path": "intermediate/12_dynamic_values/random_time_test.mmt",
385
+ "type": "test",
386
+ "category": "intermediate",
387
+ "title": "Random time"
388
+ },
389
+ {
390
+ "path": "intermediate/12_dynamic_values/random_types_test.mmt",
343
391
  "type": "test",
344
392
  "category": "intermediate",
345
- "title": "Random Values"
393
+ "title": "Random types"
394
+ },
395
+ {
396
+ "path": "intermediate/12_dynamic_values/suite.mmt",
397
+ "type": "suite",
398
+ "category": "intermediate",
399
+ "title": "Dynamic values"
346
400
  },
347
401
  {
348
402
  "path": "intermediate/13_stages_and_conditions/api/get_profile.mmt",
@@ -613,6 +667,126 @@
613
667
  "category": "intermediate",
614
668
  "title": "Local quality judge"
615
669
  },
670
+ {
671
+ "path": "intermediate/28_http_body_formats/body_formats_test.mmt",
672
+ "type": "test",
673
+ "category": "intermediate",
674
+ "title": "HTTP body formats"
675
+ },
676
+ {
677
+ "path": "intermediate/28_http_body_formats/post_binary.mmt",
678
+ "type": "api",
679
+ "category": "intermediate",
680
+ "title": "POST binary body"
681
+ },
682
+ {
683
+ "path": "intermediate/28_http_body_formats/post_json.mmt",
684
+ "type": "api",
685
+ "category": "intermediate",
686
+ "title": "POST JSON body"
687
+ },
688
+ {
689
+ "path": "intermediate/28_http_body_formats/post_multipart.mmt",
690
+ "type": "api",
691
+ "category": "intermediate",
692
+ "title": "POST multipart body"
693
+ },
694
+ {
695
+ "path": "intermediate/28_http_body_formats/post_text.mmt",
696
+ "type": "api",
697
+ "category": "intermediate",
698
+ "title": "POST text body"
699
+ },
700
+ {
701
+ "path": "intermediate/28_http_body_formats/post_urlencoded.mmt",
702
+ "type": "api",
703
+ "category": "intermediate",
704
+ "title": "POST urlencoded body"
705
+ },
706
+ {
707
+ "path": "intermediate/28_http_body_formats/post_xml.mmt",
708
+ "type": "api",
709
+ "category": "intermediate",
710
+ "title": "POST XML body"
711
+ },
712
+ {
713
+ "path": "intermediate/28_http_body_formats/post_xmle.mmt",
714
+ "type": "api",
715
+ "category": "intermediate",
716
+ "title": "POST expanded XML body"
717
+ },
718
+ {
719
+ "path": "intermediate/29_suite_tag_filter/nested/auth.mmt",
720
+ "type": "suite",
721
+ "category": "intermediate",
722
+ "title": "Nested auth"
723
+ },
724
+ {
725
+ "path": "intermediate/29_suite_tag_filter/nested/login.mmt",
726
+ "type": "test",
727
+ "category": "intermediate",
728
+ "title": "Nested login"
729
+ },
730
+ {
731
+ "path": "intermediate/29_suite_tag_filter/nested/profile.mmt",
732
+ "type": "test",
733
+ "category": "intermediate",
734
+ "title": "Nested profile"
735
+ },
736
+ {
737
+ "path": "intermediate/29_suite_tag_filter/suite_ci.mmt",
738
+ "type": "suite",
739
+ "category": "intermediate",
740
+ "title": "Suite tag filter — CI"
741
+ },
742
+ {
743
+ "path": "intermediate/29_suite_tag_filter/suite_smoke.mmt",
744
+ "type": "suite",
745
+ "category": "intermediate",
746
+ "title": "Suite tag filter — smoke"
747
+ },
748
+ {
749
+ "path": "intermediate/29_suite_tag_filter/suite.mmt",
750
+ "type": "suite",
751
+ "category": "intermediate",
752
+ "title": "Suite tag filter — all"
753
+ },
754
+ {
755
+ "path": "intermediate/29_suite_tag_filter/tests/api_echo.mmt",
756
+ "type": "test",
757
+ "category": "intermediate",
758
+ "title": "API echo"
759
+ },
760
+ {
761
+ "path": "intermediate/29_suite_tag_filter/tests/flaky_echo.mmt",
762
+ "type": "test",
763
+ "category": "intermediate",
764
+ "title": "Flaky echo"
765
+ },
766
+ {
767
+ "path": "intermediate/29_suite_tag_filter/tests/slow_status.mmt",
768
+ "type": "test",
769
+ "category": "intermediate",
770
+ "title": "Slow status"
771
+ },
772
+ {
773
+ "path": "intermediate/29_suite_tag_filter/tests/smoke_get.mmt",
774
+ "type": "test",
775
+ "category": "intermediate",
776
+ "title": "Smoke GET"
777
+ },
778
+ {
779
+ "path": "intermediate/29_suite_tag_filter/tests/smoke_post.mmt",
780
+ "type": "test",
781
+ "category": "intermediate",
782
+ "title": "Smoke POST"
783
+ },
784
+ {
785
+ "path": "intermediate/29_suite_tag_filter/tests/untagged.mmt",
786
+ "type": "test",
787
+ "category": "intermediate",
788
+ "title": "Untagged"
789
+ },
616
790
  {
617
791
  "path": "professional/01_report_generation/api/echo.mmt",
618
792
  "type": "api",
@@ -0,0 +1,89 @@
1
+ # Dynamic Values
2
+
3
+ Demonstrates built-in `r:` (random) and `c:` (current) tokens in YAML files. These tokens resolve at runtime without a `js` step.
4
+
5
+ Random tokens are split by kind. Current tokens live in their own test. Nearby random/current timestamps are checked with `=5s~`.
6
+
7
+ ## Structure
8
+
9
+ ```
10
+ 12_dynamic_values/
11
+ ├── api/
12
+ │ ├── random_types.mmt # Primitive and identifier r: tokens
13
+ │ ├── random_network.mmt # Network r: tokens
14
+ │ ├── random_identity.mmt # People, place, business, and text r: tokens
15
+ │ ├── random_time.mmt # Calendar and epoch r: tokens plus current times
16
+ │ └── current.mmt # c: clock and locale tokens
17
+ ├── random_types_test.mmt
18
+ ├── random_network_test.mmt
19
+ ├── random_identity_test.mmt
20
+ ├── random_time_test.mmt
21
+ ├── current_test.mmt
22
+ ├── suite.mmt
23
+ └── README.md
24
+ ```
25
+
26
+ ## Files
27
+
28
+ | File | Description |
29
+ |---|---|
30
+ | `api/random_types.mmt` | `r:uuid`, `r:int`, `r:bool`, `r:float`, `r:alphanumeric`, `r:password` |
31
+ | `api/random_network.mmt` | `r:domain`, `r:url`, `r:mac`, `r:ip`, `r:hostname`, `r:user_agent` |
32
+ | `api/random_identity.mmt` | `r:email`, names, address, company, and text tokens |
33
+ | `api/random_time.mmt` | Ranged and near-now datetimes/epochs, plus `c:` times for proximity |
34
+ | `api/current.mmt` | `c:utc_datetime`, offsets such as `(+2s)` / `(+1h)`, date, time, epoch, timezone |
35
+ | `*_test.mmt` | Calls the matching API and validates the echoed values |
36
+ | `suite.mmt` | Runs every dynamic-values test together |
37
+
38
+ ## Example tokens
39
+
40
+ ```yaml
41
+ body:
42
+ id: r:uuid
43
+ lucky_number: r:int(10,20)
44
+ email: r:email
45
+ random_url: r:url
46
+ datetime_near_now: r:utc_datetime_now(2s)
47
+ created_utc: c:utc_datetime
48
+ created_utc_plus_2s: c:utc_datetime(+2s)
49
+ ```
50
+
51
+ ```yaml
52
+ - check: ${result.datetime_near_now} =5s~ ${result.created_utc}
53
+ - check: ${result.created_utc} !5s~ ${result.created_utc_plus_1h}
54
+ ```
55
+
56
+ ## Key concepts
57
+
58
+ - **Standalone tokens** keep their native type. For example, `r:int` becomes a number and `r:bool` becomes a boolean.
59
+ - **Embedded tokens** must use `<< >>`. In this example, `req-<<r:uuid>>` generates a string with a random suffix.
60
+ - **Parameterized generators** use parentheses for lengths and ranges, such as `r:int(10,20)` and `r:alphanumeric(12)`.
61
+ - **Local vs UTC** — `r:datetime` / `c:datetime` use local time; `r:utc_datetime` / `c:utc_datetime` use UTC and end in `Z`.
62
+ - **Ranges around now** accept combined durations such as `2s` or `1h1m`.
63
+ - **Current offsets** use a signed duration: `c:utc_datetime(+2s)`, `c:date(+7d)`.
64
+ - **Time comparison** — `=5s~` / `!5s~` check that two times differ by at most (or more than) the velocity. A duration is required; bare `=~` is the deprecated as-string operator.
65
+ - **No JS required** — tokens can be used directly in API `body`, `headers`, `query`, `cookies`, `url`, and input defaults.
66
+
67
+ ## How to use
68
+
69
+ ### In VS Code
70
+
71
+ 1. Open any `*_test.mmt` file, or `suite.mmt` to run them all.
72
+ 2. Click **Run**.
73
+ 3. Inspect the response to see generated values change on each run.
74
+
75
+ ### With the CLI
76
+
77
+ ```sh
78
+ npx testlight run examples/intermediate/12_dynamic_values/random_types_test.mmt
79
+ npx testlight run examples/intermediate/12_dynamic_values/random_time_test.mmt
80
+ npx testlight run examples/intermediate/12_dynamic_values/current_test.mmt
81
+ npx testlight run examples/intermediate/12_dynamic_values/suite.mmt
82
+ ```
83
+
84
+ ## Next steps
85
+
86
+ - See [Check operators](../23_check_operators/) for a fixed-payload `=5s~` / `!5s~` example.
87
+ - See [Simple Mock Server](../18_simple_mock_server/) for random tokens in mock server responses.
88
+ - See [JavaScript Helpers](../14_javascript_helpers/) for the JavaScript `Random.*` helpers.
89
+ - See [Dynamic values](../../../docs/features/dynamic-values/index.md) for the full token list.
@@ -0,0 +1,29 @@
1
+ type: api
2
+ title: Current values
3
+ description: "Sends c: clock and locale tokens, including offsets, and extracts the echoed values"
4
+ outputs:
5
+ created_utc: body.body.created_utc
6
+ created_utc_plus_2s: body.body.created_utc_plus_2s
7
+ created_utc_plus_1h: body.body.created_utc_plus_1h
8
+ created_local: body.body.created_local
9
+ created_date: body.body.created_date
10
+ created_time: body.body.created_time
11
+ created_epoch: body.body.created_epoch
12
+ timezone: body.body.timezone
13
+ utc_offset: body.body.utc_offset
14
+ year: body.body.year
15
+ request_method: body.method
16
+ url: https://test.mmt.dev/echo
17
+ method: post
18
+ format: json
19
+ body:
20
+ created_utc: c:utc_datetime
21
+ created_utc_plus_2s: c:utc_datetime(+2s)
22
+ created_utc_plus_1h: c:utc_datetime(+1h)
23
+ created_local: c:datetime
24
+ created_date: c:date
25
+ created_time: c:time
26
+ created_epoch: c:epoch
27
+ timezone: c:timezone
28
+ utc_offset: c:utc_offset
29
+ year: c:year
@@ -0,0 +1,29 @@
1
+ type: api
2
+ title: Random identity
3
+ description: "Sends people, place, business, and text r: tokens and extracts the echoed values"
4
+ outputs:
5
+ email: body.body.email
6
+ name: body.body.name
7
+ username: body.body.username
8
+ company: body.body.company
9
+ job_title: body.body.job_title
10
+ postal_code: body.body.postal_code
11
+ street_address: body.body.street_address
12
+ random_word: body.body.random_word
13
+ random_sentence: body.body.random_sentence
14
+ random_paragraph: body.body.random_paragraph
15
+ request_method: body.method
16
+ url: https://test.mmt.dev/echo
17
+ method: post
18
+ format: json
19
+ body:
20
+ email: r:email
21
+ name: r:first_name
22
+ username: r:username
23
+ company: r:company
24
+ job_title: r:job_title
25
+ postal_code: r:postal_code
26
+ street_address: r:street_address
27
+ random_word: r:word
28
+ random_sentence: r:sentence
29
+ random_paragraph: r:paragraph
@@ -0,0 +1,21 @@
1
+ type: api
2
+ title: Random network
3
+ description: "Sends network r: tokens and extracts the echoed values"
4
+ outputs:
5
+ domain: body.body.domain
6
+ random_url: body.body.random_url
7
+ mac: body.body.mac
8
+ user_agent: body.body.user_agent
9
+ ip: body.body.ip
10
+ hostname: body.body.hostname
11
+ request_method: body.method
12
+ url: https://test.mmt.dev/echo
13
+ method: post
14
+ format: json
15
+ body:
16
+ domain: r:domain
17
+ random_url: r:url
18
+ mac: r:mac
19
+ user_agent: r:user_agent
20
+ ip: r:ip
21
+ hostname: r:hostname
@@ -0,0 +1,27 @@
1
+ type: api
2
+ title: Random time
3
+ description: "Sends calendar and epoch r: tokens plus a current timestamp for proximity checks"
4
+ outputs:
5
+ random_datetime: body.body.random_datetime
6
+ random_utc_datetime: body.body.random_utc_datetime
7
+ datetime_near_now: body.body.datetime_near_now
8
+ utc_datetime_near_now: body.body.utc_datetime_near_now
9
+ ranged_epoch: body.body.ranged_epoch
10
+ epoch_near_now: body.body.epoch_near_now
11
+ current_utc: body.body.current_utc
12
+ current_local: body.body.current_local
13
+ current_epoch: body.body.current_epoch
14
+ request_method: body.method
15
+ url: https://test.mmt.dev/echo
16
+ method: post
17
+ format: json
18
+ body:
19
+ random_datetime: r:datetime(2026-01-01,2026-12-31)
20
+ random_utc_datetime: r:utc_datetime(2026-01-01T00:00:00Z,2026-12-31T23:59:59Z)
21
+ datetime_near_now: r:datetime_now(2s)
22
+ utc_datetime_near_now: r:utc_datetime_now(2s)
23
+ ranged_epoch: r:epoch(1700000000,1800000000)
24
+ epoch_near_now: r:epoch_now(2s)
25
+ current_utc: c:utc_datetime
26
+ current_local: c:datetime
27
+ current_epoch: c:epoch
@@ -0,0 +1,25 @@
1
+ type: api
2
+ title: Random types
3
+ description: "Sends primitive and identifier r: tokens and extracts the echoed values"
4
+ outputs:
5
+ id: body.body.id
6
+ lucky_number: body.body.lucky_number
7
+ active: body.body.active
8
+ request_id: body.body.request_id
9
+ bounded_int: body.body.bounded_int
10
+ random_float: body.body.random_float
11
+ short_code: body.body.short_code
12
+ password: body.body.password
13
+ request_method: body.method
14
+ url: https://test.mmt.dev/echo
15
+ method: post
16
+ format: json
17
+ body:
18
+ id: r:uuid
19
+ lucky_number: r:int
20
+ active: r:bool
21
+ request_id: req-<<r:uuid>>
22
+ bounded_int: r:int(10,20)
23
+ random_float: r:float(0,1)
24
+ short_code: r:alphanumeric(12)
25
+ password: r:password(20)
@@ -0,0 +1,22 @@
1
+ type: test
2
+ title: Current values
3
+ description: "Calls an API that uses c: tokens and checks nearby times with =5s~"
4
+ import:
5
+ currentValues: ./api/current.mmt
6
+ steps:
7
+ - call: currentValues
8
+ id: result
9
+ expect:
10
+ request_method: POST
11
+ created_utc: =$ Z
12
+ created_utc_plus_2s: =$ Z
13
+ created_utc_plus_1h: =$ Z
14
+ created_local: != null
15
+ created_date: =^ 20
16
+ created_time: '=* ^[0-9]{2}:[0-9]{2}:[0-9]{2}$'
17
+ created_epoch: != null
18
+ timezone: != null
19
+ utc_offset: != null
20
+ year: >= 2026
21
+ - check: ${result.created_utc} =5s~ ${result.created_utc_plus_2s}
22
+ - check: ${result.created_utc} !5s~ ${result.created_utc_plus_1h}
@@ -0,0 +1,20 @@
1
+ type: test
2
+ title: Random identity
3
+ description: "Calls an API that uses people, place, business, and text r: tokens"
4
+ import:
5
+ randomIdentity: ./api/random_identity.mmt
6
+ steps:
7
+ - call: randomIdentity
8
+ id: result
9
+ expect:
10
+ request_method: POST
11
+ email: =C @
12
+ name: != null
13
+ username: =C .
14
+ company: != null
15
+ job_title: != null
16
+ postal_code: =* ^[0-9]{5}$
17
+ street_address: =$ Street
18
+ random_word: != null
19
+ random_sentence: =$ .
20
+ random_paragraph: =C .
@@ -0,0 +1,16 @@
1
+ type: test
2
+ title: Random network
3
+ description: "Calls an API that uses network r: tokens"
4
+ import:
5
+ randomNetwork: ./api/random_network.mmt
6
+ steps:
7
+ - call: randomNetwork
8
+ id: result
9
+ expect:
10
+ request_method: POST
11
+ domain: =C .
12
+ random_url: =^ https://
13
+ mac: =* ^([0-9a-f]{2}:){5}[0-9a-f]{2}$
14
+ user_agent: != null
15
+ ip: '=* ^[0-9]{1,3}(.[0-9]{1,3}){3}$'
16
+ hostname: =C .
@@ -0,0 +1,24 @@
1
+ type: test
2
+ title: Random time
3
+ description: "Calls an API that uses calendar and epoch r: tokens and checks times with =5s~"
4
+ import:
5
+ randomTime: ./api/random_time.mmt
6
+ steps:
7
+ - call: randomTime
8
+ id: result
9
+ expect:
10
+ request_method: POST
11
+ random_datetime: =^ 2026-
12
+ random_utc_datetime:
13
+ - =^ 2026-
14
+ - =$ Z
15
+ datetime_near_now: != null
16
+ utc_datetime_near_now: =$ Z
17
+ ranged_epoch:
18
+ - >= 1700000000
19
+ - <= 1800000000
20
+ epoch_near_now: != null
21
+ current_utc: =$ Z
22
+ - check: ${result.utc_datetime_near_now} =5s~ ${result.current_utc}
23
+ - check: ${result.datetime_near_now} =5s~ ${result.current_local}
24
+ - check: ${result.epoch_near_now} =5s~ ${result.current_epoch}
@@ -0,0 +1,21 @@
1
+ type: test
2
+ title: Random types
3
+ description: "Calls an API that uses primitive and identifier r: tokens"
4
+ import:
5
+ randomTypes: ./api/random_types.mmt
6
+ steps:
7
+ - call: randomTypes
8
+ id: result
9
+ expect:
10
+ request_method: POST
11
+ id: != null
12
+ lucky_number: != null
13
+ request_id: =^ req-
14
+ bounded_int:
15
+ - >= 10
16
+ - <= 20
17
+ random_float:
18
+ - >= 0
19
+ - <= 1
20
+ short_code: =# 12
21
+ password: =# 20
@@ -0,0 +1,9 @@
1
+ type: suite
2
+ title: Dynamic values
3
+ description: Runs the random-type, network, identity, time, and current token examples
4
+ items:
5
+ - random_types_test.mmt
6
+ - random_network_test.mmt
7
+ - random_identity_test.mmt
8
+ - random_time_test.mmt
9
+ - current_test.mmt
@@ -37,4 +37,4 @@ stages:
37
37
  inputs:
38
38
  token: ${doLogin.token}
39
39
  expect:
40
- status_code: 200
40
+ status_code: == 200
@@ -7,7 +7,7 @@ Intermediate example that exercises every comparison operator against a live ech
7
7
  | File | Description |
8
8
  |---|---|
9
9
  | `api/sample.mmt` | POST echo API with a fixed JSON body and named outputs |
10
- | `operators_test.mmt` | Test covering numeric, string, membership, regex, length, and fuzzy operators |
10
+ | `operators_test.mmt` | Test covering numeric, string, membership, regex, length, fuzzy, and time operators |
11
11
 
12
12
  ## How to use
13
13
 
@@ -28,4 +28,5 @@ npx testlight run examples/intermediate/23_check_operators/operators_test.mmt
28
28
  - **`check`** — log failures and continue.
29
29
  - **`assert`** — stop the flow on failure.
30
30
  - **Fuzzy operators** — `>N%` (at least N% similar) and `<N%` (less than N% similar); UI forms are `>%` / `<%` with a percent selector.
31
+ - **Time operators** — `=5s~` / `!5s~` compare ISO datetimes, epoch values, or quoted `HH:mm[:ss]` times within a velocity. A duration is required. Compare two outputs (`${res.created_at} =5s~ ${res.updated_at}`), a literal (`=5s~ 2026-09-18T12:00:02Z`), or a current token (`=5s~ c:utc_datetime`). Quote operators that start with `!` (for example `"!5s~"`).
31
32
  - **YAML quoting** — operators that start with `>` (including `>80%` and `>%`) must be quoted in YAML.
@@ -11,6 +11,13 @@ outputs:
11
11
  tags: body.body.tags
12
12
  message: body.body.message
13
13
  path: body.body.path
14
+ created_at: body.body.created_at
15
+ updated_at: body.body.updated_at
16
+ expired_at: body.body.expired_at
17
+ opened_at: body.body.opened_at
18
+ closed_at: body.body.closed_at
19
+ sent_at: body.body.sent_at
20
+ sent_epoch: body.body.sent_epoch
14
21
  url: https://test.mmt.dev/echo
15
22
  method: post
16
23
  format: json
@@ -25,3 +32,10 @@ body:
25
32
  - demo
26
33
  message: Hello world from Multimeter
27
34
  path: /api/v1/users
35
+ created_at: "2026-09-18T12:00:00Z"
36
+ updated_at: "2026-09-18T12:00:02Z"
37
+ expired_at: "2026-09-18T13:00:00Z"
38
+ opened_at: "14:30:00"
39
+ closed_at: "14:30:03"
40
+ sent_at: c:utc_datetime
41
+ sent_epoch: c:epoch
@@ -20,6 +20,10 @@ steps:
20
20
  path: =* /users/
21
21
  message: >60% Hello world from MMT
22
22
  tags: =# 3
23
+ created_at: =5s~ 2026-09-18T12:00:02Z
24
+ updated_at: "!5s~ 2026-09-18T13:00:00Z"
25
+ sent_at: =5s~ c:utc_datetime
26
+ sent_epoch: =5s~ c:epoch
23
27
  - assert: ${res.status_code} == 200
24
28
  - assert: ${res.score} != 0
25
29
  - assert: ${res.role} !@ guest,user
@@ -30,6 +34,9 @@ steps:
30
34
  - check: ${res.path} !$ /admin
31
35
  - check: ${res.message} !* /error/i
32
36
  - check: ${res.tags} !# 0
37
+ - check: ${res.created_at} =5s~ ${res.updated_at}
38
+ - check: ${res.created_at} !5s~ ${res.expired_at}
39
+ - check: ${res.opened_at} =5s~ ${res.closed_at}
33
40
  - check: ${res.role} =@ admin
34
41
  - check: ${res.name} <40% completely-different
35
42
  - check:
@@ -42,6 +49,21 @@ steps:
42
49
  actual: ${res.message}
43
50
  operator: <50%
44
51
  expected: Hi Universe
52
+ - check:
53
+ title: Times within 5s
54
+ actual: ${res.created_at}
55
+ operator: =5s~
56
+ expected: ${res.updated_at}
57
+ - check:
58
+ title: Times not within 5s
59
+ actual: ${res.created_at}
60
+ operator: "!5s~"
61
+ expected: ${res.expired_at}
62
+ - check:
63
+ title: Sent just now
64
+ actual: ${res.sent_at}
65
+ operator: =5s~
66
+ expected: c:utc_datetime
45
67
  - check:
46
68
  title: Path starts with /api/
47
69
  actual: ${res.path}
@@ -21,7 +21,7 @@ steps:
21
21
  username: i:user
22
22
  password: i:pass
23
23
  expect:
24
- status: =~ "200"
24
+ status: =S "200"
25
25
  token: != null
26
26
  - js: |
27
27
  outputs.token = auth.token