@nestia/e2e 14.0.0 → 14.0.2

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.
@@ -25,18 +25,31 @@ const json_equal_to_1 = require("./internal/json_equal_to");
25
25
  * maintain currying patterns for advanced composition. All provide detailed
26
26
  * error messages for debugging failed assertions.
27
27
  *
28
+ * Processing cost: Scalar assertions use constant bookkeeping; JSON assertions
29
+ * traverse the expected structure, index compares the shared page prefix,
30
+ * search scans the dataset once per sample and sort checks adjacent results.
31
+ * External callbacks own their computation costs.
32
+ *
28
33
  * @author Jeongho Nam - https://github.com/samchon
29
34
  * @example
30
- * ```typescript
31
35
  * // Basic condition testing
32
- * TestValidator.predicate("user should be authenticated", user.isAuthenticated);
36
+ * TestValidator.predicate(
37
+ * "user should be authenticated",
38
+ * user.isAuthenticated,
39
+ * );
33
40
  *
34
41
  * // Equality validation
35
42
  * TestValidator.equals("API response should match expected", x, y);
36
43
  *
37
44
  * // Error validation
38
- * TestValidator.error("should throw on invalid input", () => assertInput(""));
39
- * ```;
45
+ * TestValidator.error("should throw on invalid input", () =>
46
+ * assertInput(""),
47
+ * );
48
+ *
49
+ * @evidence contracts/common.md#principled-implementation Each validator runs a check and throws an `Error` whose message begins with `Bug on <title>`, returning a promise only when its input is asynchronous, so the same function serves synchronous and asynchronous tests; the comparison helpers read values through their JSON form, which is the form an HTTP API delivers.
50
+ * @evidence contracts/common.md#clear-and-simple-design One function per assertion kind, sharing the JSON comparison (`json_equal_to`) and two small module helpers (`is_promise` and `is_sorted`), with the curried forms only where a request must be prepared once and run twice.
51
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Assertions compare the values they are given; none consults a fixture, an entity name, or an environment.
52
+ * @evidence contracts/common.md#meaningful-documentation The namespace prose lists the assertion families and the error message convention, with examples.
40
53
  */
41
54
  var TestValidator;
42
55
  (function (TestValidator) {
@@ -47,19 +60,33 @@ var TestValidator;
47
60
  * boolean, and asynchronous functions returning Promise<boolean>. The return
48
61
  * type is automatically inferred based on the input type.
49
62
  *
63
+ * Processing cost: The condition is evaluated once and asynchronous
64
+ * settlement adds constant bookkeeping without polling or repeating the
65
+ * condition.
66
+ *
67
+ * Reuse: Each invocation must evaluate its supplied task or request to
68
+ * observe current behavior; prior results cannot replace these effectful
69
+ * checks.
70
+ *
71
+ * Resource ownership: The returned promise retains invocation-local state
72
+ * until settlement. The callback owns its external resources, and this
73
+ * validator provides no cancellation or timeout.
74
+ *
50
75
  * @example
51
- * ```typescript
52
76
  * // Synchronous boolean
53
77
  * TestValidator.predicate("user should exist", user !== null);
54
78
  *
55
79
  * // Synchronous function
56
- * TestValidator.predicate("array should be empty", () => arr.length === 0);
80
+ * TestValidator.predicate(
81
+ * "array should be empty",
82
+ * () => arr.length === 0,
83
+ * );
57
84
  *
58
85
  * // Asynchronous function
59
- * await TestValidator.predicate("database should be connected",
60
- * async () => await db.ping()
86
+ * await TestValidator.predicate(
87
+ * "database should be connected",
88
+ * async () => await db.ping(),
61
89
  * );
62
- * ```;
63
90
  *
64
91
  * @param title - Descriptive title used in error messages when validation
65
92
  * fails
@@ -67,6 +94,10 @@ var TestValidator;
67
94
  * function)
68
95
  * @returns Void or Promise<void> based on the input type
69
96
  * @throws Error with descriptive message when condition is not satisfied
97
+ * @evidence contracts/common.md#principled-implementation A boolean, a function returning a boolean, or a function returning a promise of a boolean is evaluated, and only the exact value `true` passes; a false result throws the same `Error` in all three forms, and the asynchronous form rejects with it.
98
+ * @evidence contracts/common.md#clear-and-simple-design One function whose three branches differ only in when the value is available.
99
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The check is on the value the caller gives, with no truthiness coercion.
100
+ * @evidence contracts/common.md#meaningful-documentation The comment documents the three forms, the parameters, the return type rule, and the thrown error, with examples.
70
101
  */
71
102
  function predicate(title, condition) {
72
103
  const message = () => `Bug on ${title}: expected condition is not satisfied.`;
@@ -90,7 +121,7 @@ var TestValidator;
90
121
  if (flag === true)
91
122
  resolve();
92
123
  else
93
- reject(message());
124
+ reject(new Error(message()));
94
125
  })
