@riddledc/riddle-proof 0.7.65 → 0.7.67
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 +19 -0
- package/dist/{chunk-DQU65Z65.js → chunk-376FPGFA.js} +59 -7
- package/dist/cli.cjs +103 -7
- package/dist/cli.js +45 -1
- package/dist/index.cjs +59 -7
- package/dist/index.js +1 -1
- package/dist/profile.cjs +59 -7
- package/dist/profile.d.cts +2 -0
- package/dist/profile.d.ts +2 -0
- package/dist/profile.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -226,6 +226,25 @@ cycle instead of reusing the final response, for example to repeat a fail-then-
|
|
|
226
226
|
success pair across multiple viewports. Repeated sequences also record
|
|
227
227
|
`sequence_cycle: true` after the first cycle.
|
|
228
228
|
|
|
229
|
+
Use `max_hit_count` / `max_hits` when a profile needs to prove a request does
|
|
230
|
+
not run too many times. Use `forbidden: true` as shorthand for
|
|
231
|
+
`max_hit_count: 0` and `required: false`, for example when a chat failure must
|
|
232
|
+
not trigger a downstream build:
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"label": "builder-build-should-not-run",
|
|
237
|
+
"url": "**/api/build",
|
|
238
|
+
"method": "POST",
|
|
239
|
+
"forbidden": true,
|
|
240
|
+
"json": { "previewUrl": "https://cdn.example/should-not-run/index.html" }
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The implicit `network_mocks_succeeded` check records `max_hits_by_label` and
|
|
245
|
+
fails with `forbidden_mock_hit` or `mock_hit_count_exceeded` when a cap is
|
|
246
|
+
exceeded.
|
|
247
|
+
|
|
229
248
|
Set `capture_request_body: true` to include compact request-body evidence on
|
|
230
249
|
mock hits. Add `request_body_contains` / `request_body_patterns` or
|
|
231
250
|
`request_body_not_contains` / `request_body_not_patterns` when the request body
|
|
@@ -470,6 +470,22 @@ function normalizeNetworkMock(input, index) {
|
|
|
470
470
|
if (requiredHitCount !== void 0 && (!Number.isInteger(requiredHitCount) || requiredHitCount < 1)) {
|
|
471
471
|
throw new Error(`target.network_mocks[${index}].required_hit_count must be a positive integer.`);
|
|
472
472
|
}
|
|
473
|
+
const forbidden = input.forbidden === true || input.must_not_hit === true || input.mustNotHit === true || input.should_not_run === true || input.shouldNotRun === true;
|
|
474
|
+
const configuredMaxHitCount = numberValue(
|
|
475
|
+
input.max_hit_count ?? input.maxHitCount ?? input.max_hits ?? input.maxHits
|
|
476
|
+
);
|
|
477
|
+
if (configuredMaxHitCount !== void 0 && (!Number.isInteger(configuredMaxHitCount) || configuredMaxHitCount < 0)) {
|
|
478
|
+
throw new Error(`target.network_mocks[${index}].max_hit_count must be a non-negative integer.`);
|
|
479
|
+
}
|
|
480
|
+
if (forbidden && configuredMaxHitCount !== void 0 && configuredMaxHitCount !== 0) {
|
|
481
|
+
throw new Error(`target.network_mocks[${index}].forbidden cannot be combined with max_hit_count greater than 0.`);
|
|
482
|
+
}
|
|
483
|
+
const maxHitCount = forbidden ? 0 : configuredMaxHitCount;
|
|
484
|
+
const required = input.required === false || maxHitCount === 0 && input.required !== true ? false : true;
|
|
485
|
+
const effectiveRequiredHitCount = required ? requiredHitCount ?? (Array.isArray(responses) && responses.length ? responses.length : 1) : 0;
|
|
486
|
+
if (maxHitCount !== void 0 && effectiveRequiredHitCount > maxHitCount) {
|
|
487
|
+
throw new Error(`target.network_mocks[${index}].max_hit_count cannot be less than its required hit count.`);
|
|
488
|
+
}
|
|
473
489
|
return {
|
|
474
490
|
...payload,
|
|
475
491
|
label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
|
|
@@ -478,7 +494,9 @@ function normalizeNetworkMock(input, index) {
|
|
|
478
494
|
responses,
|
|
479
495
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
480
496
|
required_hit_count: requiredHitCount,
|
|
481
|
-
|
|
497
|
+
max_hit_count: maxHitCount,
|
|
498
|
+
forbidden,
|
|
499
|
+
required,
|
|
482
500
|
capture_request_body: requestBody.capture_request_body,
|
|
483
501
|
request_body_contains: requestBody.request_body_contains,
|
|
484
502
|
request_body_patterns: requestBody.request_body_patterns,
|
|
@@ -1388,8 +1406,9 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
1388
1406
|
const events = evidence.network_mocks || [];
|
|
1389
1407
|
const failed = [];
|
|
1390
1408
|
const requiredMocks = mocks.filter((mock) => mock.required !== false);
|
|
1409
|
+
const hitCountForMock = (mock) => events.filter((event) => event.label === mock.label && event.ok !== false).length;
|
|
1391
1410
|
for (const mock of requiredMocks) {
|
|
1392
|
-
const hits =
|
|
1411
|
+
const hits = hitCountForMock(mock);
|
|
1393
1412
|
const requiredHitCount = requiredNetworkMockHitCount(mock);
|
|
1394
1413
|
if (hits < requiredHitCount) {
|
|
1395
1414
|
failed.push({
|
|
@@ -1402,6 +1421,20 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
1402
1421
|
});
|
|
1403
1422
|
}
|
|
1404
1423
|
}
|
|
1424
|
+
for (const mock of mocks) {
|
|
1425
|
+
if (mock.max_hit_count === void 0) continue;
|
|
1426
|
+
const hits = hitCountForMock(mock);
|
|
1427
|
+
if (hits > mock.max_hit_count) {
|
|
1428
|
+
failed.push({
|
|
1429
|
+
label: mock.label,
|
|
1430
|
+
url: mock.url,
|
|
1431
|
+
method: mock.method || null,
|
|
1432
|
+
reason: mock.max_hit_count === 0 ? "forbidden_mock_hit" : "mock_hit_count_exceeded",
|
|
1433
|
+
max_hit_count: mock.max_hit_count,
|
|
1434
|
+
hit_count: hits
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1405
1438
|
for (const event of events) {
|
|
1406
1439
|
if (event.ok === false) {
|
|
1407
1440
|
failed.push({
|
|
@@ -1436,15 +1469,16 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
1436
1469
|
hit_count: events.filter((event) => event.ok !== false).length,
|
|
1437
1470
|
hits_by_label: Object.fromEntries(mocks.map((mock) => [
|
|
1438
1471
|
mock.label,
|
|
1439
|
-
|
|
1472
|
+
hitCountForMock(mock)
|
|
1440
1473
|
])),
|
|
1441
1474
|
required_hits_by_label: Object.fromEntries(requiredMocks.map((mock) => [
|
|
1442
1475
|
mock.label,
|
|
1443
1476
|
requiredNetworkMockHitCount(mock)
|
|
1444
1477
|
])),
|
|
1478
|
+
max_hits_by_label: Object.fromEntries(mocks.filter((mock) => mock.max_hit_count !== void 0).map((mock) => [mock.label, mock.max_hit_count ?? null])),
|
|
1445
1479
|
failed
|
|
1446
1480
|
},
|
|
1447
|
-
message: failed.length ? `Network mocks failed or
|
|
1481
|
+
message: failed.length ? `Network mocks failed or hit-count contracts failed for ${failed.length} mock(s).` : void 0
|
|
1448
1482
|
};
|
|
1449
1483
|
}
|
|
1450
1484
|
function requiredNetworkMockHitCount(mock) {
|
|
@@ -1904,8 +1938,9 @@ function assessProfile(profile, evidence) {
|
|
|
1904
1938
|
const events = evidence.network_mocks || [];
|
|
1905
1939
|
const requiredMocks = profile.target.network_mocks.filter((mock) => mock && mock.required !== false);
|
|
1906
1940
|
const failed = [];
|
|
1941
|
+
const hitCountForMock = (mock) => events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
1907
1942
|
for (const mock of requiredMocks) {
|
|
1908
|
-
const hits =
|
|
1943
|
+
const hits = hitCountForMock(mock);
|
|
1909
1944
|
const requiredHitCount = requiredNetworkMockHitCount(mock);
|
|
1910
1945
|
if (hits < requiredHitCount) {
|
|
1911
1946
|
failed.push({
|
|
@@ -1918,6 +1953,20 @@ function assessProfile(profile, evidence) {
|
|
|
1918
1953
|
});
|
|
1919
1954
|
}
|
|
1920
1955
|
}
|
|
1956
|
+
for (const mock of profile.target.network_mocks) {
|
|
1957
|
+
if (!mock || mock.max_hit_count === undefined) continue;
|
|
1958
|
+
const hits = hitCountForMock(mock);
|
|
1959
|
+
if (hits > mock.max_hit_count) {
|
|
1960
|
+
failed.push({
|
|
1961
|
+
label: mock.label,
|
|
1962
|
+
url: mock.url,
|
|
1963
|
+
method: mock.method || null,
|
|
1964
|
+
reason: mock.max_hit_count === 0 ? "forbidden_mock_hit" : "mock_hit_count_exceeded",
|
|
1965
|
+
max_hit_count: mock.max_hit_count,
|
|
1966
|
+
hit_count: hits,
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1921
1970
|
for (const event of events) {
|
|
1922
1971
|
if (event && event.ok === false) {
|
|
1923
1972
|
failed.push({
|
|
@@ -1944,9 +1993,11 @@ function assessProfile(profile, evidence) {
|
|
|
1944
1993
|
}
|
|
1945
1994
|
const hitsByLabel = {};
|
|
1946
1995
|
const requiredHitsByLabel = {};
|
|
1996
|
+
const maxHitsByLabel = {};
|
|
1947
1997
|
for (const mock of profile.target.network_mocks) {
|
|
1948
|
-
hitsByLabel[mock.label] =
|
|
1998
|
+
hitsByLabel[mock.label] = hitCountForMock(mock);
|
|
1949
1999
|
if (mock && mock.required !== false) requiredHitsByLabel[mock.label] = requiredNetworkMockHitCount(mock);
|
|
2000
|
+
if (mock && mock.max_hit_count !== undefined) maxHitsByLabel[mock.label] = mock.max_hit_count;
|
|
1950
2001
|
}
|
|
1951
2002
|
checks.push({
|
|
1952
2003
|
type: "network_mocks_succeeded",
|
|
@@ -1958,9 +2009,10 @@ function assessProfile(profile, evidence) {
|
|
|
1958
2009
|
hit_count: events.filter((event) => event && event.ok !== false).length,
|
|
1959
2010
|
hits_by_label: hitsByLabel,
|
|
1960
2011
|
required_hits_by_label: requiredHitsByLabel,
|
|
2012
|
+
max_hits_by_label: maxHitsByLabel,
|
|
1961
2013
|
failed,
|
|
1962
2014
|
},
|
|
1963
|
-
message: failed.length ? "Network mocks failed or
|
|
2015
|
+
message: failed.length ? "Network mocks failed or hit-count contracts failed for " + failed.length + " mock(s)." : undefined,
|
|
1964
2016
|
});
|
|
1965
2017
|
}
|
|
1966
2018
|
if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
|
package/dist/cli.cjs
CHANGED
|
@@ -7343,6 +7343,22 @@ function normalizeNetworkMock(input, index) {
|
|
|
7343
7343
|
if (requiredHitCount !== void 0 && (!Number.isInteger(requiredHitCount) || requiredHitCount < 1)) {
|
|
7344
7344
|
throw new Error(`target.network_mocks[${index}].required_hit_count must be a positive integer.`);
|
|
7345
7345
|
}
|
|
7346
|
+
const forbidden = input.forbidden === true || input.must_not_hit === true || input.mustNotHit === true || input.should_not_run === true || input.shouldNotRun === true;
|
|
7347
|
+
const configuredMaxHitCount = numberValue(
|
|
7348
|
+
input.max_hit_count ?? input.maxHitCount ?? input.max_hits ?? input.maxHits
|
|
7349
|
+
);
|
|
7350
|
+
if (configuredMaxHitCount !== void 0 && (!Number.isInteger(configuredMaxHitCount) || configuredMaxHitCount < 0)) {
|
|
7351
|
+
throw new Error(`target.network_mocks[${index}].max_hit_count must be a non-negative integer.`);
|
|
7352
|
+
}
|
|
7353
|
+
if (forbidden && configuredMaxHitCount !== void 0 && configuredMaxHitCount !== 0) {
|
|
7354
|
+
throw new Error(`target.network_mocks[${index}].forbidden cannot be combined with max_hit_count greater than 0.`);
|
|
7355
|
+
}
|
|
7356
|
+
const maxHitCount = forbidden ? 0 : configuredMaxHitCount;
|
|
7357
|
+
const required = input.required === false || maxHitCount === 0 && input.required !== true ? false : true;
|
|
7358
|
+
const effectiveRequiredHitCount = required ? requiredHitCount ?? (Array.isArray(responses) && responses.length ? responses.length : 1) : 0;
|
|
7359
|
+
if (maxHitCount !== void 0 && effectiveRequiredHitCount > maxHitCount) {
|
|
7360
|
+
throw new Error(`target.network_mocks[${index}].max_hit_count cannot be less than its required hit count.`);
|
|
7361
|
+
}
|
|
7346
7362
|
return {
|
|
7347
7363
|
...payload,
|
|
7348
7364
|
label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
|
|
@@ -7351,7 +7367,9 @@ function normalizeNetworkMock(input, index) {
|
|
|
7351
7367
|
responses,
|
|
7352
7368
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
7353
7369
|
required_hit_count: requiredHitCount,
|
|
7354
|
-
|
|
7370
|
+
max_hit_count: maxHitCount,
|
|
7371
|
+
forbidden,
|
|
7372
|
+
required,
|
|
7355
7373
|
capture_request_body: requestBody.capture_request_body,
|
|
7356
7374
|
request_body_contains: requestBody.request_body_contains,
|
|
7357
7375
|
request_body_patterns: requestBody.request_body_patterns,
|
|
@@ -8261,8 +8279,9 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
8261
8279
|
const events = evidence.network_mocks || [];
|
|
8262
8280
|
const failed = [];
|
|
8263
8281
|
const requiredMocks = mocks.filter((mock) => mock.required !== false);
|
|
8282
|
+
const hitCountForMock = (mock) => events.filter((event) => event.label === mock.label && event.ok !== false).length;
|
|
8264
8283
|
for (const mock of requiredMocks) {
|
|
8265
|
-
const hits =
|
|
8284
|
+
const hits = hitCountForMock(mock);
|
|
8266
8285
|
const requiredHitCount = requiredNetworkMockHitCount(mock);
|
|
8267
8286
|
if (hits < requiredHitCount) {
|
|
8268
8287
|
failed.push({
|
|
@@ -8275,6 +8294,20 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
8275
8294
|
});
|
|
8276
8295
|
}
|
|
8277
8296
|
}
|
|
8297
|
+
for (const mock of mocks) {
|
|
8298
|
+
if (mock.max_hit_count === void 0) continue;
|
|
8299
|
+
const hits = hitCountForMock(mock);
|
|
8300
|
+
if (hits > mock.max_hit_count) {
|
|
8301
|
+
failed.push({
|
|
8302
|
+
label: mock.label,
|
|
8303
|
+
url: mock.url,
|
|
8304
|
+
method: mock.method || null,
|
|
8305
|
+
reason: mock.max_hit_count === 0 ? "forbidden_mock_hit" : "mock_hit_count_exceeded",
|
|
8306
|
+
max_hit_count: mock.max_hit_count,
|
|
8307
|
+
hit_count: hits
|
|
8308
|
+
});
|
|
8309
|
+
}
|
|
8310
|
+
}
|
|
8278
8311
|
for (const event of events) {
|
|
8279
8312
|
if (event.ok === false) {
|
|
8280
8313
|
failed.push({
|
|
@@ -8309,15 +8342,16 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
8309
8342
|
hit_count: events.filter((event) => event.ok !== false).length,
|
|
8310
8343
|
hits_by_label: Object.fromEntries(mocks.map((mock) => [
|
|
8311
8344
|
mock.label,
|
|
8312
|
-
|
|
8345
|
+
hitCountForMock(mock)
|
|
8313
8346
|
])),
|
|
8314
8347
|
required_hits_by_label: Object.fromEntries(requiredMocks.map((mock) => [
|
|
8315
8348
|
mock.label,
|
|
8316
8349
|
requiredNetworkMockHitCount(mock)
|
|
8317
8350
|
])),
|
|
8351
|
+
max_hits_by_label: Object.fromEntries(mocks.filter((mock) => mock.max_hit_count !== void 0).map((mock) => [mock.label, mock.max_hit_count ?? null])),
|
|
8318
8352
|
failed
|
|
8319
8353
|
},
|
|
8320
|
-
message: failed.length ? `Network mocks failed or
|
|
8354
|
+
message: failed.length ? `Network mocks failed or hit-count contracts failed for ${failed.length} mock(s).` : void 0
|
|
8321
8355
|
};
|
|
8322
8356
|
}
|
|
8323
8357
|
function requiredNetworkMockHitCount(mock) {
|
|
@@ -8761,8 +8795,9 @@ function assessProfile(profile, evidence) {
|
|
|
8761
8795
|
const events = evidence.network_mocks || [];
|
|
8762
8796
|
const requiredMocks = profile.target.network_mocks.filter((mock) => mock && mock.required !== false);
|
|
8763
8797
|
const failed = [];
|
|
8798
|
+
const hitCountForMock = (mock) => events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
8764
8799
|
for (const mock of requiredMocks) {
|
|
8765
|
-
const hits =
|
|
8800
|
+
const hits = hitCountForMock(mock);
|
|
8766
8801
|
const requiredHitCount = requiredNetworkMockHitCount(mock);
|
|
8767
8802
|
if (hits < requiredHitCount) {
|
|
8768
8803
|
failed.push({
|
|
@@ -8775,6 +8810,20 @@ function assessProfile(profile, evidence) {
|
|
|
8775
8810
|
});
|
|
8776
8811
|
}
|
|
8777
8812
|
}
|
|
8813
|
+
for (const mock of profile.target.network_mocks) {
|
|
8814
|
+
if (!mock || mock.max_hit_count === undefined) continue;
|
|
8815
|
+
const hits = hitCountForMock(mock);
|
|
8816
|
+
if (hits > mock.max_hit_count) {
|
|
8817
|
+
failed.push({
|
|
8818
|
+
label: mock.label,
|
|
8819
|
+
url: mock.url,
|
|
8820
|
+
method: mock.method || null,
|
|
8821
|
+
reason: mock.max_hit_count === 0 ? "forbidden_mock_hit" : "mock_hit_count_exceeded",
|
|
8822
|
+
max_hit_count: mock.max_hit_count,
|
|
8823
|
+
hit_count: hits,
|
|
8824
|
+
});
|
|
8825
|
+
}
|
|
8826
|
+
}
|
|
8778
8827
|
for (const event of events) {
|
|
8779
8828
|
if (event && event.ok === false) {
|
|
8780
8829
|
failed.push({
|
|
@@ -8801,9 +8850,11 @@ function assessProfile(profile, evidence) {
|
|
|
8801
8850
|
}
|
|
8802
8851
|
const hitsByLabel = {};
|
|
8803
8852
|
const requiredHitsByLabel = {};
|
|
8853
|
+
const maxHitsByLabel = {};
|
|
8804
8854
|
for (const mock of profile.target.network_mocks) {
|
|
8805
|
-
hitsByLabel[mock.label] =
|
|
8855
|
+
hitsByLabel[mock.label] = hitCountForMock(mock);
|
|
8806
8856
|
if (mock && mock.required !== false) requiredHitsByLabel[mock.label] = requiredNetworkMockHitCount(mock);
|
|
8857
|
+
if (mock && mock.max_hit_count !== undefined) maxHitsByLabel[mock.label] = mock.max_hit_count;
|
|
8807
8858
|
}
|
|
8808
8859
|
checks.push({
|
|
8809
8860
|
type: "network_mocks_succeeded",
|
|
@@ -8815,9 +8866,10 @@ function assessProfile(profile, evidence) {
|
|
|
8815
8866
|
hit_count: events.filter((event) => event && event.ok !== false).length,
|
|
8816
8867
|
hits_by_label: hitsByLabel,
|
|
8817
8868
|
required_hits_by_label: requiredHitsByLabel,
|
|
8869
|
+
max_hits_by_label: maxHitsByLabel,
|
|
8818
8870
|
failed,
|
|
8819
8871
|
},
|
|
8820
|
-
message: failed.length ? "Network mocks failed or
|
|
8872
|
+
message: failed.length ? "Network mocks failed or hit-count contracts failed for " + failed.length + " mock(s)." : undefined,
|
|
8821
8873
|
});
|
|
8822
8874
|
}
|
|
8823
8875
|
if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
|
|
@@ -11138,6 +11190,10 @@ function profileResultMarkdown(result) {
|
|
|
11138
11190
|
lines.push(`- ${check.status}: ${check.label || check.type}`);
|
|
11139
11191
|
if (check.message) lines.push(` ${check.message}`);
|
|
11140
11192
|
}
|
|
11193
|
+
const setupSummaryLines = profileSetupSummaryMarkdown(result);
|
|
11194
|
+
if (setupSummaryLines.length) {
|
|
11195
|
+
lines.push("", "## Setup Summary", "", ...setupSummaryLines);
|
|
11196
|
+
}
|
|
11141
11197
|
if (result.artifacts.riddle_artifacts?.length) {
|
|
11142
11198
|
lines.push("", "## Riddle Artifacts", "");
|
|
11143
11199
|
for (const artifact of result.artifacts.riddle_artifacts.slice(0, 40)) {
|
|
@@ -11147,6 +11203,46 @@ function profileResultMarkdown(result) {
|
|
|
11147
11203
|
return `${lines.join("\n")}
|
|
11148
11204
|
`;
|
|
11149
11205
|
}
|
|
11206
|
+
function cliRecord(value) {
|
|
11207
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
11208
|
+
}
|
|
11209
|
+
function cliFiniteNumber(value) {
|
|
11210
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
11211
|
+
}
|
|
11212
|
+
function cliString(value) {
|
|
11213
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
11214
|
+
}
|
|
11215
|
+
function profileSetupSummaryMarkdown(result) {
|
|
11216
|
+
const setupCheck = result.checks.find((check) => check.type === "setup_actions_succeeded");
|
|
11217
|
+
const setupSummary = cliRecord(setupCheck?.evidence?.setup_summary);
|
|
11218
|
+
if (!setupSummary) return [];
|
|
11219
|
+
const viewports = Array.isArray(setupSummary.viewports) ? setupSummary.viewports.map(cliRecord).filter((viewport) => Boolean(viewport)) : [];
|
|
11220
|
+
if (!viewports.length) return [];
|
|
11221
|
+
const declaredActions = cliFiniteNumber(setupSummary.action_count);
|
|
11222
|
+
const totalResults = viewports.reduce((sum, viewport) => sum + (cliFiniteNumber(viewport.result_count) || 0), 0);
|
|
11223
|
+
const setupScreenshots = viewports.reduce((sum, viewport) => {
|
|
11224
|
+
const labels = Array.isArray(viewport.setup_screenshots) ? viewport.setup_screenshots : [];
|
|
11225
|
+
return sum + labels.filter((label) => typeof label === "string" && label.trim()).length;
|
|
11226
|
+
}, 0);
|
|
11227
|
+
const clickedTotal = viewports.reduce((sum, viewport) => sum + (cliFiniteNumber(viewport.clicked_total) || 0), 0);
|
|
11228
|
+
const failedTotal = viewports.reduce((sum, viewport) => sum + (Array.isArray(viewport.failed) ? viewport.failed.length : 0), 0);
|
|
11229
|
+
const lines = [
|
|
11230
|
+
`- setup actions: ${declaredActions === void 0 ? "unknown" : declaredActions} declared, ${totalResults} recorded result(s) across ${viewports.length} viewport(s)`,
|
|
11231
|
+
`- setup screenshots: ${setupScreenshots}`,
|
|
11232
|
+
`- clicked targets: ${clickedTotal}${failedTotal ? `; failed setup actions: ${failedTotal}` : ""}`
|
|
11233
|
+
];
|
|
11234
|
+
for (const viewport of viewports.slice(0, 8)) {
|
|
11235
|
+
const name = cliString(viewport.name) || "viewport";
|
|
11236
|
+
const ok = viewport.ok === false ? "failed" : "ok";
|
|
11237
|
+
const resultCount = cliFiniteNumber(viewport.result_count) || 0;
|
|
11238
|
+
const screenshotCount = Array.isArray(viewport.setup_screenshots) ? viewport.setup_screenshots.filter((label) => typeof label === "string" && label.trim()).length : 0;
|
|
11239
|
+
const clicked = cliFiniteNumber(viewport.clicked_total) || 0;
|
|
11240
|
+
const observedPath = cliString(viewport.observed_path);
|
|
11241
|
+
lines.push(`- ${name}: ${ok}, ${resultCount} result(s), ${screenshotCount} setup screenshot(s), ${clicked} click(s)${observedPath ? `, path ${observedPath}` : ""}`);
|
|
11242
|
+
}
|
|
11243
|
+
if (viewports.length > 8) lines.push(`- ${viewports.length - 8} additional viewport(s) omitted from setup summary.`);
|
|
11244
|
+
return lines;
|
|
11245
|
+
}
|
|
11150
11246
|
function writeProfileOutput(outputDir, result) {
|
|
11151
11247
|
if (!outputDir) return;
|
|
11152
11248
|
(0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
profileStatusExitCode,
|
|
11
11
|
resolveRiddleProofProfileTargetUrl,
|
|
12
12
|
resolveRiddleProofProfileTimeoutSec
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-376FPGFA.js";
|
|
14
14
|
import {
|
|
15
15
|
createRiddleApiClient,
|
|
16
16
|
parseRiddleViewport
|
|
@@ -312,6 +312,10 @@ function profileResultMarkdown(result) {
|
|
|
312
312
|
lines.push(`- ${check.status}: ${check.label || check.type}`);
|
|
313
313
|
if (check.message) lines.push(` ${check.message}`);
|
|
314
314
|
}
|
|
315
|
+
const setupSummaryLines = profileSetupSummaryMarkdown(result);
|
|
316
|
+
if (setupSummaryLines.length) {
|
|
317
|
+
lines.push("", "## Setup Summary", "", ...setupSummaryLines);
|
|
318
|
+
}
|
|
315
319
|
if (result.artifacts.riddle_artifacts?.length) {
|
|
316
320
|
lines.push("", "## Riddle Artifacts", "");
|
|
317
321
|
for (const artifact of result.artifacts.riddle_artifacts.slice(0, 40)) {
|
|
@@ -321,6 +325,46 @@ function profileResultMarkdown(result) {
|
|
|
321
325
|
return `${lines.join("\n")}
|
|
322
326
|
`;
|
|
323
327
|
}
|
|
328
|
+
function cliRecord(value) {
|
|
329
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
330
|
+
}
|
|
331
|
+
function cliFiniteNumber(value) {
|
|
332
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
333
|
+
}
|
|
334
|
+
function cliString(value) {
|
|
335
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
336
|
+
}
|
|
337
|
+
function profileSetupSummaryMarkdown(result) {
|
|
338
|
+
const setupCheck = result.checks.find((check) => check.type === "setup_actions_succeeded");
|
|
339
|
+
const setupSummary = cliRecord(setupCheck?.evidence?.setup_summary);
|
|
340
|
+
if (!setupSummary) return [];
|
|
341
|
+
const viewports = Array.isArray(setupSummary.viewports) ? setupSummary.viewports.map(cliRecord).filter((viewport) => Boolean(viewport)) : [];
|
|
342
|
+
if (!viewports.length) return [];
|
|
343
|
+
const declaredActions = cliFiniteNumber(setupSummary.action_count);
|
|
344
|
+
const totalResults = viewports.reduce((sum, viewport) => sum + (cliFiniteNumber(viewport.result_count) || 0), 0);
|
|
345
|
+
const setupScreenshots = viewports.reduce((sum, viewport) => {
|
|
346
|
+
const labels = Array.isArray(viewport.setup_screenshots) ? viewport.setup_screenshots : [];
|
|
347
|
+
return sum + labels.filter((label) => typeof label === "string" && label.trim()).length;
|
|
348
|
+
}, 0);
|
|
349
|
+
const clickedTotal = viewports.reduce((sum, viewport) => sum + (cliFiniteNumber(viewport.clicked_total) || 0), 0);
|
|
350
|
+
const failedTotal = viewports.reduce((sum, viewport) => sum + (Array.isArray(viewport.failed) ? viewport.failed.length : 0), 0);
|
|
351
|
+
const lines = [
|
|
352
|
+
`- setup actions: ${declaredActions === void 0 ? "unknown" : declaredActions} declared, ${totalResults} recorded result(s) across ${viewports.length} viewport(s)`,
|
|
353
|
+
`- setup screenshots: ${setupScreenshots}`,
|
|
354
|
+
`- clicked targets: ${clickedTotal}${failedTotal ? `; failed setup actions: ${failedTotal}` : ""}`
|
|
355
|
+
];
|
|
356
|
+
for (const viewport of viewports.slice(0, 8)) {
|
|
357
|
+
const name = cliString(viewport.name) || "viewport";
|
|
358
|
+
const ok = viewport.ok === false ? "failed" : "ok";
|
|
359
|
+
const resultCount = cliFiniteNumber(viewport.result_count) || 0;
|
|
360
|
+
const screenshotCount = Array.isArray(viewport.setup_screenshots) ? viewport.setup_screenshots.filter((label) => typeof label === "string" && label.trim()).length : 0;
|
|
361
|
+
const clicked = cliFiniteNumber(viewport.clicked_total) || 0;
|
|
362
|
+
const observedPath = cliString(viewport.observed_path);
|
|
363
|
+
lines.push(`- ${name}: ${ok}, ${resultCount} result(s), ${screenshotCount} setup screenshot(s), ${clicked} click(s)${observedPath ? `, path ${observedPath}` : ""}`);
|
|
364
|
+
}
|
|
365
|
+
if (viewports.length > 8) lines.push(`- ${viewports.length - 8} additional viewport(s) omitted from setup summary.`);
|
|
366
|
+
return lines;
|
|
367
|
+
}
|
|
324
368
|
function writeProfileOutput(outputDir, result) {
|
|
325
369
|
if (!outputDir) return;
|
|
326
370
|
mkdirSync(outputDir, { recursive: true });
|
package/dist/index.cjs
CHANGED
|
@@ -9184,6 +9184,22 @@ function normalizeNetworkMock(input, index) {
|
|
|
9184
9184
|
if (requiredHitCount !== void 0 && (!Number.isInteger(requiredHitCount) || requiredHitCount < 1)) {
|
|
9185
9185
|
throw new Error(`target.network_mocks[${index}].required_hit_count must be a positive integer.`);
|
|
9186
9186
|
}
|
|
9187
|
+
const forbidden = input.forbidden === true || input.must_not_hit === true || input.mustNotHit === true || input.should_not_run === true || input.shouldNotRun === true;
|
|
9188
|
+
const configuredMaxHitCount = numberValue3(
|
|
9189
|
+
input.max_hit_count ?? input.maxHitCount ?? input.max_hits ?? input.maxHits
|
|
9190
|
+
);
|
|
9191
|
+
if (configuredMaxHitCount !== void 0 && (!Number.isInteger(configuredMaxHitCount) || configuredMaxHitCount < 0)) {
|
|
9192
|
+
throw new Error(`target.network_mocks[${index}].max_hit_count must be a non-negative integer.`);
|
|
9193
|
+
}
|
|
9194
|
+
if (forbidden && configuredMaxHitCount !== void 0 && configuredMaxHitCount !== 0) {
|
|
9195
|
+
throw new Error(`target.network_mocks[${index}].forbidden cannot be combined with max_hit_count greater than 0.`);
|
|
9196
|
+
}
|
|
9197
|
+
const maxHitCount = forbidden ? 0 : configuredMaxHitCount;
|
|
9198
|
+
const required = input.required === false || maxHitCount === 0 && input.required !== true ? false : true;
|
|
9199
|
+
const effectiveRequiredHitCount = required ? requiredHitCount ?? (Array.isArray(responses) && responses.length ? responses.length : 1) : 0;
|
|
9200
|
+
if (maxHitCount !== void 0 && effectiveRequiredHitCount > maxHitCount) {
|
|
9201
|
+
throw new Error(`target.network_mocks[${index}].max_hit_count cannot be less than its required hit count.`);
|
|
9202
|
+
}
|
|
9187
9203
|
return {
|
|
9188
9204
|
...payload,
|
|
9189
9205
|
label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
|
|
@@ -9192,7 +9208,9 @@ function normalizeNetworkMock(input, index) {
|
|
|
9192
9208
|
responses,
|
|
9193
9209
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
9194
9210
|
required_hit_count: requiredHitCount,
|
|
9195
|
-
|
|
9211
|
+
max_hit_count: maxHitCount,
|
|
9212
|
+
forbidden,
|
|
9213
|
+
required,
|
|
9196
9214
|
capture_request_body: requestBody.capture_request_body,
|
|
9197
9215
|
request_body_contains: requestBody.request_body_contains,
|
|
9198
9216
|
request_body_patterns: requestBody.request_body_patterns,
|
|
@@ -10102,8 +10120,9 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
10102
10120
|
const events = evidence.network_mocks || [];
|
|
10103
10121
|
const failed = [];
|
|
10104
10122
|
const requiredMocks = mocks.filter((mock) => mock.required !== false);
|
|
10123
|
+
const hitCountForMock = (mock) => events.filter((event) => event.label === mock.label && event.ok !== false).length;
|
|
10105
10124
|
for (const mock of requiredMocks) {
|
|
10106
|
-
const hits =
|
|
10125
|
+
const hits = hitCountForMock(mock);
|
|
10107
10126
|
const requiredHitCount = requiredNetworkMockHitCount(mock);
|
|
10108
10127
|
if (hits < requiredHitCount) {
|
|
10109
10128
|
failed.push({
|
|
@@ -10116,6 +10135,20 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
10116
10135
|
});
|
|
10117
10136
|
}
|
|
10118
10137
|
}
|
|
10138
|
+
for (const mock of mocks) {
|
|
10139
|
+
if (mock.max_hit_count === void 0) continue;
|
|
10140
|
+
const hits = hitCountForMock(mock);
|
|
10141
|
+
if (hits > mock.max_hit_count) {
|
|
10142
|
+
failed.push({
|
|
10143
|
+
label: mock.label,
|
|
10144
|
+
url: mock.url,
|
|
10145
|
+
method: mock.method || null,
|
|
10146
|
+
reason: mock.max_hit_count === 0 ? "forbidden_mock_hit" : "mock_hit_count_exceeded",
|
|
10147
|
+
max_hit_count: mock.max_hit_count,
|
|
10148
|
+
hit_count: hits
|
|
10149
|
+
});
|
|
10150
|
+
}
|
|
10151
|
+
}
|
|
10119
10152
|
for (const event of events) {
|
|
10120
10153
|
if (event.ok === false) {
|
|
10121
10154
|
failed.push({
|
|
@@ -10150,15 +10183,16 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
10150
10183
|
hit_count: events.filter((event) => event.ok !== false).length,
|
|
10151
10184
|
hits_by_label: Object.fromEntries(mocks.map((mock) => [
|
|
10152
10185
|
mock.label,
|
|
10153
|
-
|
|
10186
|
+
hitCountForMock(mock)
|
|
10154
10187
|
])),
|
|
10155
10188
|
required_hits_by_label: Object.fromEntries(requiredMocks.map((mock) => [
|
|
10156
10189
|
mock.label,
|
|
10157
10190
|
requiredNetworkMockHitCount(mock)
|
|
10158
10191
|
])),
|
|
10192
|
+
max_hits_by_label: Object.fromEntries(mocks.filter((mock) => mock.max_hit_count !== void 0).map((mock) => [mock.label, mock.max_hit_count ?? null])),
|
|
10159
10193
|
failed
|
|
10160
10194
|
},
|
|
10161
|
-
message: failed.length ? `Network mocks failed or
|
|
10195
|
+
message: failed.length ? `Network mocks failed or hit-count contracts failed for ${failed.length} mock(s).` : void 0
|
|
10162
10196
|
};
|
|
10163
10197
|
}
|
|
10164
10198
|
function requiredNetworkMockHitCount(mock) {
|
|
@@ -10618,8 +10652,9 @@ function assessProfile(profile, evidence) {
|
|
|
10618
10652
|
const events = evidence.network_mocks || [];
|
|
10619
10653
|
const requiredMocks = profile.target.network_mocks.filter((mock) => mock && mock.required !== false);
|
|
10620
10654
|
const failed = [];
|
|
10655
|
+
const hitCountForMock = (mock) => events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
10621
10656
|
for (const mock of requiredMocks) {
|
|
10622
|
-
const hits =
|
|
10657
|
+
const hits = hitCountForMock(mock);
|
|
10623
10658
|
const requiredHitCount = requiredNetworkMockHitCount(mock);
|
|
10624
10659
|
if (hits < requiredHitCount) {
|
|
10625
10660
|
failed.push({
|
|
@@ -10632,6 +10667,20 @@ function assessProfile(profile, evidence) {
|
|
|
10632
10667
|
});
|
|
10633
10668
|
}
|
|
10634
10669
|
}
|
|
10670
|
+
for (const mock of profile.target.network_mocks) {
|
|
10671
|
+
if (!mock || mock.max_hit_count === undefined) continue;
|
|
10672
|
+
const hits = hitCountForMock(mock);
|
|
10673
|
+
if (hits > mock.max_hit_count) {
|
|
10674
|
+
failed.push({
|
|
10675
|
+
label: mock.label,
|
|
10676
|
+
url: mock.url,
|
|
10677
|
+
method: mock.method || null,
|
|
10678
|
+
reason: mock.max_hit_count === 0 ? "forbidden_mock_hit" : "mock_hit_count_exceeded",
|
|
10679
|
+
max_hit_count: mock.max_hit_count,
|
|
10680
|
+
hit_count: hits,
|
|
10681
|
+
});
|
|
10682
|
+
}
|
|
10683
|
+
}
|
|
10635
10684
|
for (const event of events) {
|
|
10636
10685
|
if (event && event.ok === false) {
|
|
10637
10686
|
failed.push({
|
|
@@ -10658,9 +10707,11 @@ function assessProfile(profile, evidence) {
|
|
|
10658
10707
|
}
|
|
10659
10708
|
const hitsByLabel = {};
|
|
10660
10709
|
const requiredHitsByLabel = {};
|
|
10710
|
+
const maxHitsByLabel = {};
|
|
10661
10711
|
for (const mock of profile.target.network_mocks) {
|
|
10662
|
-
hitsByLabel[mock.label] =
|
|
10712
|
+
hitsByLabel[mock.label] = hitCountForMock(mock);
|
|
10663
10713
|
if (mock && mock.required !== false) requiredHitsByLabel[mock.label] = requiredNetworkMockHitCount(mock);
|
|
10714
|
+
if (mock && mock.max_hit_count !== undefined) maxHitsByLabel[mock.label] = mock.max_hit_count;
|
|
10664
10715
|
}
|
|
10665
10716
|
checks.push({
|
|
10666
10717
|
type: "network_mocks_succeeded",
|
|
@@ -10672,9 +10723,10 @@ function assessProfile(profile, evidence) {
|
|
|
10672
10723
|
hit_count: events.filter((event) => event && event.ok !== false).length,
|
|
10673
10724
|
hits_by_label: hitsByLabel,
|
|
10674
10725
|
required_hits_by_label: requiredHitsByLabel,
|
|
10726
|
+
max_hits_by_label: maxHitsByLabel,
|
|
10675
10727
|
failed,
|
|
10676
10728
|
},
|
|
10677
|
-
message: failed.length ? "Network mocks failed or
|
|
10729
|
+
message: failed.length ? "Network mocks failed or hit-count contracts failed for " + failed.length + " mock(s)." : undefined,
|
|
10678
10730
|
});
|
|
10679
10731
|
}
|
|
10680
10732
|
if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
resolveRiddleProofProfileTimeoutSec,
|
|
59
59
|
slugifyRiddleProofProfileName,
|
|
60
60
|
summarizeRiddleProofProfileResult
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-376FPGFA.js";
|
|
62
62
|
import {
|
|
63
63
|
DEFAULT_RIDDLE_API_BASE_URL,
|
|
64
64
|
DEFAULT_RIDDLE_API_KEY_FILE,
|
package/dist/profile.cjs
CHANGED
|
@@ -513,6 +513,22 @@ function normalizeNetworkMock(input, index) {
|
|
|
513
513
|
if (requiredHitCount !== void 0 && (!Number.isInteger(requiredHitCount) || requiredHitCount < 1)) {
|
|
514
514
|
throw new Error(`target.network_mocks[${index}].required_hit_count must be a positive integer.`);
|
|
515
515
|
}
|
|
516
|
+
const forbidden = input.forbidden === true || input.must_not_hit === true || input.mustNotHit === true || input.should_not_run === true || input.shouldNotRun === true;
|
|
517
|
+
const configuredMaxHitCount = numberValue(
|
|
518
|
+
input.max_hit_count ?? input.maxHitCount ?? input.max_hits ?? input.maxHits
|
|
519
|
+
);
|
|
520
|
+
if (configuredMaxHitCount !== void 0 && (!Number.isInteger(configuredMaxHitCount) || configuredMaxHitCount < 0)) {
|
|
521
|
+
throw new Error(`target.network_mocks[${index}].max_hit_count must be a non-negative integer.`);
|
|
522
|
+
}
|
|
523
|
+
if (forbidden && configuredMaxHitCount !== void 0 && configuredMaxHitCount !== 0) {
|
|
524
|
+
throw new Error(`target.network_mocks[${index}].forbidden cannot be combined with max_hit_count greater than 0.`);
|
|
525
|
+
}
|
|
526
|
+
const maxHitCount = forbidden ? 0 : configuredMaxHitCount;
|
|
527
|
+
const required = input.required === false || maxHitCount === 0 && input.required !== true ? false : true;
|
|
528
|
+
const effectiveRequiredHitCount = required ? requiredHitCount ?? (Array.isArray(responses) && responses.length ? responses.length : 1) : 0;
|
|
529
|
+
if (maxHitCount !== void 0 && effectiveRequiredHitCount > maxHitCount) {
|
|
530
|
+
throw new Error(`target.network_mocks[${index}].max_hit_count cannot be less than its required hit count.`);
|
|
531
|
+
}
|
|
516
532
|
return {
|
|
517
533
|
...payload,
|
|
518
534
|
label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
|
|
@@ -521,7 +537,9 @@ function normalizeNetworkMock(input, index) {
|
|
|
521
537
|
responses,
|
|
522
538
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
523
539
|
required_hit_count: requiredHitCount,
|
|
524
|
-
|
|
540
|
+
max_hit_count: maxHitCount,
|
|
541
|
+
forbidden,
|
|
542
|
+
required,
|
|
525
543
|
capture_request_body: requestBody.capture_request_body,
|
|
526
544
|
request_body_contains: requestBody.request_body_contains,
|
|
527
545
|
request_body_patterns: requestBody.request_body_patterns,
|
|
@@ -1431,8 +1449,9 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
1431
1449
|
const events = evidence.network_mocks || [];
|
|
1432
1450
|
const failed = [];
|
|
1433
1451
|
const requiredMocks = mocks.filter((mock) => mock.required !== false);
|
|
1452
|
+
const hitCountForMock = (mock) => events.filter((event) => event.label === mock.label && event.ok !== false).length;
|
|
1434
1453
|
for (const mock of requiredMocks) {
|
|
1435
|
-
const hits =
|
|
1454
|
+
const hits = hitCountForMock(mock);
|
|
1436
1455
|
const requiredHitCount = requiredNetworkMockHitCount(mock);
|
|
1437
1456
|
if (hits < requiredHitCount) {
|
|
1438
1457
|
failed.push({
|
|
@@ -1445,6 +1464,20 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
1445
1464
|
});
|
|
1446
1465
|
}
|
|
1447
1466
|
}
|
|
1467
|
+
for (const mock of mocks) {
|
|
1468
|
+
if (mock.max_hit_count === void 0) continue;
|
|
1469
|
+
const hits = hitCountForMock(mock);
|
|
1470
|
+
if (hits > mock.max_hit_count) {
|
|
1471
|
+
failed.push({
|
|
1472
|
+
label: mock.label,
|
|
1473
|
+
url: mock.url,
|
|
1474
|
+
method: mock.method || null,
|
|
1475
|
+
reason: mock.max_hit_count === 0 ? "forbidden_mock_hit" : "mock_hit_count_exceeded",
|
|
1476
|
+
max_hit_count: mock.max_hit_count,
|
|
1477
|
+
hit_count: hits
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1448
1481
|
for (const event of events) {
|
|
1449
1482
|
if (event.ok === false) {
|
|
1450
1483
|
failed.push({
|
|
@@ -1479,15 +1512,16 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
1479
1512
|
hit_count: events.filter((event) => event.ok !== false).length,
|
|
1480
1513
|
hits_by_label: Object.fromEntries(mocks.map((mock) => [
|
|
1481
1514
|
mock.label,
|
|
1482
|
-
|
|
1515
|
+
hitCountForMock(mock)
|
|
1483
1516
|
])),
|
|
1484
1517
|
required_hits_by_label: Object.fromEntries(requiredMocks.map((mock) => [
|
|
1485
1518
|
mock.label,
|
|
1486
1519
|
requiredNetworkMockHitCount(mock)
|
|
1487
1520
|
])),
|
|
1521
|
+
max_hits_by_label: Object.fromEntries(mocks.filter((mock) => mock.max_hit_count !== void 0).map((mock) => [mock.label, mock.max_hit_count ?? null])),
|
|
1488
1522
|
failed
|
|
1489
1523
|
},
|
|
1490
|
-
message: failed.length ? `Network mocks failed or
|
|
1524
|
+
message: failed.length ? `Network mocks failed or hit-count contracts failed for ${failed.length} mock(s).` : void 0
|
|
1491
1525
|
};
|
|
1492
1526
|
}
|
|
1493
1527
|
function requiredNetworkMockHitCount(mock) {
|
|
@@ -1947,8 +1981,9 @@ function assessProfile(profile, evidence) {
|
|
|
1947
1981
|
const events = evidence.network_mocks || [];
|
|
1948
1982
|
const requiredMocks = profile.target.network_mocks.filter((mock) => mock && mock.required !== false);
|
|
1949
1983
|
const failed = [];
|
|
1984
|
+
const hitCountForMock = (mock) => events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
1950
1985
|
for (const mock of requiredMocks) {
|
|
1951
|
-
const hits =
|
|
1986
|
+
const hits = hitCountForMock(mock);
|
|
1952
1987
|
const requiredHitCount = requiredNetworkMockHitCount(mock);
|
|
1953
1988
|
if (hits < requiredHitCount) {
|
|
1954
1989
|
failed.push({
|
|
@@ -1961,6 +1996,20 @@ function assessProfile(profile, evidence) {
|
|
|
1961
1996
|
});
|
|
1962
1997
|
}
|
|
1963
1998
|
}
|
|
1999
|
+
for (const mock of profile.target.network_mocks) {
|
|
2000
|
+
if (!mock || mock.max_hit_count === undefined) continue;
|
|
2001
|
+
const hits = hitCountForMock(mock);
|
|
2002
|
+
if (hits > mock.max_hit_count) {
|
|
2003
|
+
failed.push({
|
|
2004
|
+
label: mock.label,
|
|
2005
|
+
url: mock.url,
|
|
2006
|
+
method: mock.method || null,
|
|
2007
|
+
reason: mock.max_hit_count === 0 ? "forbidden_mock_hit" : "mock_hit_count_exceeded",
|
|
2008
|
+
max_hit_count: mock.max_hit_count,
|
|
2009
|
+
hit_count: hits,
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
1964
2013
|
for (const event of events) {
|
|
1965
2014
|
if (event && event.ok === false) {
|
|
1966
2015
|
failed.push({
|
|
@@ -1987,9 +2036,11 @@ function assessProfile(profile, evidence) {
|
|
|
1987
2036
|
}
|
|
1988
2037
|
const hitsByLabel = {};
|
|
1989
2038
|
const requiredHitsByLabel = {};
|
|
2039
|
+
const maxHitsByLabel = {};
|
|
1990
2040
|
for (const mock of profile.target.network_mocks) {
|
|
1991
|
-
hitsByLabel[mock.label] =
|
|
2041
|
+
hitsByLabel[mock.label] = hitCountForMock(mock);
|
|
1992
2042
|
if (mock && mock.required !== false) requiredHitsByLabel[mock.label] = requiredNetworkMockHitCount(mock);
|
|
2043
|
+
if (mock && mock.max_hit_count !== undefined) maxHitsByLabel[mock.label] = mock.max_hit_count;
|
|
1993
2044
|
}
|
|
1994
2045
|
checks.push({
|
|
1995
2046
|
type: "network_mocks_succeeded",
|
|
@@ -2001,9 +2052,10 @@ function assessProfile(profile, evidence) {
|
|
|
2001
2052
|
hit_count: events.filter((event) => event && event.ok !== false).length,
|
|
2002
2053
|
hits_by_label: hitsByLabel,
|
|
2003
2054
|
required_hits_by_label: requiredHitsByLabel,
|
|
2055
|
+
max_hits_by_label: maxHitsByLabel,
|
|
2004
2056
|
failed,
|
|
2005
2057
|
},
|
|
2006
|
-
message: failed.length ? "Network mocks failed or
|
|
2058
|
+
message: failed.length ? "Network mocks failed or hit-count contracts failed for " + failed.length + " mock(s)." : undefined,
|
|
2007
2059
|
});
|
|
2008
2060
|
}
|
|
2009
2061
|
if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
|
package/dist/profile.d.cts
CHANGED
|
@@ -74,6 +74,8 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
|
|
|
74
74
|
responses?: RiddleProofProfileNetworkMockResponse[];
|
|
75
75
|
repeat_responses?: boolean;
|
|
76
76
|
required_hit_count?: number;
|
|
77
|
+
max_hit_count?: number;
|
|
78
|
+
forbidden?: boolean;
|
|
77
79
|
required?: boolean;
|
|
78
80
|
capture_request_body?: boolean;
|
|
79
81
|
request_body_contains?: string[];
|
package/dist/profile.d.ts
CHANGED
|
@@ -74,6 +74,8 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
|
|
|
74
74
|
responses?: RiddleProofProfileNetworkMockResponse[];
|
|
75
75
|
repeat_responses?: boolean;
|
|
76
76
|
required_hit_count?: number;
|
|
77
|
+
max_hit_count?: number;
|
|
78
|
+
forbidden?: boolean;
|
|
77
79
|
required?: boolean;
|
|
78
80
|
capture_request_body?: boolean;
|
|
79
81
|
request_body_contains?: string[];
|
package/dist/profile.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
resolveRiddleProofProfileTimeoutSec,
|
|
20
20
|
slugifyRiddleProofProfileName,
|
|
21
21
|
summarizeRiddleProofProfileResult
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-376FPGFA.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|