@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.d.ts
CHANGED
|
@@ -11,18 +11,31 @@
|
|
|
11
11
|
* maintain currying patterns for advanced composition. All provide detailed
|
|
12
12
|
* error messages for debugging failed assertions.
|
|
13
13
|
*
|
|
14
|
+
* Processing cost: Scalar assertions use constant bookkeeping; JSON assertions
|
|
15
|
+
* traverse the expected structure, index compares the shared page prefix,
|
|
16
|
+
* search scans the dataset once per sample and sort checks adjacent results.
|
|
17
|
+
* External callbacks own their computation costs.
|
|
18
|
+
*
|
|
14
19
|
* @author Jeongho Nam - https://github.com/samchon
|
|
15
20
|
* @example
|
|
16
|
-
* ```typescript
|
|
17
21
|
* // Basic condition testing
|
|
18
|
-
* TestValidator.predicate(
|
|
22
|
+
* TestValidator.predicate(
|
|
23
|
+
* "user should be authenticated",
|
|
24
|
+
* user.isAuthenticated,
|
|
25
|
+
* );
|
|
19
26
|
*
|
|
20
27
|
* // Equality validation
|
|
21
28
|
* TestValidator.equals("API response should match expected", x, y);
|
|
22
29
|
*
|
|
23
30
|
* // Error validation
|
|
24
|
-
* TestValidator.error("should throw on invalid input", () =>
|
|
25
|
-
*
|
|
31
|
+
* TestValidator.error("should throw on invalid input", () =>
|
|
32
|
+
* assertInput(""),
|
|
33
|
+
* );
|
|
34
|
+
*
|
|
35
|
+
* @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.
|
|
36
|
+
* @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.
|
|
37
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Assertions compare the values they are given; none consults a fixture, an entity name, or an environment.
|
|
38
|
+
* @evidence contracts/common.md#meaningful-documentation The namespace prose lists the assertion families and the error message convention, with examples.
|
|
26
39
|
*/
|
|
27
40
|
export declare namespace TestValidator {
|
|
28
41
|
/**
|
|
@@ -32,19 +45,33 @@ export declare namespace TestValidator {
|
|
|
32
45
|
* boolean, and asynchronous functions returning Promise<boolean>. The return
|
|
33
46
|
* type is automatically inferred based on the input type.
|
|
34
47
|
*
|
|
48
|
+
* Processing cost: The condition is evaluated once and asynchronous
|
|
49
|
+
* settlement adds constant bookkeeping without polling or repeating the
|
|
50
|
+
* condition.
|
|
51
|
+
*
|
|
52
|
+
* Reuse: Each invocation must evaluate its supplied task or request to
|
|
53
|
+
* observe current behavior; prior results cannot replace these effectful
|
|
54
|
+
* checks.
|
|
55
|
+
*
|
|
56
|
+
* Resource ownership: The returned promise retains invocation-local state
|
|
57
|
+
* until settlement. The callback owns its external resources, and this
|
|
58
|
+
* validator provides no cancellation or timeout.
|
|
59
|
+
*
|
|
35
60
|
* @example
|
|
36
|
-
* ```typescript
|
|
37
61
|
* // Synchronous boolean
|
|
38
62
|
* TestValidator.predicate("user should exist", user !== null);
|
|
39
63
|
*
|
|
40
64
|
* // Synchronous function
|
|
41
|
-
* TestValidator.predicate(
|
|
65
|
+
* TestValidator.predicate(
|
|
66
|
+
* "array should be empty",
|
|
67
|
+
* () => arr.length === 0,
|
|
68
|
+
* );
|
|
42
69
|
*
|
|
43
70
|
* // Asynchronous function
|
|
44
|
-
* await TestValidator.predicate(
|
|
45
|
-
*
|
|
71
|
+
* await TestValidator.predicate(
|
|
72
|
+
* "database should be connected",
|
|
73
|
+
* async () => await db.ping(),
|
|
46
74
|
* );
|
|
47
|
-
* ```;
|
|
48
75
|
*
|
|
49
76
|
* @param title - Descriptive title used in error messages when validation
|
|
50
77
|
* fails
|
|
@@ -52,35 +79,54 @@ export declare namespace TestValidator {
|
|
|
52
79
|
* function)
|
|
53
80
|
* @returns Void or Promise<void> based on the input type
|
|
54
81
|
* @throws Error with descriptive message when condition is not satisfied
|
|
82
|
+
* @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.
|
|
83
|
+
* @evidence contracts/common.md#clear-and-simple-design One function whose three branches differ only in when the value is available.
|
|
84
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The check is on the value the caller gives, with no truthiness coercion.
|
|
85
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the three forms, the parameters, the return type rule, and the thrown error, with examples.
|
|
55
86
|
*/
|
|
56
87
|
function predicate<T extends boolean | (() => boolean) | (() => Promise<boolean>)>(title: string, condition: T): T extends () => Promise<boolean> ? Promise<void> : void;
|
|
57
88
|
/**
|
|
58
89
|
* Validates deep equality between two values using JSON comparison.
|
|
59
90
|
*
|
|
60
|
-
* Performs recursive comparison of objects and arrays
|
|
91
|
+
* Performs recursive comparison of objects and arrays through their JSON
|
|
92
|
+
* form. The comparison walks the keys of the first value, so a key that only
|
|
93
|
+
* the second value has is ignored, which is why the second value may be a
|
|
94
|
+
* subtype of the first. Keys whose value in the first value is `undefined`
|
|
95
|
+
* are skipped, and arrays must have the same length. Supports an optional
|
|
61
96
|
* exception filter to ignore specific keys during comparison. Useful for
|
|
62
97
|
* validating API responses, data transformations, and object state changes.
|
|
63
98
|
*
|
|
99
|
+
* Processing cost: The shared JSON walker traverses the first value and its
|
|
100
|
+
* corresponding second-value positions once, with recursion and accessor
|
|
101
|
+
* output proportional to visited structure; serialization occurs only on
|
|
102
|
+
* failure.
|
|
103
|
+
*
|
|
64
104
|
* @example
|
|
65
|
-
* ```typescript
|
|
66
105
|
* // Basic equality
|
|
67
|
-
* TestValidator.equals(
|
|
106
|
+
* TestValidator.equals(
|
|
107
|
+
* "response should match expected",
|
|
108
|
+
* expectedUser,
|
|
109
|
+
* actualUser,
|
|
110
|
+
* );
|
|
68
111
|
*
|
|
69
112
|
* // Ignore timestamps in comparison
|
|
70
|
-
* TestValidator.equals(
|
|
71
|
-
*
|
|
113
|
+
* TestValidator.equals(
|
|
114
|
+
* "user data should match",
|
|
115
|
+
* expectedUser,
|
|
116
|
+
* actualUser,
|
|
117
|
+
* (key) => key === "updatedAt",
|
|
72
118
|
* );
|
|
73
119
|
*
|
|
74
120
|
* // Validate API response structure
|
|
75
|
-
* TestValidator.equals(
|
|
121
|
+
* TestValidator.equals(
|
|
122
|
+
* "API response structure",
|
|
123
|
+
* { id: 1, name: "John" },
|
|
76
124
|
* { id: 1, name: "John" },
|
|
77
|
-
* { id: 1, name: "John" }
|
|
78
125
|
* );
|
|
79
126
|
*
|
|
80
127
|
* // Type-safe nullable comparisons
|
|
81
128
|
* const nullableData: { name: string } | null = getData();
|
|
82
129
|
* TestValidator.equals("nullable check", nullableData, null);
|
|
83
|
-
* ```;
|
|
84
130
|
*
|
|
85
131
|
* @param title - Descriptive title used in error messages when values differ
|
|
86
132
|
* @param X - The first value to compare
|
|
@@ -88,33 +134,52 @@ export declare namespace TestValidator {
|
|
|
88
134
|
* @param exception - Optional filter function to exclude specific keys from
|
|
89
135
|
* comparison
|
|
90
136
|
* @throws Error with detailed diff information when values are not equal
|
|
137
|
+
* @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.
|
|
138
|
+
* @evidence contracts/common.md#clear-and-simple-design A thin wrapper over the shared comparison that adds the message.
|
|
139
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The exception filter is the caller's; the function contains no key name of its own.
|
|
140
|
+
* @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.
|
|
91
141
|
*/
|
|
92
142
|
function equals<X, Y extends X = X>(title: string, X: X, y: Y | null | undefined, exception?: (key: string) => boolean): void;
|
|
93
143
|
/**
|
|
94
144
|
* Validates deep inequality between two values using JSON comparison.
|
|
95
145
|
*
|
|
96
146
|
* Performs recursive comparison of objects and arrays to ensure they are NOT
|
|
97
|
-
* equal
|
|
98
|
-
*
|
|
147
|
+
* equal, by the same rules as {@link equals}: only the keys of the first value
|
|
148
|
+
* are compared, so a key that only the second value has does not make them
|
|
149
|
+
* different. Supports an optional exception filter to ignore specific keys
|
|
150
|
+
* during comparison. Useful for validating that data has changed, objects are
|
|
99
151
|
* different, or mutations have occurred.
|
|
100
152
|
*
|
|
153
|
+
* Processing cost: The shared JSON walker visits the first value and
|
|
154
|
+
* corresponding positions once; a failure additionally serializes both values
|
|
155
|
+
* for diagnostics. Space follows visited structure and reported differences.
|
|
156
|
+
*
|
|
101
157
|
* @example
|
|
102
|
-
* ```typescript
|
|
103
158
|
* // Basic inequality
|
|
104
|
-
* TestValidator.notEquals(
|
|
159
|
+
* TestValidator.notEquals(
|
|
160
|
+
* "user should be different after update",
|
|
161
|
+
* originalUser,
|
|
162
|
+
* updatedUser,
|
|
163
|
+
* );
|
|
105
164
|
*
|
|
106
165
|
* // Ignore timestamps in comparison
|
|
107
|
-
* TestValidator.notEquals(
|
|
108
|
-
*
|
|
166
|
+
* TestValidator.notEquals(
|
|
167
|
+
* "user data should differ",
|
|
168
|
+
* originalUser,
|
|
169
|
+
* modifiedUser,
|
|
170
|
+
* (key) => key === "updatedAt",
|
|
109
171
|
* );
|
|
110
172
|
*
|
|
111
173
|
* // Validate state changes
|
|
112
|
-
* TestValidator.notEquals(
|
|
174
|
+
* TestValidator.notEquals(
|
|
175
|
+
* "state should have changed",
|
|
176
|
+
* initialState,
|
|
177
|
+
* currentState,
|
|
178
|
+
* );
|
|
113
179
|
*
|
|
114
180
|
* // Type-safe nullable comparisons
|
|
115
181
|
* const mutableData: { count: number } | null = getMutableData();
|
|
116
182
|
* TestValidator.notEquals("should have changed", mutableData, null);
|
|
117
|
-
* ```;
|
|
118
183
|
*
|
|
119
184
|
* @param title - Descriptive title used in error messages when values are
|
|
120
185
|
* equal
|
|
@@ -123,6 +188,10 @@ export declare namespace TestValidator {
|
|
|
123
188
|
* @param exception - Optional filter function to exclude specific keys from
|
|
124
189
|
* comparison
|
|
125
190
|
* @throws Error when values are equal (indicating validation failure)
|
|
191
|
+
* @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.
|
|
192
|
+
* @evidence contracts/common.md#clear-and-simple-design A thin wrapper over the shared comparison with the opposite outcome.
|
|
193
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The exception filter is the caller's; the function contains no key name of its own.
|
|
194
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states that the rules are those of `equals`, and gives examples.
|
|
126
195
|
*/
|
|
127
196
|
function notEquals<X, Y extends X = X>(title: string, x: X, y: Y | null | undefined, exception?: (key: string) => boolean): void;
|
|
128
197
|
/**
|
|
@@ -132,29 +201,43 @@ export declare namespace TestValidator {
|
|
|
132
201
|
* successfully without throwing an error or rejecting, this validator will
|
|
133
202
|
* throw an exception. Supports both synchronous and asynchronous functions.
|
|
134
203
|
*
|
|
204
|
+
* Processing cost: The task is invoked once and its settlement is classified
|
|
205
|
+
* with constant bookkeeping; no retry or repeated execution occurs.
|
|
206
|
+
*
|
|
207
|
+
* Reuse: Each invocation must evaluate its supplied task or request to
|
|
208
|
+
* observe current behavior; prior results cannot replace these effectful
|
|
209
|
+
* checks.
|
|
210
|
+
*
|
|
211
|
+
* Resource ownership: The returned promise retains invocation-local state
|
|
212
|
+
* until settlement. The callback owns its external resources, and this
|
|
213
|
+
* validator provides no cancellation or timeout.
|
|
214
|
+
*
|
|
135
215
|
* @example
|
|
136
|
-
* ```typescript
|
|
137
216
|
* // Synchronous error validation
|
|
138
|
-
* TestValidator.error("should reject invalid email",
|
|
139
|
-
*
|
|
217
|
+
* TestValidator.error("should reject invalid email", () =>
|
|
218
|
+
* validateEmail("invalid-email"),
|
|
140
219
|
* );
|
|
141
220
|
*
|
|
142
221
|
* // Asynchronous error validation
|
|
143
|
-
* await TestValidator.error(
|
|
144
|
-
*
|
|
222
|
+
* await TestValidator.error(
|
|
223
|
+
* "should reject unauthorized access",
|
|
224
|
+
* async () => await api.functional.getSecretData(),
|
|
145
225
|
* );
|
|
146
226
|
*
|
|
147
227
|
* // Validate input validation
|
|
148
|
-
* TestValidator.error("should throw on empty string",
|
|
149
|
-
*
|
|
228
|
+
* TestValidator.error("should throw on empty string", () =>
|
|
229
|
+
* processRequiredInput(""),
|
|
150
230
|
* );
|
|
151
|
-
* ```;
|
|
152
231
|
*
|
|
153
232
|
* @param title - Descriptive title used in error messages when no error
|
|
154
233
|
* occurs
|
|
155
234
|
* @param task - The function that should throw an error or reject
|
|
156
235
|
* @returns Void or Promise<void> based on the input type
|
|
157
236
|
* @throws Error when the task function does not throw an error or reject
|
|
237
|
+
* @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.
|
|
238
|
+
* @evidence contracts/common.md#clear-and-simple-design One function with a synchronous and an asynchronous path selected by the returned value.
|
|
239
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Any error satisfies it, as documented, and no error type is special-cased.
|
|
240
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states that an exception is required, the return type rule, and the thrown error, with examples.
|
|
158
241
|
*/
|
|
159
242
|
function error<T>(title: string, task: () => T): T extends Promise<any> ? Promise<void> : void;
|
|
160
243
|
/**
|
|
@@ -164,23 +247,40 @@ export declare namespace TestValidator {
|
|
|
164
247
|
* function throws an HttpError with one of the specified status codes. Useful
|
|
165
248
|
* for testing API endpoints, authentication, and authorization logic.
|
|
166
249
|
*
|
|
250
|
+
* Processing cost: The task is invoked once. Classification reads the thrown
|
|
251
|
+
* shape once and scans the expected status list until a match, using O(S)
|
|
252
|
+
* worst-case status comparisons and constant auxiliary state.
|
|
253
|
+
*
|
|
254
|
+
* Reuse: Each invocation must evaluate its supplied task or request to
|
|
255
|
+
* observe current behavior; prior results cannot replace these effectful
|
|
256
|
+
* checks.
|
|
257
|
+
*
|
|
258
|
+
* Resource ownership: The returned promise retains invocation-local state
|
|
259
|
+
* until settlement. The callback owns its external resources, and this
|
|
260
|
+
* validator provides no cancellation or timeout.
|
|
261
|
+
*
|
|
167
262
|
* @example
|
|
168
|
-
* ```typescript
|
|
169
263
|
* // Validate 401 Unauthorized
|
|
170
|
-
* await TestValidator.httpError(
|
|
171
|
-
*
|
|
264
|
+
* await TestValidator.httpError(
|
|
265
|
+
* "should return 401 for invalid token",
|
|
266
|
+
* 401,
|
|
267
|
+
* async () =>
|
|
268
|
+
* await api.functional.getProtectedResource("invalid-token"),
|
|
172
269
|
* );
|
|
173
270
|
*
|
|
174
271
|
* // Validate multiple possible error codes
|
|
175
|
-
* await TestValidator.httpError(
|
|
176
|
-
*
|
|
272
|
+
* await TestValidator.httpError(
|
|
273
|
+
* "should return client error",
|
|
274
|
+
* [400, 404, 422],
|
|
275
|
+
* async () => await api.functional.updateNonexistentResource(data),
|
|
177
276
|
* );
|
|
178
277
|
*
|
|
179
278
|
* // Validate server errors
|
|
180
|
-
* TestValidator.httpError(
|
|
181
|
-
*
|
|
279
|
+
* TestValidator.httpError(
|
|
280
|
+
* "should handle server errors",
|
|
281
|
+
* [500, 502, 503],
|
|
282
|
+
* () => callFaultyEndpoint(),
|
|
182
283
|
* );
|
|
183
|
-
* ```;
|
|
184
284
|
*
|
|
185
285
|
* @param title - Descriptive title used in error messages
|
|
186
286
|
* @param status - Expected status code(s), can be a single number or array
|
|
@@ -188,6 +288,10 @@ export declare namespace TestValidator {
|
|
|
188
288
|
* @returns Void or Promise<void> based on the input type
|
|
189
289
|
* @throws Error when function doesn't throw HttpError or status code doesn't
|
|
190
290
|
* match
|
|
291
|
+
* @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.
|
|
292
|
+
* @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.
|
|
293
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The check follows the documented error shape, not a specific SDK or status list.
|
|
294
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the status argument, the two task kinds, and the thrown error, with examples.
|
|
191
295
|
*/
|
|
192
296
|
function httpError<T>(title: string, status: number | number[], task: () => T): T extends Promise<any> ? Promise<void> : void;
|
|
193
297
|
/**
|
|
@@ -202,22 +306,40 @@ export declare namespace TestValidator {
|
|
|
202
306
|
* is compared. An empty result against expected entities, or the reverse,
|
|
203
307
|
* fails.
|
|
204
308
|
*
|
|
309
|
+
* Processing cost: Only the common prefix of P entities is sliced, converted
|
|
310
|
+
* to id strings and compared, giving O(P) entries and temporary storage;
|
|
311
|
+
* string conversion and comparison add their text costs.
|
|
312
|
+
*
|
|
205
313
|
* @example
|
|
206
|
-
* ```typescript
|
|
207
314
|
* // Test article pagination
|
|
208
|
-
* const expectedArticles = await db.articles.findAll({
|
|
209
|
-
*
|
|
315
|
+
* const expectedArticles = await db.articles.findAll({
|
|
316
|
+
* order: "created_at DESC",
|
|
317
|
+
* });
|
|
318
|
+
* const actualArticles = await api.functional.getArticles({
|
|
319
|
+
* page: 1,
|
|
320
|
+
* limit: 10,
|
|
321
|
+
* });
|
|
210
322
|
*
|
|
211
|
-
* TestValidator.index(
|
|
212
|
-
*
|
|
323
|
+
* TestValidator.index(
|
|
324
|
+
* "article pagination order",
|
|
325
|
+
* expectedArticles,
|
|
326
|
+
* actualArticles,
|
|
327
|
+
* true, // enable trace logging
|
|
213
328
|
* );
|
|
214
329
|
*
|
|
215
330
|
* // Test user search results
|
|
216
|
-
* const manuallyFilteredUsers = allUsers.filter(u =>
|
|
217
|
-
*
|
|
331
|
+
* const manuallyFilteredUsers = allUsers.filter((u) =>
|
|
332
|
+
* u.name.includes("John"),
|
|
333
|
+
* );
|
|
334
|
+
* const apiSearchResults = await api.functional.searchUsers({
|
|
335
|
+
* query: "John",
|
|
336
|
+
* });
|
|
218
337
|
*
|
|
219
|
-
* TestValidator.index(
|
|
220
|
-
*
|
|
338
|
+
* TestValidator.index(
|
|
339
|
+
* "user search results",
|
|
340
|
+
* manuallyFilteredUsers,
|
|
341
|
+
* apiSearchResults,
|
|
342
|
+
* );
|
|
221
343
|
*
|
|
222
344
|
* @param title - Descriptive title used in error messages when order differs
|
|
223
345
|
* @param expected - The expected entities in correct order
|
|
@@ -225,6 +347,10 @@ export declare namespace TestValidator {
|
|
|
225
347
|
* @param trace - Optional flag to enable debug logging (default: false)
|
|
226
348
|
* @throws Error when entity order differs between expected and actual results
|
|
227
349
|
* over their common prefix, or when exactly one of them is empty
|
|
350
|
+
* @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.
|
|
351
|
+
* @evidence contracts/common.md#clear-and-simple-design One function over the id lists produced by a private helper, with an optional trace flag.
|
|
352
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Only ids are compared, with no entity type or field named in the function.
|
|
353
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states the prefix rule, the empty rule, the trace flag, and the thrown error, with examples.
|
|
228
354
|
*/
|
|
229
355
|
const index: <X extends IEntity<any>, Y extends X = X>(title: string, expected: X[], gotten: Y[], trace?: boolean) => void;
|
|
230
356
|
/**
|
|
@@ -236,15 +362,27 @@ export declare namespace TestValidator {
|
|
|
236
362
|
* API, and compares results. Validates that search APIs return the correct
|
|
237
363
|
* subset of data matching the search criteria.
|
|
238
364
|
*
|
|
365
|
+
* Processing cost: For K sampled entities from N entries, sparse sampling
|
|
366
|
+
* costs O(K), each reference filter scans N entries and each returned page
|
|
367
|
+
* prefix is compared once. Calls are sequential and one sample result is live
|
|
368
|
+
* at a time.
|
|
369
|
+
*
|
|
370
|
+
* Reuse: Each invocation must evaluate its supplied task or request to
|
|
371
|
+
* observe current behavior; prior results cannot replace these effectful
|
|
372
|
+
* checks.
|
|
373
|
+
*
|
|
374
|
+
* Resource ownership: The returned promise retains invocation-local state
|
|
375
|
+
* until settlement. The callback owns its external resources, and this
|
|
376
|
+
* validator provides no cancellation or timeout.
|
|
377
|
+
*
|
|
239
378
|
* @example
|
|
240
|
-
* ```typescript
|
|
241
379
|
* // Test article search functionality with exact matching
|
|
242
380
|
* const allArticles = await db.articles.findAll();
|
|
243
381
|
* const searchValidator = TestValidator.search(
|
|
244
382
|
* "article search API",
|
|
245
383
|
* (req) => api.searchArticles(req),
|
|
246
384
|
* allArticles,
|
|
247
|
-
* 5 // test with 5 random samples
|
|
385
|
+
* 5, // test with 5 random samples
|
|
248
386
|
* );
|
|
249
387
|
*
|
|
250
388
|
* // Test exact match search
|
|
@@ -252,7 +390,7 @@ export declare namespace TestValidator {
|
|
|
252
390
|
* fields: ["title"],
|
|
253
391
|
* values: (article) => [article.title], // full title for exact match
|
|
254
392
|
* filter: (article, [title]) => article.title === title, // exact match
|
|
255
|
-
* request: ([title]) => ({ search: { title } })
|
|
393
|
+
* request: ([title]) => ({ search: { title } }),
|
|
256
394
|
* });
|
|
257
395
|
*
|
|
258
396
|
* // Test partial match search with includes
|
|
@@ -260,7 +398,7 @@ export declare namespace TestValidator {
|
|
|
260
398
|
* fields: ["content"],
|
|
261
399
|
* values: (article) => [article.content.substring(0, 20)], // partial content
|
|
262
400
|
* filter: (article, [keyword]) => article.content.includes(keyword),
|
|
263
|
-
* request: ([keyword]) => ({ q: keyword })
|
|
401
|
+
* request: ([keyword]) => ({ q: keyword }),
|
|
264
402
|
* });
|
|
265
403
|
*
|
|
266
404
|
* // Test multi-field search with exact matching
|
|
@@ -269,9 +407,8 @@ export declare namespace TestValidator {
|
|
|
269
407
|
* values: (article) => [article.writer, article.title],
|
|
270
408
|
* filter: (article, [writer, title]) =>
|
|
271
409
|
* article.writer === writer && article.title === title,
|
|
272
|
-
* request: ([writer, title]) => ({ search: { writer, title } })
|
|
410
|
+
* request: ([writer, title]) => ({ search: { writer, title } }),
|
|
273
411
|
* });
|
|
274
|
-
* ```;
|
|
275
412
|
*
|
|
276
413
|
* @param title - Descriptive title used in error messages when search fails
|
|
277
414
|
* @param getter - API function that performs the search
|
|
@@ -279,6 +416,10 @@ export declare namespace TestValidator {
|
|
|
279
416
|
* @param sampleCount - Number of random samples to test (default: 1)
|
|
280
417
|
* @returns A function that accepts search configuration properties
|
|
281
418
|
* @throws Error when API search results don't match manual filtering results
|
|
419
|
+
* @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.
|
|
420
|
+
* @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`.
|
|
421
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The sample, values, filter, and request all come from the caller's configuration.
|
|
422
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the steps, the parameters, and the thrown error, with examples.
|
|
282
423
|
*/
|
|
283
424
|
const search: <Entity extends IEntity<any>, Request>(title: string, getter: (input: Request) => Promise<Entity[]>, total: Entity[], sampleCount?: number) => <Values extends any[]>(props: ISearchProps<Entity, Values, Request>) => Promise<void>;
|
|
284
425
|
/**
|
|
@@ -292,6 +433,10 @@ export declare namespace TestValidator {
|
|
|
292
433
|
* @template Values - Tuple type representing the search values extracted from
|
|
293
434
|
* entities
|
|
294
435
|
* @template Request - Type of the API request object
|
|
436
|
+
* @evidence contracts/common.md#principled-implementation The record supplies what the check needs from a test author: the field names for the message and the three functions that extract values, filter, and build the request.
|
|
437
|
+
* @evidence contracts/common.md#clear-and-simple-design A four-member record with the functions as methods.
|
|
438
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
439
|
+
* @evidence contracts/common.md#meaningful-documentation The comment names the role of the record and each member documents itself.
|
|
295
440
|
*/
|
|
296
441
|
interface ISearchProps<Entity extends IEntity<any>, Values extends any[], Request> {
|
|
297
442
|
/** Field names being searched, used in error messages for identification */
|
|
@@ -301,6 +446,10 @@ export declare namespace TestValidator {
|
|
|
301
446
|
*
|
|
302
447
|
* @param entity - The entity to extract search values from
|
|
303
448
|
* @returns Tuple of values used for searching
|
|
449
|
+
* @evidence contracts/common.md#principled-implementation The search values are read from a sampled entity, so the search is exercised with values that occur in the dataset.
|
|
450
|
+
* @evidence contracts/common.md#clear-and-simple-design One method with one entity argument.
|
|
451
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is caller code.
|
|
452
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the parameter and the return value.
|
|
304
453
|
*/
|
|
305
454
|
values(entity: Entity): Values;
|
|
306
455
|
/**
|
|
@@ -309,6 +458,10 @@ export declare namespace TestValidator {
|
|
|
309
458
|
* @param entity - Entity to test against criteria
|
|
310
459
|
* @param values - Search values to match against
|
|
311
460
|
* @returns True if entity matches the search criteria
|
|
461
|
+
* @evidence contracts/common.md#principled-implementation The predicate decides whether an entity matches the values, which is the manual reference against which the API is compared.
|
|
462
|
+
* @evidence contracts/common.md#clear-and-simple-design One predicate over an entity and the values.
|
|
463
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is caller code.
|
|
464
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the parameters and the return value.
|
|
312
465
|
*/
|
|
313
466
|
filter(entity: Entity, values: Values): boolean;
|
|
314
467
|
/**
|
|
@@ -316,6 +469,10 @@ export declare namespace TestValidator {
|
|
|
316
469
|
*
|
|
317
470
|
* @param values - Search values to include in request
|
|
318
471
|
* @returns Request object for the search API
|
|
472
|
+
* @evidence contracts/common.md#principled-implementation The function builds the API request from the values, so the same values drive the manual filter and the call.
|
|
473
|
+
* @evidence contracts/common.md#clear-and-simple-design One method from values to the request.
|
|
474
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is caller code.
|
|
475
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the parameter and the return value.
|
|
319
476
|
*/
|
|
320
477
|
request(values: Values): Request;
|
|
321
478
|
}
|
|
@@ -327,15 +484,24 @@ export declare namespace TestValidator {
|
|
|
327
484
|
* ascending/descending order, and optional filtering. Provides detailed error
|
|
328
485
|
* reporting for sorting failures.
|
|
329
486
|
*
|
|
487
|
+
* Processing cost: After one getter call, an optional filter scans N results
|
|
488
|
+
* and sortedness checks at most N minus one adjacent pairs. Signed field
|
|
489
|
+
* construction costs O(F); filter output costs O(N) space when requested.
|
|
490
|
+
*
|
|
491
|
+
* Reuse: Each invocation must evaluate its supplied task or request to
|
|
492
|
+
* observe current behavior; prior results cannot replace these effectful
|
|
493
|
+
* checks.
|
|
494
|
+
*
|
|
495
|
+
* Resource ownership: The returned promise retains invocation-local state
|
|
496
|
+
* until settlement. The callback owns its external resources, and this
|
|
497
|
+
* validator provides no cancellation or timeout.
|
|
498
|
+
*
|
|
330
499
|
* @example
|
|
331
|
-
* ```typescript
|
|
332
500
|
* // Test single field sorting with GaffComparator
|
|
333
501
|
* const sortValidator = TestValidator.sort(
|
|
334
502
|
* "article sorting",
|
|
335
|
-
* (sortable) => api.getArticles({ sort: sortable })
|
|
336
|
-
* )("created_at")(
|
|
337
|
-
* GaffComparator.dates((a) => a.created_at)
|
|
338
|
-
* );
|
|
503
|
+
* (sortable) => api.getArticles({ sort: sortable }),
|
|
504
|
+
* )("created_at")(GaffComparator.dates((a) => a.created_at));
|
|
339
505
|
*
|
|
340
506
|
* await sortValidator("+"); // ascending
|
|
341
507
|
* await sortValidator("-"); // descending
|
|
@@ -343,10 +509,10 @@ export declare namespace TestValidator {
|
|
|
343
509
|
* // Test multi-field sorting with GaffComparator
|
|
344
510
|
* const userSortValidator = TestValidator.sort(
|
|
345
511
|
* "user sorting",
|
|
346
|
-
* (sortable) => api.getUsers({ sort: sortable })
|
|
512
|
+
* (sortable) => api.getUsers({ sort: sortable }),
|
|
347
513
|
* )("lastName", "firstName")(
|
|
348
514
|
* GaffComparator.strings((user) => [user.lastName, user.firstName]),
|
|
349
|
-
* (user) => user.isActive // only test active users
|
|
515
|
+
* (user) => user.isActive, // only test active users
|
|
350
516
|
* );
|
|
351
517
|
*
|
|
352
518
|
* await userSortValidator("+", true); // ascending with trace logging
|
|
@@ -354,20 +520,24 @@ export declare namespace TestValidator {
|
|
|
354
520
|
* // Custom comparator for complex logic
|
|
355
521
|
* const customSortValidator = TestValidator.sort(
|
|
356
522
|
* "custom sorting",
|
|
357
|
-
* (sortable) => api.getProducts({ sort: sortable })
|
|
358
|
-
* )(
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
523
|
+
* (sortable) => api.getProducts({ sort: sortable }),
|
|
524
|
+
* )(
|
|
525
|
+
* "price",
|
|
526
|
+
* "rating",
|
|
527
|
+
* )((a, b) => {
|
|
528
|
+
* const priceDiff = a.price - b.price;
|
|
529
|
+
* return priceDiff !== 0 ? priceDiff : b.rating - a.rating; // price asc, rating desc
|
|
530
|
+
* });
|
|
365
531
|
*
|
|
366
532
|
* @param title - Descriptive title used in error messages when sorting fails
|
|
367
533
|
* @param getter - API function that fetches sorted data
|
|
368
534
|
* @returns A currying function chain: field names, comparator, then direction
|
|
369
535
|
* @throws Error when API results are not properly sorted according to
|
|
370
536
|
* specification
|
|
537
|
+
* @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.
|
|
538
|
+
* @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.
|
|
539
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The order is judged by the caller's comparator, with no field or direction special-cased.
|
|
540
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents each stage, the parameters, and the thrown error, with examples.
|
|
371
541
|
*/
|
|
372
542
|
const sort: <T extends object, Fields extends string, Sortable extends Array<`-${Fields}` | `+${Fields}`> = Array<`-${Fields}` | `+${Fields}`>>(title: string, getter: (sortable: Sortable) => Promise<T[]>) => (...fields: Fields[]) => (comp: (x: T, y: T) => number, filter?: (elem: T) => boolean) => (direction: "+" | "-", trace?: boolean) => Promise<void>;
|
|
373
543
|
/**
|
|
@@ -377,14 +547,18 @@ export declare namespace TestValidator {
|
|
|
377
547
|
* prefixed with '+' for ascending order or '-' for descending order.
|
|
378
548
|
*
|
|
379
549
|
* @example
|
|
380
|
-
*
|
|
381
|
-
*
|
|
550
|
+
* type UserSortable = TestValidator.Sortable<
|
|
551
|
+
* "name" | "email" | "created_at"
|
|
552
|
+
* >;
|
|
382
553
|
* // Results in: Array<"-name" | "+name" | "-email" | "+email" | "-created_at" | "+created_at">
|
|
383
554
|
*
|
|
384
555
|
* const userSort: UserSortable = ["+name", "-created_at"];
|
|
385
|
-
* ```;
|
|
386
556
|
*
|
|
387
557
|
* @template Literal - String literal type representing available field names
|
|
558
|
+
* @evidence contracts/common.md#principled-implementation A sortable is an array of field names with a leading `+` or `-`, the notation the API under test receives.
|
|
559
|
+
* @evidence contracts/common.md#clear-and-simple-design A single template-literal type.
|
|
560
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
561
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states the notation.
|
|
388
562
|
*/
|
|
389
563
|
type Sortable<Literal extends string> = Array<`-${Literal}` | `+${Literal}`>;
|
|
390
564
|
}
|