95
126
  .catch(reject);
96
127
  });
@@ -99,30 +130,45 @@ var TestValidator;
99
130
  /**
100
131
  * Validates deep equality between two values using JSON comparison.
101
132
  *
102
- * Performs recursive comparison of objects and arrays. Supports an optional
133
+ * Performs recursive comparison of objects and arrays through their JSON
134
+ * form. The comparison walks the keys of the first value, so a key that only
135
+ * the second value has is ignored, which is why the second value may be a
136
+ * subtype of the first. Keys whose value in the first value is `undefined`
137
+ * are skipped, and arrays must have the same length. Supports an optional
103
138
  * exception filter to ignore specific keys during comparison. Useful for
104
139
  * validating API responses, data transformations, and object state changes.
105
140
  *
141
+ * Processing cost: The shared JSON walker traverses the first value and its
142
+ * corresponding second-value positions once, with recursion and accessor
143
+ * output proportional to visited structure; serialization occurs only on
144
+ * failure.
145
+ *
106
146
  * @example
107
- * ```typescript
108
147
  * // Basic equality
109
- * TestValidator.equals("response should match expected", expectedUser, actualUser);
148
+ * TestValidator.equals(
149
+ * "response should match expected",
150
+ * expectedUser,
151
+ * actualUser,
152
+ * );
110
153
  *
111
154
  * // Ignore timestamps in comparison
112
- * TestValidator.equals("user data should match", expectedUser, actualUser,
113
- * (key) => key === "updatedAt"
155
+ * TestValidator.equals(
156
+ * "user data should match",
157
+ * expectedUser,
158
+ * actualUser,
159
+ * (key) => key === "updatedAt",
114
160
  * );
115
161
  *
116
162
  * // Validate API response structure
117
- * TestValidator.equals("API response structure",
163
+ * TestValidator.equals(
164
+ * "API response structure",
165
+ * { id: 1, name: "John" },
118
166
  * { id: 1, name: "John" },
119
- * { id: 1, name: "John" }
120
167
  * );
121
168
  *
122
169
  * // Type-safe nullable comparisons
123
170
  * const nullableData: { name: string } | null = getData();
124
171
  * TestValidator.equals("nullable check", nullableData, null);
125
- * ```;
126
172
  *
127
173
  * @param title - Descriptive title used in error messages when values differ
128
174
  * @param X - The first value to compare
@@ -130,6 +176,10 @@ var TestValidator;
130
176
  * @param exception - Optional filter function to exclude specific keys from
131
177
  * comparison
132
178
  * @throws Error with detailed diff information when values are not equal
179
+ * @evidence contracts/common.md#principled-implementation Both values are compared through `json_equal_to`, which walks the keys of the first value, so the second value may carry extra keys and the typing `Y extends X` states that; the differing accessors are listed in the error with both values serialized.
180
+ * @evidence contracts/common.md#clear-and-simple-design A thin wrapper over the shared comparison that adds the message.
181
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The exception filter is the caller's; the function contains no key name of its own.
182
+ * @evidence contracts/common.md#meaningful-documentation The comment states the JSON form, the key-walking rule with its consequence for extra keys, the array length rule, and the exception filter, with examples.
133
183
  */
