@nestia/e2e 14.0.1 → 14.0.3
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/lib/ArrayUtil.d.ts +152 -70
- package/lib/ArrayUtil.js +168 -74
- package/lib/ArrayUtil.js.map +1 -1
- package/lib/DynamicExecutor.d.ts +139 -11
- package/lib/DynamicExecutor.js +98 -10
- package/lib/DynamicExecutor.js.map +1 -1
- package/lib/GaffComparator.d.ts +176 -73
- package/lib/GaffComparator.js +189 -76
- package/lib/GaffComparator.js.map +1 -1
- package/lib/MapUtil.d.ts +37 -6
- package/lib/MapUtil.js +37 -6
- package/lib/MapUtil.js.map +1 -1
- package/lib/RandomGenerator.d.ts +157 -84
- package/lib/RandomGenerator.js +173 -89
- package/lib/RandomGenerator.js.map +1 -1
- package/lib/TestValidator.d.ts +248 -74
- package/lib/TestValidator.js +252 -89
- package/lib/TestValidator.js.map +1 -1
- package/lib/internal/json_equal_to.d.ts +21 -0
- package/lib/internal/json_equal_to.js +26 -6
- package/lib/internal/json_equal_to.js.map +1 -1
- package/package.json +3 -2
- package/src/ArrayUtil.ts +172 -76
- package/src/DynamicExecutor.ts +158 -15
- package/src/GaffComparator.ts +187 -77
- package/src/MapUtil.ts +37 -6
- package/src/RandomGenerator.ts +172 -89
- package/src/TestValidator.ts +276 -98
- package/src/internal/json_equal_to.ts +25 -4
package/lib/TestValidator.js
CHANGED
|
@@ -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(
|
|
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", () =>
|
|
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(
|
|
80
|
+
* TestValidator.predicate(
|
|
81
|
+
* "array should be empty",
|
|
82
|
+
* () => arr.length === 0,
|
|
83
|
+
* );
|
|
57
84
|
*
|
|
58
85
|
* // Asynchronous function
|
|
59
|
-
* await TestValidator.predicate(
|
|
60
|
-
*
|
|
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.`;
|
|
@@ -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
|
|
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(
|
|
148
|
+
* TestValidator.equals(
|
|
149
|
+
* "response should match expected",
|
|
150
|
+
* expectedUser,
|
|
151
|
+
* actualUser,
|
|
152
|
+
* );
|
|
110
153
|
*
|
|
111
154
|
* // Ignore timestamps in comparison
|
|
112
|
-
* TestValidator.equals(
|
|
113
|
-
*
|
|
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(
|
|
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
|
|
149
|
-
*
|
|
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(
|
|
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(
|
|
159
|
-
*
|
|
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(
|
|
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,29 +261,43 @@ 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
|
-
*
|
|
277
|
+
* TestValidator.error("should reject invalid email", () =>
|
|
278
|
+
* validateEmail("invalid-email"),
|
|
200
279
|
* );
|
|
201
280
|
*
|
|
202
281
|
* // Asynchronous error validation
|
|
203
|
-
* await TestValidator.error(
|
|
204
|
-
*
|
|
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
|
-
*
|
|
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.`;
|
|
@@ -238,23 +321,40 @@ var TestValidator;
|
|
|
238
321
|
* function throws an HttpError with one of the specified status codes. Useful
|
|
239
322
|
* for testing API endpoints, authentication, and authorization logic.
|
|
240
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
|
+
*
|
|
241
336
|
* @example
|
|
242
|
-
* ```typescript
|
|
243
337
|
* // Validate 401 Unauthorized
|
|
244
|
-
* await TestValidator.httpError(
|
|
245
|
-
*
|
|
338
|
+
* await TestValidator.httpError(
|
|
339
|
+
* "should return 401 for invalid token",
|
|
340
|
+
* 401,
|
|
341
|
+
* async () =>
|
|
342
|
+
* await api.functional.getProtectedResource("invalid-token"),
|
|
246
343
|
* );
|
|
247
344
|
*
|
|
248
345
|
* // Validate multiple possible error codes
|
|
249
|
-
* await TestValidator.httpError(
|
|
250
|
-
*
|
|
346
|
+
* await TestValidator.httpError(
|
|
347
|
+
* "should return client error",
|
|
348
|
+
* [400, 404, 422],
|
|
349
|
+
* async () => await api.functional.updateNonexistentResource(data),
|
|
251
350
|
* );
|
|
252
351
|
*
|
|
253
352
|
* // Validate server errors
|
|
254
|
-
* TestValidator.httpError(
|
|
255
|
-
*
|
|
353
|
+
* TestValidator.httpError(
|
|
354
|
+
* "should handle server errors",
|
|
355
|
+
* [500, 502, 503],
|
|
356
|
+
* () => callFaultyEndpoint(),
|
|
256
357
|
* );
|
|
257
|
-
* ```;
|
|
258
358
|
*
|
|
259
359
|
* @param title - Descriptive title used in error messages
|
|
260
360
|
* @param status - Expected status code(s), can be a single number or array
|
|
@@ -262,6 +362,10 @@ var TestValidator;
|
|
|
262
362
|
* @returns Void or Promise<void> based on the input type
|
|
263
363
|
* @throws Error when function doesn't throw HttpError or status code doesn't
|
|
264
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.
|
|
265
369
|
*/
|
|
266
370
|
function httpError(title, status, task) {
|
|
267
371
|
if (typeof status === "number")
|
|
@@ -269,25 +373,36 @@ var TestValidator;
|
|
|
269
373
|
const message = (actual) => typeof actual === "number"
|
|
270
374
|
? `Bug on ${title}: status code must be ${status.join(" or ")}, but ${actual}.`
|
|
271
375
|
: `Bug on ${title}: status code must be ${status.join(" or ")}, but succeeded.`;
|
|
272
|
-
const predicate = (exp) =>
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
+
};
|
|
279
396
|
try {
|
|
280
397
|
const output = task();
|
|
281
398
|
if (is_promise(output))
|
|
282
|
-
return
|
|
283
|
-
|
|
399
|
+
return output.then(() => {
|
|
400
|
+
throw new Error(message());
|
|
401
|
+
}, (exp) => {
|
|
284
402
|
const res = predicate(exp);
|
|
285
403
|
if (res)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
resolve();
|
|
289
|
-
})
|
|
290
|
-
.then(() => reject(new Error(message()))));
|
|
404
|
+
throw res;
|
|
405
|
+
});
|
|
291
406
|
else
|
|
292
407
|
throw new Error(message());
|
|
293
408
|
}
|
|
@@ -311,22 +426,40 @@ var TestValidator;
|
|
|
311
426
|
* is compared. An empty result against expected entities, or the reverse,
|
|
312
427
|
* fails.
|
|
313
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
|
+
*
|
|
314
433
|
* @example
|
|
315
|
-
* ```typescript
|
|
316
434
|
* // Test article pagination
|
|
317
|
-
* const expectedArticles = await db.articles.findAll({
|
|
318
|
-
*
|
|
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
|
+
* });
|
|
319
442
|
*
|
|
320
|
-
* TestValidator.index(
|
|
321
|
-
*
|
|
443
|
+
* TestValidator.index(
|
|
444
|
+
* "article pagination order",
|
|
445
|
+
* expectedArticles,
|
|
446
|
+
* actualArticles,
|
|
447
|
+
* true, // enable trace logging
|
|
322
448
|
* );
|
|
323
449
|
*
|
|
324
450
|
* // Test user search results
|
|
325
|
-
* const manuallyFilteredUsers = allUsers.filter(u =>
|
|
326
|
-
*
|
|
451
|
+
* const manuallyFilteredUsers = allUsers.filter((u) =>
|
|
452
|
+
* u.name.includes("John"),
|
|
453
|
+
* );
|
|
454
|
+
* const apiSearchResults = await api.functional.searchUsers({
|
|
455
|
+
* query: "John",
|
|
456
|
+
* });
|
|
327
457
|
*
|
|
328
|
-
* TestValidator.index(
|
|
329
|
-
*
|
|
458
|
+
* TestValidator.index(
|
|
459
|
+
* "user search results",
|
|
460
|
+
* manuallyFilteredUsers,
|
|
461
|
+
* apiSearchResults,
|
|
462
|
+
* );
|
|
330
463
|
*
|
|
331
464
|
* @param title - Descriptive title used in error messages when order differs
|
|
332
465
|
* @param expected - The expected entities in correct order
|
|
@@ -334,6 +467,10 @@ var TestValidator;
|
|
|
334
467
|
* @param trace - Optional flag to enable debug logging (default: false)
|
|
335
468
|
* @throws Error when entity order differs between expected and actual results
|
|
336
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.
|
|
337
474
|
*/
|
|
338
475
|
TestValidator.index = (title, expected, gotten, trace = false) => {
|
|
339
476
|
// a page holds the first entities, so only the common prefix is compared,
|
|
@@ -347,10 +484,8 @@ var TestValidator;
|
|
|
347
484
|
throw new Error(`Bug on ${title}: result of the index is different with manual aggregation.`);
|
|
348
485
|
}
|
|
349
486
|
const length = Math.min(expected.length, gotten.length);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const xIds = get_ids(expected).slice(0, length);
|
|
353
|
-
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));
|
|
354
489
|
const equals = xIds.every((x, i) => x === yIds[i]);
|
|
355
490
|
if (equals === true)
|
|
356
491
|
return;
|
|
@@ -370,15 +505,27 @@ var TestValidator;
|
|
|
370
505
|
* API, and compares results. Validates that search APIs return the correct
|
|
371
506
|
* subset of data matching the search criteria.
|
|
372
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
|
+
*
|
|
373
521
|
* @example
|
|
374
|
-
* ```typescript
|
|
375
522
|
* // Test article search functionality with exact matching
|
|
376
523
|
* const allArticles = await db.articles.findAll();
|
|
377
524
|
* const searchValidator = TestValidator.search(
|
|
378
525
|
* "article search API",
|
|
379
526
|
* (req) => api.searchArticles(req),
|
|
380
527
|
* allArticles,
|
|
381
|
-
* 5 // test with 5 random samples
|
|
528
|
+
* 5, // test with 5 random samples
|
|
382
529
|
* );
|
|
383
530
|
*
|
|
384
531
|
* // Test exact match search
|
|
@@ -386,7 +533,7 @@ var TestValidator;
|
|
|
386
533
|
* fields: ["title"],
|
|
387
534
|
* values: (article) => [article.title], // full title for exact match
|
|
388
535
|
* filter: (article, [title]) => article.title === title, // exact match
|
|
389
|
-
* request: ([title]) => ({ search: { title } })
|
|
536
|
+
* request: ([title]) => ({ search: { title } }),
|
|
390
537
|
* });
|
|
391
538
|
*
|
|
392
539
|
* // Test partial match search with includes
|
|
@@ -394,7 +541,7 @@ var TestValidator;
|
|
|
394
541
|
* fields: ["content"],
|
|
395
542
|
* values: (article) => [article.content.substring(0, 20)], // partial content
|
|
396
543
|
* filter: (article, [keyword]) => article.content.includes(keyword),
|
|
397
|
-
* request: ([keyword]) => ({ q: keyword })
|
|
544
|
+
* request: ([keyword]) => ({ q: keyword }),
|
|
398
545
|
* });
|
|
399
546
|
*
|
|
400
547
|
* // Test multi-field search with exact matching
|
|
@@ -403,9 +550,8 @@ var TestValidator;
|
|
|
403
550
|
* values: (article) => [article.writer, article.title],
|
|
404
551
|
* filter: (article, [writer, title]) =>
|
|
405
552
|
* article.writer === writer && article.title === title,
|
|
406
|
-
* request: ([writer, title]) => ({ search: { writer, title } })
|
|
553
|
+
* request: ([writer, title]) => ({ search: { writer, title } }),
|
|
407
554
|
* });
|
|
408
|
-
* ```;
|
|
409
555
|
*
|
|
410
556
|
* @param title - Descriptive title used in error messages when search fails
|
|
411
557
|
* @param getter - API function that performs the search
|
|
@@ -413,6 +559,10 @@ var TestValidator;
|
|
|
413
559
|
* @param sampleCount - Number of random samples to test (default: 1)
|
|
414
560
|
* @returns A function that accepts search configuration properties
|
|
415
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.
|
|
416
566
|
*/
|
|
417
567
|
TestValidator.search = (title, getter, total, sampleCount = 1) => (props) => __awaiter(this, void 0, void 0, function* () {
|
|
418
568
|
const samples = RandomGenerator_1.RandomGenerator.sample(total, sampleCount);
|
|
@@ -431,15 +581,24 @@ var TestValidator;
|
|
|
431
581
|
* ascending/descending order, and optional filtering. Provides detailed error
|
|
432
582
|
* reporting for sorting failures.
|
|
433
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
|
+
*
|
|
434
596
|
* @example
|
|
435
|
-
* ```typescript
|
|
436
597
|
* // Test single field sorting with GaffComparator
|
|
437
598
|
* const sortValidator = TestValidator.sort(
|
|
438
599
|
* "article sorting",
|
|
439
|
-
* (sortable) => api.getArticles({ sort: sortable })
|
|
440
|
-
* )("created_at")(
|
|
441
|
-
* GaffComparator.dates((a) => a.created_at)
|
|
442
|
-
* );
|
|
600
|
+
* (sortable) => api.getArticles({ sort: sortable }),
|
|
601
|
+
* )("created_at")(GaffComparator.dates((a) => a.created_at));
|
|
443
602
|
*
|
|
444
603
|
* await sortValidator("+"); // ascending
|
|
445
604
|
* await sortValidator("-"); // descending
|
|
@@ -447,10 +606,10 @@ var TestValidator;
|
|
|
447
606
|
* // Test multi-field sorting with GaffComparator
|
|
448
607
|
* const userSortValidator = TestValidator.sort(
|
|
449
608
|
* "user sorting",
|
|
450
|
-
* (sortable) => api.getUsers({ sort: sortable })
|
|
609
|
+
* (sortable) => api.getUsers({ sort: sortable }),
|
|
451
610
|
* )("lastName", "firstName")(
|
|
452
611
|
* GaffComparator.strings((user) => [user.lastName, user.firstName]),
|
|
453
|
-
* (user) => user.isActive // only test active users
|
|
612
|
+
* (user) => user.isActive, // only test active users
|
|
454
613
|
* );
|
|
455
614
|
*
|
|
456
615
|
* await userSortValidator("+", true); // ascending with trace logging
|
|
@@ -458,20 +617,24 @@ var TestValidator;
|
|
|
458
617
|
* // Custom comparator for complex logic
|
|
459
618
|
* const customSortValidator = TestValidator.sort(
|
|
460
619
|
* "custom sorting",
|
|
461
|
-
* (sortable) => api.getProducts({ sort: sortable })
|
|
462
|
-
* )(
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
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
|
+
* });
|
|
469
628
|
*
|
|
470
629
|
* @param title - Descriptive title used in error messages when sorting fails
|
|
471
630
|
* @param getter - API function that fetches sorted data
|
|
472
631
|
* @returns A currying function chain: field names, comparator, then direction
|
|
473
632
|
* @throws Error when API results are not properly sorted according to
|
|
474
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.
|
|
475
638
|
*/
|
|
476
639
|
TestValidator.sort = (title, getter) => (...fields) => (comp, filter) => (direction_1, ...args_1) => __awaiter(this, [direction_1, ...args_1], void 0, function* (direction, trace = false) {
|
|
477
640
|
let data = yield getter(fields.map((field) => `${direction}${field}`));
|
package/lib/TestValidator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestValidator.js","sourceRoot":"","sources":["../src/TestValidator.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uDAAoD;AACpD,4DAAyD;AAEzD
|
|
1
|
+
{"version":3,"file":"TestValidator.js","sourceRoot":"","sources":["../src/TestValidator.ts"],"names":[],"mappings":"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"}
|