@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.
- package/README.md +1 -1
- package/lib/ArrayUtil.d.ts +152 -70
- package/lib/ArrayUtil.js +169 -75
- 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 +196 -77
- 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 +253 -74
- package/lib/TestValidator.js +274 -97
- package/lib/TestValidator.js.map +1 -1
- package/lib/internal/json_equal_to.d.ts +21 -0
- package/lib/internal/json_equal_to.js +35 -6
- package/lib/internal/json_equal_to.js.map +1 -1
- package/package.json +3 -2
- package/src/ArrayUtil.ts +173 -77
- package/src/DynamicExecutor.ts +158 -15
- package/src/GaffComparator.ts +195 -77
- package/src/MapUtil.ts +37 -6
- package/src/RandomGenerator.ts +172 -89
- package/src/TestValidator.ts +302 -107
- package/src/internal/json_equal_to.ts +36 -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
|
/**
|
|
@@ -198,28 +302,55 @@ export declare namespace TestValidator {
|
|
|
198
302
|
* sequence. Commonly used for testing database queries, search results, and
|
|
199
303
|
* any paginated data APIs.
|
|
200
304
|
*
|
|
305
|
+
* A page holds the first entities, so only the common prefix of the two lists
|
|
306
|
+
* is compared. An empty result against expected entities, or the reverse,
|
|
307
|
+
* fails.
|
|
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
|
+
*
|
|
201
313
|
* @example
|
|
202
|
-
* ```typescript
|
|
203
314
|
* // Test article pagination
|
|
204
|
-
* const expectedArticles = await db.articles.findAll({
|
|
205
|
-
*
|
|
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
|
+
* });
|
|
206
322
|
*
|
|
207
|
-
* TestValidator.index(
|
|
208
|
-
*
|
|
323
|
+
* TestValidator.index(
|
|
324
|
+
* "article pagination order",
|
|
325
|
+
* expectedArticles,
|
|
326
|
+
* actualArticles,
|
|
327
|
+
* true, // enable trace logging
|
|
209
328
|
* );
|
|
210
329
|
*
|
|
211
330
|
* // Test user search results
|
|
212
|
-
* const manuallyFilteredUsers = allUsers.filter(u =>
|
|
213
|
-
*
|
|
331
|
+
* const manuallyFilteredUsers = allUsers.filter((u) =>
|
|
332
|
+
* u.name.includes("John"),
|
|
333
|
+
* );
|
|
334
|
+
* const apiSearchResults = await api.functional.searchUsers({
|
|
335
|
+
* query: "John",
|
|
336
|
+
* });
|
|
214
337
|
*
|
|
215
|
-
* TestValidator.index(
|
|
216
|
-
*
|
|
338
|
+
* TestValidator.index(
|
|
339
|
+
* "user search results",
|
|
340
|
+
* manuallyFilteredUsers,
|
|
341
|
+
* apiSearchResults,
|
|
342
|
+
* );
|
|
217
343
|
*
|
|
218
344
|
* @param title - Descriptive title used in error messages when order differs
|
|
219
345
|
* @param expected - The expected entities in correct order
|
|
220
346
|
* @param gotten - The actual entities returned by the API
|
|
221
347
|
* @param trace - Optional flag to enable debug logging (default: false)
|
|
222
348
|
* @throws Error when entity order differs between expected and actual results
|
|
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.
|
|
223
354
|
*/
|
|
224
355
|
const index: <X extends IEntity<any>, Y extends X = X>(title: string, expected: X[], gotten: Y[], trace?: boolean) => void;
|
|
225
356
|
/**
|
|
@@ -231,15 +362,27 @@ export declare namespace TestValidator {
|
|
|
231
362
|
* API, and compares results. Validates that search APIs return the correct
|
|
232
363
|
* subset of data matching the search criteria.
|
|
233
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
|
+
*
|
|
234
378
|
* @example
|
|
235
|
-
* ```typescript
|
|
236
379
|
* // Test article search functionality with exact matching
|
|
237
380
|
* const allArticles = await db.articles.findAll();
|
|
238
381
|
* const searchValidator = TestValidator.search(
|
|
239
382
|
* "article search API",
|
|
240
383
|
* (req) => api.searchArticles(req),
|
|
241
384
|
* allArticles,
|
|
242
|
-
* 5 // test with 5 random samples
|
|
385
|
+
* 5, // test with 5 random samples
|
|
243
386
|
* );
|
|
244
387
|
*
|
|
245
388
|
* // Test exact match search
|
|
@@ -247,7 +390,7 @@ export declare namespace TestValidator {
|
|
|
247
390
|
* fields: ["title"],
|
|
248
391
|
* values: (article) => [article.title], // full title for exact match
|
|
249
392
|
* filter: (article, [title]) => article.title === title, // exact match
|
|
250
|
-
* request: ([title]) => ({ search: { title } })
|
|
393
|
+
* request: ([title]) => ({ search: { title } }),
|
|
251
394
|
* });
|
|
252
395
|
*
|
|
253
396
|
* // Test partial match search with includes
|
|
@@ -255,7 +398,7 @@ export declare namespace TestValidator {
|
|
|
255
398
|
* fields: ["content"],
|
|
256
399
|
* values: (article) => [article.content.substring(0, 20)], // partial content
|
|
257
400
|
* filter: (article, [keyword]) => article.content.includes(keyword),
|
|
258
|
-
* request: ([keyword]) => ({ q: keyword })
|
|
401
|
+
* request: ([keyword]) => ({ q: keyword }),
|
|
259
402
|
* });
|
|
260
403
|
*
|
|
261
404
|
* // Test multi-field search with exact matching
|
|
@@ -264,9 +407,8 @@ export declare namespace TestValidator {
|
|
|
264
407
|
* values: (article) => [article.writer, article.title],
|
|
265
408
|
* filter: (article, [writer, title]) =>
|
|
266
409
|
* article.writer === writer && article.title === title,
|
|
267
|
-
* request: ([writer, title]) => ({ search: { writer, title } })
|
|
410
|
+
* request: ([writer, title]) => ({ search: { writer, title } }),
|
|
268
411
|
* });
|
|
269
|
-
* ```;
|
|
270
412
|
*
|
|
271
413
|
* @param title - Descriptive title used in error messages when search fails
|
|
272
414
|
* @param getter - API function that performs the search
|
|
@@ -274,6 +416,10 @@ export declare namespace TestValidator {
|
|
|
274
416
|
* @param sampleCount - Number of random samples to test (default: 1)
|
|
275
417
|
* @returns A function that accepts search configuration properties
|
|
276
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.
|
|
277
423
|
*/
|
|
278
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>;
|
|
279
425
|
/**
|
|
@@ -287,6 +433,10 @@ export declare namespace TestValidator {
|
|
|
287
433
|
* @template Values - Tuple type representing the search values extracted from
|
|
288
434
|
* entities
|
|
289
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.
|
|
290
440
|
*/
|
|
291
441
|
interface ISearchProps<Entity extends IEntity<any>, Values extends any[], Request> {
|
|
292
442
|
/** Field names being searched, used in error messages for identification */
|
|
@@ -296,6 +446,10 @@ export declare namespace TestValidator {
|
|
|
296
446
|
*
|
|
297
447
|
* @param entity - The entity to extract search values from
|
|
298
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.
|
|
299
453
|
*/
|
|
300
454
|
values(entity: Entity): Values;
|
|
301
455
|
/**
|
|
@@ -304,6 +458,10 @@ export declare namespace TestValidator {
|
|
|
304
458
|
* @param entity - Entity to test against criteria
|
|
305
459
|
* @param values - Search values to match against
|
|
306
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.
|
|
307
465
|
*/
|
|
308
466
|
filter(entity: Entity, values: Values): boolean;
|
|
309
467
|
/**
|
|
@@ -311,6 +469,10 @@ export declare namespace TestValidator {
|
|
|
311
469
|
*
|
|
312
470
|
* @param values - Search values to include in request
|
|
313
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.
|
|
314
476
|
*/
|
|
315
477
|
request(values: Values): Request;
|
|
316
478
|
}
|
|
@@ -322,15 +484,24 @@ export declare namespace TestValidator {
|
|
|
322
484
|
* ascending/descending order, and optional filtering. Provides detailed error
|
|
323
485
|
* reporting for sorting failures.
|
|
324
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
|
+
*
|
|
325
499
|
* @example
|
|
326
|
-
* ```typescript
|
|
327
500
|
* // Test single field sorting with GaffComparator
|
|
328
501
|
* const sortValidator = TestValidator.sort(
|
|
329
502
|
* "article sorting",
|
|
330
|
-
* (sortable) => api.getArticles({ sort: sortable })
|
|
331
|
-
* )("created_at")(
|
|
332
|
-
* GaffComparator.dates((a) => a.created_at)
|
|
333
|
-
* );
|
|
503
|
+
* (sortable) => api.getArticles({ sort: sortable }),
|
|
504
|
+
* )("created_at")(GaffComparator.dates((a) => a.created_at));
|
|
334
505
|
*
|
|
335
506
|
* await sortValidator("+"); // ascending
|
|
336
507
|
* await sortValidator("-"); // descending
|
|
@@ -338,10 +509,10 @@ export declare namespace TestValidator {
|
|
|
338
509
|
* // Test multi-field sorting with GaffComparator
|
|
339
510
|
* const userSortValidator = TestValidator.sort(
|
|
340
511
|
* "user sorting",
|
|
341
|
-
* (sortable) => api.getUsers({ sort: sortable })
|
|
512
|
+
* (sortable) => api.getUsers({ sort: sortable }),
|
|
342
513
|
* )("lastName", "firstName")(
|
|
343
514
|
* GaffComparator.strings((user) => [user.lastName, user.firstName]),
|
|
344
|
-
* (user) => user.isActive // only test active users
|
|
515
|
+
* (user) => user.isActive, // only test active users
|
|
345
516
|
* );
|
|
346
517
|
*
|
|
347
518
|
* await userSortValidator("+", true); // ascending with trace logging
|
|
@@ -349,20 +520,24 @@ export declare namespace TestValidator {
|
|
|
349
520
|
* // Custom comparator for complex logic
|
|
350
521
|
* const customSortValidator = TestValidator.sort(
|
|
351
522
|
* "custom sorting",
|
|
352
|
-
* (sortable) => api.getProducts({ sort: sortable })
|
|
353
|
-
* )(
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
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
|
+
* });
|
|
360
531
|
*
|
|
361
532
|
* @param title - Descriptive title used in error messages when sorting fails
|
|
362
533
|
* @param getter - API function that fetches sorted data
|
|
363
534
|
* @returns A currying function chain: field names, comparator, then direction
|
|
364
535
|
* @throws Error when API results are not properly sorted according to
|
|
365
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.
|
|
366
541
|
*/
|
|
367
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>;
|
|
368
543
|
/**
|
|
@@ -372,14 +547,18 @@ export declare namespace TestValidator {
|
|
|
372
547
|
* prefixed with '+' for ascending order or '-' for descending order.
|
|
373
548
|
*
|
|
374
549
|
* @example
|
|
375
|
-
*
|
|
376
|
-
*
|
|
550
|
+
* type UserSortable = TestValidator.Sortable<
|
|
551
|
+
* "name" | "email" | "created_at"
|
|
552
|
+
* >;
|
|
377
553
|
* // Results in: Array<"-name" | "+name" | "-email" | "+email" | "-created_at" | "+created_at">
|
|
378
554
|
*
|
|
379
555
|
* const userSort: UserSortable = ["+name", "-created_at"];
|
|
380
|
-
* ```;
|
|
381
556
|
*
|
|
382
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.
|
|
383
562
|
*/
|
|
384
563
|
type Sortable<Literal extends string> = Array<`-${Literal}` | `+${Literal}`>;
|
|
385
564
|
}
|