134
184
  function equals(title, X, y, exception) {
135
185
  const diff = (0, json_equal_to_1.json_equal_to)(exception !== null && exception !== void 0 ? exception : (() => false))(X)(y);
@@ -145,27 +195,42 @@ var TestValidator;
145
195
  * Validates deep inequality between two values using JSON comparison.
146
196
  *
147
197
  * Performs recursive comparison of objects and arrays to ensure they are NOT
148
- * equal. Supports an optional exception filter to ignore specific keys during
149
- * comparison. Useful for validating that data has changed, objects are
198
+ * equal, by the same rules as {@link equals}: only the keys of the first value
199
+ * are compared, so a key that only the second value has does not make them
200
+ * different. Supports an optional exception filter to ignore specific keys
201
+ * during comparison. Useful for validating that data has changed, objects are
150
202
  * different, or mutations have occurred.
151
203
  *
204
+ * Processing cost: The shared JSON walker visits the first value and
205
+ * corresponding positions once; a failure additionally serializes both values
206
+ * for diagnostics. Space follows visited structure and reported differences.
207
+ *
152
208
  * @example
153
- * ```typescript
154
209
  * // Basic inequality
155
- * TestValidator.notEquals("user should be different after update", originalUser, updatedUser);
210
+ * TestValidator.notEquals(
211
+ * "user should be different after update",
212
+ * originalUser,
213
+ * updatedUser,
214
+ * );
156
215
  *
157
216
  * // Ignore timestamps in comparison
158
- * TestValidator.notEquals("user data should differ", originalUser, modifiedUser,
159
- * (key) => key === "updatedAt"
217
+ * TestValidator.notEquals(
218
+ * "user data should differ",
219
+ * originalUser,
220
+ * modifiedUser,
221
+ * (key) => key === "updatedAt",
160
222
  * );
161
223
  *
162
224
  * // Validate state changes
163
- * TestValidator.notEquals("state should have changed", initialState, currentState);
225
+ * TestValidator.notEquals(
226
+ * "state should have changed",
227
+ * initialState,
228
+ * currentState,
229
+ * );
164
230
  *
165
231
  * // Type-safe nullable comparisons
166
232
  * const mutableData: { count: number } | null = getMutableData();
167
233
  * TestValidator.notEquals("should have changed", mutableData, null);
168
- * ```;
169
234
  *
170
235
  * @param title - Descriptive title used in error messages when values are
171
236
  * equal
@@ -174,6 +239,10 @@ var TestValidator;
174
239
  * @param exception - Optional filter function to exclude specific keys from
175
240
  * comparison
176
241
  * @throws Error when values are equal (indicating validation failure)
242
+ * @evidence contracts/common.md#principled-implementation It runs the same comparison as `equals` and fails when the comparison finds no difference, so two values differing only by extra keys of the second are considered equal and fail.
243
+ * @evidence contracts/common.md#clear-and-simple-design A thin wrapper over the shared comparison with the opposite outcome.
244
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The exception filter is the caller's; the function contains no key name of its own.
245
+ * @evidence contracts/common.md#meaningful-documentation The comment states that the rules are those of `equals`, and gives examples.
177
246
  */
178
247
  function notEquals(title, x, y, exception) {
179
248
  const diff = (0, json_equal_to_1.json_equal_to)(exception !== null && exception !== void 0 ? exception : (() => false))(x)(y);
@@ -192,44 +261,57 @@ var TestValidator;
192
261
  * successfully without throwing an error or rejecting, this validator will
193
262
  * throw an exception. Supports both synchronous and asynchronous functions.
194
263
  *
264
+ * Processing cost: The task is invoked once and its settlement is classified
265
+ * with constant bookkeeping; no retry or repeated execution occurs.
266
+ *
267
+ * Reuse: Each invocation must evaluate its supplied task or request to
268
+ * observe current behavior; prior results cannot replace these effectful
269
+ * checks.
270
+ *
271
+ * Resource ownership: The returned promise retains invocation-local state
272
+ * until settlement. The callback owns its external resources, and this
273
+ * validator provides no cancellation or timeout.
274
+ *
195
275
  * @example
196
- * ```typescript
197
276
  * // Synchronous error validation
198
- * TestValidator.error("should reject invalid email",
199
- * () => validateEmail("invalid-email")
277
+ * TestValidator.error("should reject invalid email", () =>
278
+ * validateEmail("invalid-email"),
200
279
  * );
201
280
  *
202
281
  * // Asynchronous error validation
203
- * await TestValidator.error("should reject unauthorized access",
204
- * async () => await api.functional.getSecretData()
282
+ * await TestValidator.error(
283
+ * "should reject unauthorized access",
284
+ * async () => await api.functional.getSecretData(),
205
285
  * );
206
286
  *
207
287
  * // Validate input validation
208
- * TestValidator.error("should throw on empty string",
209
- * () => processRequiredInput("")
288
+ * TestValidator.error("should throw on empty string", () =>
289
+ * processRequiredInput(""),
210
290
  * );
211
- * ```;
212
291
  *
213
292
  * @param title - Descriptive title used in error messages when no error
214
293
  * occurs
215
294
  * @param task - The function that should throw an error or reject
216
295
  * @returns Void or Promise<void> based on the input type
217
296
  * @throws Error when the task function does not throw an error or reject
297
+ * @evidence contracts/common.md#principled-implementation The task is run inside a `try` so that only its own throw counts, a promise is awaited and a rejection counts as the expected error, and a task that neither throws nor rejects raises the failure outside the `try`, so the failure cannot be swallowed.
298
+ * @evidence contracts/common.md#clear-and-simple-design One function with a synchronous and an asynchronous path selected by the returned value.
299
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Any error satisfies it, as documented, and no error type is special-cased.
300
+ * @evidence contracts/common.md#meaningful-documentation The comment states that an exception is required, the return type rule, and the thrown error, with examples.
218
301
  */
219
302
  function error(title, task) {
220
303
  const message = () => `Bug on ${title}: exception must be thrown.`;
304
+ // only the task's own throw is caught: the failure below must escape
305
+ let output;
221
306
  try {
222
- const output = task();
223
- if (is_promise(output))
224
- return new Promise((resolve, reject) => output
225
- .catch(() => resolve())
226
- .then(() => reject(new Error(message()))));
227
- else
228
- throw new Error(message());
307
+ output = task();
229
308
  }
230
309
  catch (_a) {
231
310
  return undefined;
232
311
  }
312
+ if (is_promise(output))
313
+ return new Promise((resolve, reject) => output.catch(() => resolve()).then(() => reject(new Error(message()))));
314
+ throw new Error(message());
233
315
  }
234
316
  TestValidator.error = error;
235
317
  /**
@@ -239,23 +321,40 @@ var TestValidator;
239
321
  * function throws an HttpError with one of the specified status codes. Useful
240
322
  * for testing API endpoints, authentication, and authorization logic.
241
323
  *
324
+ * Processing cost: The task is invoked once. Classification reads the thrown
325
+ * shape once and scans the expected status list until a match, using O(S)
326
+ * worst-case status comparisons and constant auxiliary state.
327
+ *
328
+ * Reuse: Each invocation must evaluate its supplied task or request to
329
+ * observe current behavior; prior results cannot replace these effectful
330
+ * checks.
331
+ *
332
+ * Resource ownership: The returned promise retains invocation-local state
333
+ * until settlement. The callback owns its external resources, and this
334
+ * validator provides no cancellation or timeout.
335
+ *
242
336
  * @example
243
- * ```typescript
244
337
  * // Validate 401 Unauthorized
245
- * await TestValidator.httpError("should return 401 for invalid token", 401,
246
- * async () => await api.functional.getProtectedResource("invalid-token")
338
+ * await TestValidator.httpError(
339
+ * "should return 401 for invalid token",
340
+ * 401,
341
+ * async () =>
342
+ * await api.functional.getProtectedResource("invalid-token"),
247
343
  * );
248
344
  *
249
345
  * // Validate multiple possible error codes
250
- * await TestValidator.httpError("should return client error", [400, 404, 422],
251
- * async () => await api.functional.updateNonexistentResource(data)
346
+ * await TestValidator.httpError(
347
+ * "should return client error",
348
+ * [400, 404, 422],
349
+ * async () => await api.functional.updateNonexistentResource(data),
252
350
  * );
253
351
  *
254
352
  * // Validate server errors
255
- * TestValidator.httpError("should handle server errors", [500, 502, 503],
256
- * () => callFaultyEndpoint()
353
+ * TestValidator.httpError(
354
+ * "should handle server errors",
355
+ * [500, 502, 503],
356
+ * () => callFaultyEndpoint(),
257
357
  * );
258
- * ```;
259
358
  *
260
359
  * @param title - Descriptive title used in error messages
261
360
  * @param status - Expected status code(s), can be a single number or array
@@ -263,6 +362,10 @@ var TestValidator;
263
362
  * @returns Void or Promise<void> based on the input type
264
363
  * @throws Error when function doesn't throw HttpError or status code doesn't
265
364
  * match
365
+ * @evidence contracts/common.md#principled-implementation The thrown value must expose a constructor named `HttpError` and one expected numeric status, matched by name because generated SDKs define their own classes. Missing or throwing properties make classification fail instead of escaping the expectation. The returned promise chain propagates rejection-handler failures without leaving a separately bridged promise pending.
366
+ * @evidence contracts/common.md#clear-and-simple-design One predicate reads the thrown value once and is shared by the synchronous and asynchronous paths; asynchronous settlement uses the task's own promise chain.
367
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The check follows the documented error shape, not a specific SDK or status list.
368
+ * @evidence contracts/common.md#meaningful-documentation The comment documents the status argument, the two task kinds, and the thrown error, with examples.
266
369
  */
267
370
  function httpError(title, status, task) {
268
371
  if (typeof status === "number")
@@ -270,25 +373,36 @@ var TestValidator;
270
373
  const message = (actual) => typeof actual === "number"
271
374
  ? `Bug on ${title}: status code must be ${status.join(" or ")}, but ${actual}.`
272
375
  : `Bug on ${title}: status code must be ${status.join(" or ")}, but succeeded.`;
273
- const predicate = (exp) => typeof exp === "object" &&
274
- exp.constructor.name === "HttpError" &&
275
- status.some((val) => val === exp.status)
276
- ? null
277
- : new Error(message(typeof exp === "object" && exp.constructor.name === "HttpError"
278
- ? exp.status
279
- : undefined));
376
+ const predicate = (exp) => {
377
+ var _a;
378
+ let actual;
379
+ try {
380
+ if (typeof exp === "object" &&
381
+ exp !== null &&
382
+ ((_a = exp.constructor) === null || _a === void 0 ? void 0 : _a.name) === "HttpError") {
383
+ const value = exp.status;
384
+ if (typeof value === "number")
385
+ actual = value;
386
+ }
387
+ }
388
+ catch (_b) {
389
+ // A thrown value with unreadable properties cannot establish an
390
+ // HTTP status; it fails the same expectation as any other value.
391
+ }
392
+ return actual !== undefined && status.some((val) => val === actual)
393
+ ? null
394
+ : new Error(message(actual));
395
+ };
280
396
  try {
281
397
  const output = task();
282
398
  if (is_promise(output))
283
- return new Promise((resolve, reject) => output
284
- .catch((exp) => {
399
+ return output.then(() => {
400
+ throw new Error(message());
401
+ }, (exp) => {
285
402
  const res = predicate(exp);
286
403
  if (res)
287
- reject(res);
288
- else
289
- resolve();
290
- })
291
- .then(() => reject(new Error(message()))));
404
+ throw res;
405
+ });
292
406
  else
293
407
  throw new Error(message());
294
408
  }
@@ -308,35 +422,70 @@ var TestValidator;
308
422
  * sequence. Commonly used for testing database queries, search results, and
309
423
  * any paginated data APIs.
310
424
  *
425
+ * A page holds the first entities, so only the common prefix of the two lists
426
+ * is compared. An empty result against expected entities, or the reverse,
427
+ * fails.
428
+ *
429
+ * Processing cost: Only the common prefix of P entities is sliced, converted
430
+ * to id strings and compared, giving O(P) entries and temporary storage;
431
+ * string conversion and comparison add their text costs.
432
+ *
311
433
  * @example
312
- * ```typescript
313
434
  * // Test article pagination
314
- * const expectedArticles = await db.articles.findAll({ order: 'created_at DESC' });
315
- * const actualArticles = await api.functional.getArticles({ page: 1, limit: 10 });
435
+ * const expectedArticles = await db.articles.findAll({
436
+ * order: "created_at DESC",
437
+ * });
438
+ * const actualArticles = await api.functional.getArticles({
439
+ * page: 1,
440
+ * limit: 10,
441
+ * });
316
442
  *
317
- * TestValidator.index("article pagination order", expectedArticles, actualArticles,
318
- * true // enable trace logging
443
+ * TestValidator.index(
444
+ * "article pagination order",
445
+ * expectedArticles,
446
+ * actualArticles,
447
+ * true, // enable trace logging
319
448
  * );
320
449
  *
321
450
  * // Test user search results
322
- * const manuallyFilteredUsers = allUsers.filter(u => u.name.includes("John"));
323
- * const apiSearchResults = await api.functional.searchUsers({ query: "John" });
451
+ * const manuallyFilteredUsers = allUsers.filter((u) =>
452
+ * u.name.includes("John"),
453
+ * );
454
+ * const apiSearchResults = await api.functional.searchUsers({
455
+ * query: "John",
456
+ * });
324
457
  *
325
- * TestValidator.index("user search results", manuallyFilteredUsers, apiSearchResults);
326
- * ```;
458
+ * TestValidator.index(
459
+ * "user search results",
460
+ * manuallyFilteredUsers,
461
+ * apiSearchResults,
462
+ * );
327
463
  *
328
464
  * @param title - Descriptive title used in error messages when order differs
329
465
  * @param expected - The expected entities in correct order
330
466
  * @param gotten - The actual entities returned by the API
331
467
  * @param trace - Optional flag to enable debug logging (default: false)
332
468
  * @throws Error when entity order differs between expected and actual results
469
+ * over their common prefix, or when exactly one of them is empty
470
+ * @evidence contracts/common.md#principled-implementation A page holds the first entities of the expected list, so the two id lists are compared over their common prefix, and one side being empty while the other is not is a difference because there is no prefix to compare; ids are compared as strings.
471
+ * @evidence contracts/common.md#clear-and-simple-design One function over the id lists produced by a private helper, with an optional trace flag.
472
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Only ids are compared, with no entity type or field named in the function.
473
+ * @evidence contracts/common.md#meaningful-documentation The comment states the prefix rule, the empty rule, the trace flag, and the thrown error, with examples.
333
474
  */
334
475
  TestValidator.index = (title, expected, gotten, trace = false) => {
476
+ // a page holds the first entities, so only the common prefix is compared,
477
+ // but an empty side against a non-empty one has no prefix to prove
478
+ if ((expected.length === 0) !== (gotten.length === 0)) {
479
+ if (trace === true)
480
+ console.log({
481
+ expected: get_ids(expected),
482
+ gotten: get_ids(gotten),
483
+ });
484
+ throw new Error(`Bug on ${title}: result of the index is different with manual aggregation.`);
485
+ }
335
486
  const length = Math.min(expected.length, gotten.length);
336
- expected = expected.slice(0, length);
337
- gotten = gotten.slice(0, length);
338
- const xIds = get_ids(expected).slice(0, length);
339
- const yIds = get_ids(gotten).slice(0, length);
487
+ const xIds = get_ids(expected.slice(0, length));
488
+ const yIds = get_ids(gotten.slice(0, length));
340
489
  const equals = xIds.every((x, i) => x === yIds[i]);
341
490
  if (equals === true)
342
491
  return;
@@ -356,15 +505,27 @@ var TestValidator;
356
505
  * API, and compares results. Validates that search APIs return the correct
357
506
  * subset of data matching the search criteria.
358
507
  *
508
+ * Processing cost: For K sampled entities from N entries, sparse sampling
509
+ * costs O(K), each reference filter scans N entries and each returned page
510
+ * prefix is compared once. Calls are sequential and one sample result is live
511
+ * at a time.
512
+ *
513
+ * Reuse: Each invocation must evaluate its supplied task or request to
514
+ * observe current behavior; prior results cannot replace these effectful
515
+ * checks.
516
+ *
517
+ * Resource ownership: The returned promise retains invocation-local state
518
+ * until settlement. The callback owns its external resources, and this
519
+ * validator provides no cancellation or timeout.
520
+ *
359
521
  * @example
360
- * ```typescript
361
522
  * // Test article search functionality with exact matching
362
523
  * const allArticles = await db.articles.findAll();
363
524
  * const searchValidator = TestValidator.search(
364
525
  * "article search API",
365
526
  * (req) => api.searchArticles(req),
366
527
  * allArticles,
367
- * 5 // test with 5 random samples
528
+ * 5, // test with 5 random samples
368
529
  * );
369
530
  *
370
531
  * // Test exact match search
@@ -372,7 +533,7 @@ var TestValidator;
372
533
  * fields: ["title"],
373
534
  * values: (article) => [article.title], // full title for exact match
374
535
  * filter: (article, [title]) => article.title === title, // exact match
375
- * request: ([title]) => ({ search: { title } })
536
+ * request: ([title]) => ({ search: { title } }),
376
537
  * });
377
538
  *
378
539
  * // Test partial match search with includes
@@ -380,7 +541,7 @@ var TestValidator;
380
541
  * fields: ["content"],
381
542
  * values: (article) => [article.content.substring(0, 20)], // partial content
382
543
  * filter: (article, [keyword]) => article.content.includes(keyword),
383
- * request: ([keyword]) => ({ q: keyword })
544
+ * request: ([keyword]) => ({ q: keyword }),
384
545
  * });
385
546
  *
386
547
  * // Test multi-field search with exact matching
@@ -389,9 +550,8 @@ var TestValidator;
389
550
  * values: (article) => [article.writer, article.title],
390
551
  * filter: (article, [writer, title]) =>
391
552
  * article.writer === writer && article.title === title,
392
- * request: ([writer, title]) => ({ search: { writer, title } })
553
+ * request: ([writer, title]) => ({ search: { writer, title } }),
393
554
  * });
394
- * ```;
395
555
  *
396
556
  * @param title - Descriptive title used in error messages when search fails
397
557
  * @param getter - API function that performs the search
@@ -399,6 +559,10 @@ var TestValidator;
399
559
  * @param sampleCount - Number of random samples to test (default: 1)
400
560
  * @returns A function that accepts search configuration properties
401
561
  * @throws Error when API search results don't match manual filtering results
562
+ * @evidence contracts/common.md#principled-implementation A sample of entities is drawn, the search values are read from each, the expected set is computed by a manual filter over the whole dataset, the API is called with the request built from the values, and `index` compares the two lists, so the API is checked against a filter the test author wrote independently.
563
+ * @evidence contracts/common.md#clear-and-simple-design The function is curried so the dataset and the API are given once and each search configuration is run against them, and the comparison is delegated to `index`.
564
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The sample, values, filter, and request all come from the caller's configuration.
565
+ * @evidence contracts/common.md#meaningful-documentation The comment documents the steps, the parameters, and the thrown error, with examples.
402
566
  */
403
567
  TestValidator.search = (title, getter, total, sampleCount = 1) => (props) => __awaiter(this, void 0, void 0, function* () {
404
568
  const samples = RandomGenerator_1.RandomGenerator.sample(total, sampleCount);
@@ -417,15 +581,24 @@ var TestValidator;
417
581
  * ascending/descending order, and optional filtering. Provides detailed error
418
582
  * reporting for sorting failures.
419
583
  *
584
+ * Processing cost: After one getter call, an optional filter scans N results
585
+ * and sortedness checks at most N minus one adjacent pairs. Signed field
586
+ * construction costs O(F); filter output costs O(N) space when requested.
587
+ *
588
+ * Reuse: Each invocation must evaluate its supplied task or request to
589
+ * observe current behavior; prior results cannot replace these effectful
590
+ * checks.
591
+ *
592
+ * Resource ownership: The returned promise retains invocation-local state
593
+ * until settlement. The callback owns its external resources, and this
594
+ * validator provides no cancellation or timeout.
595
+ *
420
596
  * @example
421
- * ```typescript
422
597
  * // Test single field sorting with GaffComparator
423
598
  * const sortValidator = TestValidator.sort(
424
599
  * "article sorting",
425
- * (sortable) => api.getArticles({ sort: sortable })
426
- * )("created_at")(
427
- * GaffComparator.dates((a) => a.created_at)
428
- * );
600
+ * (sortable) => api.getArticles({ sort: sortable }),
601
+ * )("created_at")(GaffComparator.dates((a) => a.created_at));
429
602
  *
430
603
  * await sortValidator("+"); // ascending
431
604
  * await sortValidator("-"); // descending
@@ -433,10 +606,10 @@ var TestValidator;
433
606
  * // Test multi-field sorting with GaffComparator
434
607
  * const userSortValidator = TestValidator.sort(
435
608
  * "user sorting",
436
- * (sortable) => api.getUsers({ sort: sortable })
609
+ * (sortable) => api.getUsers({ sort: sortable }),
437
610
  * )("lastName", "firstName")(
438
611
  * GaffComparator.strings((user) => [user.lastName, user.firstName]),
439
- * (user) => user.isActive // only test active users
612
+ * (user) => user.isActive, // only test active users
440
613
  * );
441
614
  *
442
615
  * await userSortValidator("+", true); // ascending with trace logging
@@ -444,20 +617,24 @@ var TestValidator;
444
617
  * // Custom comparator for complex logic
445
618
  * const customSortValidator = TestValidator.sort(
446
619
  * "custom sorting",
447
- * (sortable) => api.getProducts({ sort: sortable })
448
- * )("price", "rating")(
449
- * (a, b) => {
450
- * const priceDiff = a.price - b.price;
451
- * return priceDiff !== 0 ? priceDiff : b.rating - a.rating; // price asc, rating desc
452
- * }
453
- * );
454
- * ```;
620
+ * (sortable) => api.getProducts({ sort: sortable }),
621
+ * )(
622
+ * "price",
623
+ * "rating",
624
+ * )((a, b) => {
625
+ * const priceDiff = a.price - b.price;
626
+ * return priceDiff !== 0 ? priceDiff : b.rating - a.rating; // price asc, rating desc
627
+ * });
455
628
  *
456
629
  * @param title - Descriptive title used in error messages when sorting fails
457
630
  * @param getter - API function that fetches sorted data
458
631
  * @returns A currying function chain: field names, comparator, then direction
459
632
  * @throws Error when API results are not properly sorted according to
460
633
  * specification
634
+ * @evidence contracts/common.md#principled-implementation The API is called with the signed field names, its result is optionally filtered, and the comparator (reversed for the descending direction) is applied to adjacent pairs, so any adjacent pair out of order fails; checking adjacent pairs suffices for a total preorder.
635
+ * @evidence contracts/common.md#clear-and-simple-design The function is curried in three steps so the request, the comparator, and the direction can be given at the moments each is known.
636
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The order is judged by the caller's comparator, with no field or direction special-cased.
637
+ * @evidence contracts/common.md#meaningful-documentation The comment documents each stage, the parameters, and the thrown error, with examples.
461
638
  */
462
639
  TestValidator.sort = (title, getter) => (...fields) => (comp, filter) => (direction_1, ...args_1) => __awaiter(this, [direction_1, ...args_1], void 0, function* (direction, trace = false) {
463
640
  let data = yield getter(fields.map((field) => `${direction}${field}`));