@schwabyio/gta 0.12.0 → 0.14.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,563 @@
1
+ # The `gta` functions
2
+
3
+ Code in a collection runs in two places: `before.script`, before a request is sent, and
4
+ `tests`, after its response arrives. Both have a `gta` object, with nothing to import or
5
+ load. This document lists every function on it.
6
+
7
+ ```yaml
8
+ - name: get user
9
+ GET: '{{baseUrl}}/users/7'
10
+ before:
11
+ script: |
12
+ gta.set('traceId', gta.uuidv7())
13
+ tests: |
14
+ gta.expectResponseStatusCodeToBe(200)
15
+ gta.expectResponseBodyToHaveProperty('user.name', 'Ada')
16
+ gta.expectResponseBodyToHaveProperty('user.email', 'userEmail', 'setAsCollectionVariable')
17
+ ```
18
+
19
+ [SPEC.md](./SPEC.md) specifies the files this code lives in. §5 there covers where code
20
+ goes, the order it runs in, and the other globals: `res`, `req`, `assert`, `params`,
21
+ `endpoint` and `checks`.
22
+
23
+ ---
24
+
25
+ ## At a glance
26
+
27
+ | Function | What it does | `tests` | `before.script` |
28
+ | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------- | :-----: | :-------------: |
29
+ | [`expectResponseStatusCodeToBe`](#gtaexpectresponsestatuscodetobe) | Check the status code | ✓ | |
30
+ | [`expectResponseToHaveHeader`](#gtaexpectresponsetohaveheader) | Check a header | ✓ | |
31
+ | [`expectResponseBodyToHaveProperty`](#gtaexpectresponsebodytohaveproperty) | Check a property of the body | ✓ | |
32
+ | [`expectResponseBodyToHaveUnorderedArray`](#gtaexpectresponsebodytohaveunorderedarray) | Check an array holds items, in any order | ✓ | |
33
+ | [`expectResponseBodyToHaveUnorderedArrayNotThisItem`](#gtaexpectresponsebodytohaveunorderedarraynotthisitem) | Check an array holds none of some items | ✓ | |
34
+ | [`sortResponseBodyArrays`](#gtasortresponsebodyarrays) | Sort arrays before the checks after it | ✓ | |
35
+ | [`useStrictValidation`](#gtausestrictvalidation) | Fail when any body property goes unchecked | ✓ | |
36
+ | [`ignoreResponseBodyProperty`](#gtaignoreresponsebodyproperty) | Leave a property out of strict validation | ✓ | |
37
+ | [`ignoreResponseBodyArrayObjectProperty`](#gtaignoreresponsebodyarrayobjectproperty) | The same, for a property of every item of an array | ✓ | |
38
+ | [`test`](#gtatest) | A named check of your own | ✓ | |
39
+ | [`get`](#gtaget) | Read a variable | ✓ | ✓ |
40
+ | [`set`](#gtaset) | Set a variable for the steps after | ✓ | ✓ |
41
+ | [`skip`](#gtaskip) | Send nothing for this step | | ✓ |
42
+ | [`skipRest`](#gtaskiprest) | Skip the steps after this one | ✓ | ✓ |
43
+ | [`flag`](#gtaflag) | Read a feature flag | ✓ | ✓ |
44
+ | [`uuid`](#gtauuid) | A random UUID | ✓ | ✓ |
45
+ | [`uuidv7`](#gtauuidv7) | A time-ordered UUID | ✓ | ✓ |
46
+ | [`randomInt`](#gtarandomint) | A random whole number | ✓ | ✓ |
47
+ | [`date`](#gtadate) | A formatted date, now or offset from now | ✓ | ✓ |
48
+
49
+ ## How calls behave
50
+
51
+ - **Every `tests` script of a step feeds one list of checks**: the collection's, the
52
+ step's, and those of an endpoint base, a base collection or a request set it runs
53
+ under (SPEC.md §2.5–§2.7). Strict validation counts them all together.
54
+ - **A check that fails does not stop the script.** The checks after it still run, and
55
+ the step fails.
56
+ - **A mistake in how a check is called is a failed check** that says what is wrong: an
57
+ unknown `specialHandling`, a length that is not a number, a path that is neither text
58
+ nor a list. The checks after it still run.
59
+ - **Any other error stops the script**: a misspelled function name, a call that belongs
60
+ in the other script, or a JavaScript error. The step is marked errored, with the line,
61
+ and checks made before it are kept.
62
+ - **A script is stopped after 10 seconds.**
63
+ - **Check files** in `checks/` call `gta` too, and what they check is reported on the
64
+ step that called them (SPEC.md §5).
65
+
66
+ ---
67
+
68
+ ## Checking the response
69
+
70
+ These run in `tests`. Calling one in `before.script` is an error, since nothing has been
71
+ received yet.
72
+
73
+ ### Paths
74
+
75
+ A path finds a property in the body:
76
+
77
+ | Path | Finds |
78
+ | --------------------------------------- | --------------------------------------------------------------- |
79
+ | `user.name` | A property of a property |
80
+ | `groups.0.name` or `groups[0].name` | A property of an array's first item |
81
+ | `sessions[].id` | The property of every item of an array |
82
+ | `jwt.payload["https://x.io/id"]` | A key holding `.`, `[` or `]`, written in brackets as JSON text |
83
+ | `modules[""].edition` | An empty key |
84
+ | `['jwt', 'payload', 'https://x.io/id']` | A list of keys: each item is one key, whatever it holds |
85
+
86
+ In a list of keys a number is its digits, so `['groups', 0, 'name']` reads an index.
87
+ Every function that takes a path takes a list too, and so does `pathToProperty`.
88
+
89
+ The body is read as checks need it. JSON is used as it is. XML is converted: the root
90
+ element is the one top-level key, an element holding only text becomes that text, and a
91
+ repeated element becomes an array. A text or HTML body is one property, `plaintext`.
92
+ An event stream (`text/event-stream`) is a list with one item per event, holding its
93
+ `data` and, when the event set them, its `event` and `id`: `[1].data.price` is the
94
+ second event's price. SPEC.md §3 has the full rules.
95
+
96
+ ### Values
97
+
98
+ - **A body property compares with its type.** `'12345'` does not equal `12345`, and the
99
+ failure says which is which.
100
+ - **The status and headers compare as text**, so `200` and `'200'` are the same.
101
+ - **A `RegExp` is a pattern**, flags included, tested against the value as text.
102
+
103
+ ### `specialHandling`
104
+
105
+ The last argument of a check may be one of these strings, to change what it asks:
106
+
107
+ | String | Means |
108
+ | -------------------------- | ----------------------------------------------------------------------------------- |
109
+ | _(none, and no value)_ | The property or header is present. |
110
+ | `notThisExpectedKey` | It must not be present. Pass `null` as the value. |
111
+ | `notThisExpectedValue` | It must be present, and not equal the value or match the `RegExp`. |
112
+ | `setAsCollectionVariable` | Save it into the variable the value names, for the steps after. It must be present. |
113
+ | `setAsEnvironmentVariable` | The same. Nothing is written to the environment file. |
114
+ | `dateAsEpoch` | Epoch milliseconds, on the calendar day the value names (below). |
115
+ | `dateWithin<X>Sec` | A date within X seconds of the value, as in `dateWithin5Sec`. |
116
+ | `integerWithin<X>` | A number within X of the value, as in `integerWithin2`. |
117
+ | `isArray` | An array. What it holds is not checked. Pass `null` as the value. |
118
+ | `isArrayAndEmpty` | An empty array. Pass `null` as the value. |
119
+ | `isArrayAndNotEmpty` | An array with at least one item. Pass `null` as the value. |
120
+ | `isArrayAndHasLength` | An array of exactly the value's length. |
121
+
122
+ - The status takes `notThisExpectedValue` and the two `setAs…` strings. A header takes
123
+ those and `notThisExpectedKey`. The rest are for body properties.
124
+ - **`dateAsEpoch`**: the property holds epoch milliseconds, as a number or as digits.
125
+ The value is a number of seconds from now (`0` is today, `86400` tomorrow), or a date
126
+ whose first ten characters, `2026-10-01`, must match. Days are the calendar days of
127
+ the machine running the test.
128
+ - **`dateWithin<X>Sec`**: the property and the value are each an ISO 8601 date or epoch
129
+ milliseconds.
130
+ - **Saving** keeps a string, number, boolean or null as it is. An object or array is
131
+ saved as its JSON text.
132
+
133
+ ### `gta.expectResponseStatusCodeToBe`
134
+
135
+ ```js
136
+ gta.expectResponseStatusCodeToBe(expected, specialHandling?)
137
+ ```
138
+
139
+ The status code is `expected`, or matches it.
140
+
141
+ ```js
142
+ gta.expectResponseStatusCodeToBe(201)
143
+ gta.expectResponseStatusCodeToBe(/^2\d\d$/) // any 2xx
144
+ gta.expectResponseStatusCodeToBe(500, 'notThisExpectedValue') // anything but 500
145
+ gta.expectResponseStatusCodeToBe('lastStatus', 'setAsCollectionVariable')
146
+ ```
147
+
148
+ ### `gta.expectResponseToHaveHeader`
149
+
150
+ ```js
151
+ gta.expectResponseToHaveHeader(name, expected?, specialHandling?)
152
+ ```
153
+
154
+ The header is present, and with `expected`, equals it or matches it.
155
+
156
+ ```js
157
+ gta.expectResponseToHaveHeader('X-Request-Id')
158
+ gta.expectResponseToHaveHeader('Content-Type', /^application\/json/)
159
+ gta.expectResponseToHaveHeader('Cache-Control', 'no-store')
160
+ gta.expectResponseToHaveHeader('X-Debug', null, 'notThisExpectedKey')
161
+ gta.expectResponseToHaveHeader('Server', /nginx/, 'notThisExpectedValue')
162
+ gta.expectResponseToHaveHeader('X-Request-Id', 'requestId', 'setAsCollectionVariable')
163
+ ```
164
+
165
+ - The name matches in any case.
166
+ - A header sent more than once is its values joined with `, `, as in `a=1, b=2`.
167
+
168
+ ### `gta.expectResponseBodyToHaveProperty`
169
+
170
+ ```js
171
+ gta.expectResponseBodyToHaveProperty(path, expected?, specialHandling?)
172
+ ```
173
+
174
+ The property at [`path`](#paths) is present, and with `expected`, equals it or matches
175
+ it.
176
+
177
+ ```js
178
+ gta.expectResponseBodyToHaveProperty('user.id')
179
+ gta.expectResponseBodyToHaveProperty('user.name', 'Ada')
180
+ gta.expectResponseBodyToHaveProperty('user.email', /@example\.com$/)
181
+ gta.expectResponseBodyToHaveProperty('user.nickname', null, 'notThisExpectedKey')
182
+ gta.expectResponseBodyToHaveProperty('user.score', 100, 'integerWithin2')
183
+ gta.expectResponseBodyToHaveProperty('user.roles', 2, 'isArrayAndHasLength')
184
+ gta.expectResponseBodyToHaveProperty('user.token', 'token', 'setAsCollectionVariable')
185
+ gta.expectResponseBodyToHaveProperty('items[].status', 'active') // every item
186
+ ```
187
+
188
+ - **`expected` is a string, number, boolean, `null` or `RegExp`.** An object or array
189
+ never equals one. Check its properties one by one, use
190
+ [`expectResponseBodyToHaveUnorderedArray`](#gtaexpectresponsebodytohaveunorderedarray)
191
+ for an array, or compare it in [`gta.test`](#gtatest) with `assert.deepEqual`.
192
+ - **A property whose value is `null` is present.**
193
+ - **A path that runs into a `null` before its end**, such as `phone.number` when `phone`
194
+ is `null`, reads as `null` for a check that the value is `null`. For any other check,
195
+ the property is not present.
196
+ - **A path with `[]` checks every item**, and passes only when each one does. An empty
197
+ array has no items, so the property is not present. Saving one saves every item's
198
+ value, as a JSON list.
199
+
200
+ ### `gta.expectResponseBodyToHaveUnorderedArray`
201
+
202
+ ```js
203
+ gta.expectResponseBodyToHaveUnorderedArray(path, list)
204
+ ```
205
+
206
+ The array at `path` holds what `list` describes, in any order. `list` takes one of two
207
+ forms.
208
+
209
+ **A list of values.** Each must be in the array, which may hold others too. A `RegExp`
210
+ is a pattern, which some item must match:
211
+
212
+ ```js
213
+ gta.expectResponseBodyToHaveUnorderedArray('roles', ['admin', 'editor'])
214
+ gta.expectResponseBodyToHaveUnorderedArray('roles', [/^admin/, 'editor'])
215
+ gta.expectResponseBodyToHaveUnorderedArray('users', [{ name: 'Ada' }, { name: /^Grace/ }])
216
+ ```
217
+
218
+ An object in the list matches an item holding each of its properties with that value, or
219
+ matching it when the value is a `RegExp`. The item may have others. A property that is
220
+ itself an object is matched the same way, so `{ data: { status: 'reversed' } }` finds an
221
+ item whose `data` has that status, whatever else `data` holds. An array compares whole.
222
+ A pattern is tested against the value as text, so it never matches an item that is an
223
+ object or an array.
224
+
225
+ **A list of `{ pathToProperty, expectedValue, specialHandling? }` entries.** Together
226
+ they describe **one** item, property by property, and some item must match every entry.
227
+ Call the function once for each item:
228
+
229
+ ```js
230
+ gta.expectResponseBodyToHaveUnorderedArray('users', [
231
+ { pathToProperty: 'name', expectedValue: 'Ada' },
232
+ { pathToProperty: 'role', expectedValue: /^admin/ },
233
+ { pathToProperty: 'nickname', expectedValue: null, specialHandling: 'notThisExpectedKey' },
234
+ { pathToProperty: 'id', expectedValue: 'adaId', specialHandling: 'setAsCollectionVariable' }
235
+ ])
236
+ ```
237
+
238
+ - `pathToProperty` is a path inside the item. `specialHandling` is any of the strings
239
+ above, and a save takes its value from the item that matched.
240
+ - A property may be named twice: once to check it, and once to save it.
241
+ - **Each call prefers items an earlier call did not match.** Two calls with the same
242
+ description find two items when there are two, so each saves from, and strict
243
+ validation counts, a different one. A sort starts this over.
244
+ - **A list of one `notThisExpectedValue` entry depends on strict validation.** Without
245
+ it, the entry means no item has that value, so an empty array passes. With it, the
246
+ entry means one item whose value is something else, as any list does. The step's last
247
+ call to `useStrictValidation` decides.
248
+
249
+ ### `gta.expectResponseBodyToHaveUnorderedArrayNotThisItem`
250
+
251
+ ```js
252
+ gta.expectResponseBodyToHaveUnorderedArrayNotThisItem(path, list)
253
+ ```
254
+
255
+ No item of the array at `path` matches what `list` describes.
256
+
257
+ ```js
258
+ gta.expectResponseBodyToHaveUnorderedArrayNotThisItem('roles', ['owner', /^guest/])
259
+ gta.expectResponseBodyToHaveUnorderedArrayNotThisItem('users', [
260
+ { pathToProperty: 'status', compareValue: 'deleted' },
261
+ { pathToProperty: 'email', compareValue: /@test\.invalid$/ }
262
+ ])
263
+ ```
264
+
265
+ - A list of values: none of them may be in the array, and no item may match a `RegExp`
266
+ among them. An object in the list may hold patterns too, as above.
267
+ - A list of `{ pathToProperty, compareValue }` entries describes one item. The check
268
+ fails when some item matches every entry. `compareValue` may be a `RegExp`.
269
+ - **The key is `compareValue`, not `expectedValue`.** An entry without it matches no
270
+ item, so the check always passes.
271
+
272
+ ### `gta.sortResponseBodyArrays`
273
+
274
+ ```js
275
+ gta.sortResponseBodyArrays(property)
276
+ ```
277
+
278
+ Sorts every array of objects that holds `property`, anywhere in the body, for the
279
+ checks after it. Use it when an API returns a list in no fixed order and a check names
280
+ an index.
281
+
282
+ ```js
283
+ gta.sortResponseBodyArrays('id')
284
+ gta.expectResponseBodyToHaveProperty('accounts[0].id', 'acct-100')
285
+ ```
286
+
287
+ - `property` may be a path inside each item, such as `id.value`. Arrays inside the items
288
+ are sorted too.
289
+ - Items without the property go last. Values compare alphanumerically, so `Group 2`
290
+ comes before `Group 10`.
291
+ - Checks made before the call see the order as received, and so does `res.body`.
292
+ - Calling it again sorts by the new property first, and by the earlier ones where that
293
+ ties.
294
+ - Called with no property, it sorts nothing and writes a warning to the step's console.
295
+
296
+ ---
297
+
298
+ ## Strict validation
299
+
300
+ ### `gta.useStrictValidation`
301
+
302
+ ```js
303
+ gta.useStrictValidation(enabled?)
304
+ ```
305
+
306
+ Fails the step unless every property of the body is checked, ignored or saved. It
307
+ catches a response that gained a field no test looks at. `enabled` is `true` when left
308
+ out.
309
+
310
+ ```js
311
+ gta.useStrictValidation()
312
+ gta.expectResponseBodyToHaveProperty('id', 'orderId', 'setAsCollectionVariable')
313
+ gta.expectResponseBodyToHaveProperty('status', 'open')
314
+ gta.ignoreResponseBodyProperty('createdAt')
315
+ ```
316
+
317
+ - It adds one check, `Strict: every body property is asserted`, which lists each
318
+ property left over.
319
+ - It is judged after every `tests` script of the step has run, over what any of them
320
+ checked. Put it in the collection's `tests` to cover every step.
321
+ - `null`, `""`, and empty arrays and objects never need a check of their own.
322
+ - A check of a value's content accounts for it and everything inside it. A check of
323
+ shape only (`isArray`, `isArrayAndHasLength`, or that an object is present) does not
324
+ vouch for what is inside.
325
+ - `false` turns it off. The last call wins, so a step can turn off what the collection
326
+ turned on. `'true'` as text counts as `true`, so a variable can decide:
327
+ `gta.useStrictValidation(gta.get('strictValidation'))`.
328
+ - A binary or HTML body has no properties, so strict validation passes.
329
+
330
+ ### `gta.ignoreResponseBodyProperty`
331
+
332
+ ```js
333
+ gta.ignoreResponseBodyProperty(path)
334
+ ```
335
+
336
+ Counts the property at `path`, and everything inside it, as checked, without checking
337
+ it. For values that change on every call, such as timestamps.
338
+
339
+ ```js
340
+ gta.ignoreResponseBodyProperty('meta')
341
+ gta.ignoreResponseBodyProperty('items[].updatedAt')
342
+ ```
343
+
344
+ It changes nothing unless strict validation is on.
345
+
346
+ ### `gta.ignoreResponseBodyArrayObjectProperty`
347
+
348
+ ```js
349
+ gta.ignoreResponseBodyArrayObjectProperty(arrayPath, propertyPath)
350
+ ```
351
+
352
+ The same, for one property of every item of an array.
353
+ `gta.ignoreResponseBodyArrayObjectProperty('items', 'updatedAt')` is
354
+ `gta.ignoreResponseBodyProperty('items[].updatedAt')`.
355
+
356
+ ---
357
+
358
+ ## Checks of your own
359
+
360
+ ### `gta.test`
361
+
362
+ ```js
363
+ gta.test(name, fn)
364
+ ```
365
+
366
+ A named check, for anything the functions above do not cover. It passes unless `fn`
367
+ throws, or the promise it returns rejects, and then the error's message is the reason.
368
+
369
+ ```js
370
+ gta.test('ids are unique', () => {
371
+ const ids = res.body.items.map((item) => item.id)
372
+ assert.equal(new Set(ids).size, ids.length)
373
+ })
374
+
375
+ gta.test('total is the sum of the lines', () => {
376
+ const sum = res.body.lines.reduce((total, line) => total + line.amount, 0)
377
+ assert.equal(res.body.total, sum)
378
+ })
379
+ ```
380
+
381
+ - `assert` is Node's strict `assert`, also reachable as `gta.assert`.
382
+ - `fn` may be `async`. The step waits for it, whether or not the script does.
383
+ - An endpoint base's named checks are never replaced by a step's own (SPEC.md §2.6).
384
+
385
+ ---
386
+
387
+ ## Variables
388
+
389
+ ### `gta.get`
390
+
391
+ ```js
392
+ gta.get(name)
393
+ ```
394
+
395
+ A variable's current value, from whichever layer sets it: the project, the collection,
396
+ the environment, a data file row, or a value saved or set earlier in the run.
397
+ `undefined` when no layer does.
398
+
399
+ ```js
400
+ gta.expectResponseStatusCodeToBe(gta.get('expectedStatus'))
401
+ ```
402
+
403
+ A value comes back with its type. Every value from a CSV data file is text.
404
+
405
+ ### `gta.set`
406
+
407
+ ```js
408
+ gta.set(name, value, options?)
409
+ ```
410
+
411
+ Sets a variable, which `{{name}}` and `gta.get(name)` read from then on: in this step's
412
+ request when it is set in `before.script`, and in every step after.
413
+
414
+ ```js
415
+ gta.set('traceId', gta.uuidv7())
416
+ gta.set(
417
+ 'ids',
418
+ res.body.items.map((item) => item.id)
419
+ ) // saved as '["a","b"]'
420
+ gta.set('adminId', res.body.id, { scope: 'run' })
421
+ ```
422
+
423
+ - **Anything computed goes here**, since `vars` in a file hold plain values only
424
+ (SPEC.md §4). A collection's `before.script` runs before every step, so a value set
425
+ there is fresh for each.
426
+ - A string, number, boolean or `null` is kept as it is. An object or array is saved as
427
+ its JSON text, so `{{ids}}` writes the list into a JSON body, and `forEach: '{{ids}}'`
428
+ sends a request for each item. Read it back in code with `JSON.parse(gta.get('ids'))`.
429
+ `undefined` is saved as `null`.
430
+ - **In a collection with a data file**, a value lasts the rest of its row.
431
+ `{ scope: 'run' }` keeps it for every row after, and for teardown (SPEC.md §2.10).
432
+ Any other `scope` is an error.
433
+ - Nothing is written to a file.
434
+ - SPEC.md §4 gives the order in which layers win.
435
+
436
+ ---
437
+
438
+ ## Skipping steps
439
+
440
+ ### `gta.skip`
441
+
442
+ ```js
443
+ gta.skip(reason?)
444
+ ```
445
+
446
+ In `before.script` only. The request is not sent, and the step is reported as skipped,
447
+ with the reason. A skipped step never fails a run.
448
+
449
+ ```js
450
+ if (!gta.get('adminToken')) gta.skip('no admin account in this environment')
451
+ ```
452
+
453
+ - The script runs to its end. No later `before.script` runs for the step: when the
454
+ collection's skips it, the step's own does not run.
455
+ - In `tests` it is an error, because the request has been sent. Use `gta.skipRest`
456
+ there.
457
+ - To skip a step depending on a feature flag, give it `flags:` (SPEC.md §2.9).
458
+
459
+ ### `gta.skipRest`
460
+
461
+ ```js
462
+ gta.skipRest(reason?)
463
+ ```
464
+
465
+ Skips the steps after this one, each reported as skipped with the reason.
466
+
467
+ ```js
468
+ if (res.status === 404) gta.skipRest('no such account, so nothing more to check')
469
+ ```
470
+
471
+ - In `tests`, this step's own result stands. In `before.script`, this step is skipped
472
+ too.
473
+ - It skips only the current row. The next row of a data file, and teardown, still run.
474
+ - The first reason given stands.
475
+
476
+ ---
477
+
478
+ ## Feature flags
479
+
480
+ ### `gta.flag`
481
+
482
+ ```js
483
+ gta.flag(name)
484
+ ```
485
+
486
+ A feature flag's value in this run: a string, number or boolean, from the environment,
487
+ the flag command or an override (SPEC.md §2.9). Use it to check something different
488
+ when a flag is on, rather than skip a step.
489
+
490
+ ```js
491
+ if (gta.flag('newCheckout')) {
492
+ gta.expectResponseBodyToHaveProperty('total.currency', 'USD')
493
+ }
494
+ ```
495
+
496
+ A flag the run does not know is an error, so a misspelled name stops the script rather
497
+ than quietly reading as off.
498
+
499
+ ---
500
+
501
+ ## Generated values
502
+
503
+ ### `gta.uuid`
504
+
505
+ ```js
506
+ gta.uuid()
507
+ ```
508
+
509
+ A random (version 4) UUID, such as `f397d495-8679-42de-8b69-d6ef86b3f5a1`.
510
+
511
+ ### `gta.uuidv7`
512
+
513
+ ```js
514
+ gta.uuidv7()
515
+ ```
516
+
517
+ A time-ordered (version 7) UUID, such as `01a0f78d-cecb-73c4-ac6e-557141551574`. Ids made
518
+ later sort later, which suits a request id or a record that must read in creation
519
+ order.
520
+
521
+ ### `gta.randomInt`
522
+
523
+ ```js
524
+ gta.randomInt(min, max)
525
+ ```
526
+
527
+ A whole number from `min` to `max`, both included: `gta.randomInt(1, 6)` rolls a die.
528
+
529
+ ### `gta.date`
530
+
531
+ ```js
532
+ gta.date(format, secondsOffset?, timeZone?)
533
+ ```
534
+
535
+ The time now, moved by `secondsOffset` seconds and formatted. `secondsOffset` is `0`
536
+ when left out, and `timeZone` is `'local'`.
537
+
538
+ ```js
539
+ gta.date('%Y-%m-%d') // today: 2026-10-01
540
+ gta.date('%F', 86400, 'utc') // tomorrow, in UTC
541
+ gta.date('%FT%T%z', -3600, 'America/New_York') // an hour ago: 2026-10-01T04:15:00-0400
542
+ ```
543
+
544
+ | Specifier | Gives | Specifier | Gives |
545
+ | --------- | ---------------------------- | --------- | ---------------------------- |
546
+ | `%Y` | Year: `2026` | `%p` | `AM` or `PM` |
547
+ | `%y` | Year, two digits: `26` | `%b` | Month, short: `Oct` |
548
+ | `%m` | Month: `01`–`12` | `%B` | Month: `October` |
549
+ | `%d` | Day: `01`–`31` | `%a` | Weekday, short: `Thu` |
550
+ | `%e` | Day, space-padded: ` 1`–`31` | `%A` | Weekday: `Thursday` |
551
+ | `%H` | Hour: `00`–`23` | `%j` | Day of the year: `001`–`366` |
552
+ | `%I` | Hour: `01`–`12` | `%Z` | Time zone: `PDT` |
553
+ | `%M` | Minute: `00`–`59` | `%z` | Offset from UTC: `-0700` |
554
+ | `%S` | Second: `00`–`59` | `%s` | Epoch seconds |
555
+ | `%L` | Millisecond: `000`–`999` | `%F` | `%Y-%m-%d` |
556
+ | `%%` | `%` | `%T` | `%H:%M:%S` |
557
+
558
+ - A specifier not listed is left as written, so `%Q` stays `%Q` and a typo shows.
559
+ - A negative `secondsOffset` is in the past.
560
+ - `timeZone` is `local`, the time zone of the machine running the test; `utc`; an IANA
561
+ name, such as `America/New_York`; or a military letter. `A` to `M` are 1 to 12 hours
562
+ ahead of UTC, skipping `J`, `N` to `Y` are 1 to 12 hours behind, and `Z` is UTC, so
563
+ `U` is -08:00, not UTC. `IST` is India.