capdag 0.127.280 → 0.138.305
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/capdag.js +147 -90
- package/capdag.test.js +359 -270
- package/package.json +2 -2
package/capdag.test.js
CHANGED
|
@@ -12,7 +12,7 @@ const {
|
|
|
12
12
|
CapGraphEdge, CapGraphStats, CapGraph,
|
|
13
13
|
StdinSource, StdinSourceKind,
|
|
14
14
|
validateNoMediaSpecRedefinitionSync,
|
|
15
|
-
CapArgumentValue,
|
|
15
|
+
CapArgumentValue, CapArg, ArgSource, validateCapArgs, ValidationError,
|
|
16
16
|
llmGenerateTextUrn, modelAvailabilityUrn, modelPathUrn,
|
|
17
17
|
MachineSyntaxError, MachineSyntaxErrorCodes, MachineEdge, Machine, MachineBuilder, parseMachine, parseMachineWithAST,
|
|
18
18
|
CapRegistryEntry, MediaRegistryEntry, CapRegistryClient,
|
|
@@ -151,7 +151,7 @@ function matrixTestUrn(tags) {
|
|
|
151
151
|
// cap_urn.rs: TEST001-TEST050, TEST890-TEST891
|
|
152
152
|
// ============================================================================
|
|
153
153
|
|
|
154
|
-
// TEST001:
|
|
154
|
+
// TEST001: Test that cap URN is created with tags parsed correctly and direction specs accessible
|
|
155
155
|
function test001_capUrnCreation() {
|
|
156
156
|
const cap = CapUrn.fromString(testUrn('op=generate;ext=pdf;target=thumbnail'));
|
|
157
157
|
assertEqual(cap.getTag('op'), 'generate', 'Should get op tag');
|
|
@@ -161,21 +161,18 @@ function test001_capUrnCreation() {
|
|
|
161
161
|
assertEqual(cap.getOutSpec(), MEDIA_OBJECT, 'Should get outSpec');
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
// TEST002:
|
|
164
|
+
// TEST002: Test that missing 'in' or 'out' defaults to media: wildcard
|
|
165
165
|
function test002_directionSpecsRequired() {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
ErrorCodes.MISSING_OUT_SPEC,
|
|
174
|
-
'Missing out should fail with MISSING_OUT_SPEC'
|
|
175
|
-
);
|
|
166
|
+
const missingIn = CapUrn.fromString('cap:out="media:void";op=test');
|
|
167
|
+
assertEqual(missingIn.getInSpec(), MEDIA_IDENTITY, 'Missing in should default to media:');
|
|
168
|
+
assertEqual(missingIn.getOutSpec(), MEDIA_VOID, 'Explicit out should be preserved');
|
|
169
|
+
|
|
170
|
+
const missingOut = CapUrn.fromString('cap:in="media:void";op=test');
|
|
171
|
+
assertEqual(missingOut.getInSpec(), MEDIA_VOID, 'Explicit in should be preserved');
|
|
172
|
+
assertEqual(missingOut.getOutSpec(), MEDIA_IDENTITY, 'Missing out should default to media:');
|
|
176
173
|
}
|
|
177
174
|
|
|
178
|
-
// TEST003:
|
|
175
|
+
// TEST003: Test that direction specs must match exactly, different in/out types don't match, wildcard matches any
|
|
179
176
|
function test003_directionMatching() {
|
|
180
177
|
const cap = CapUrn.fromString(testUrn('op=generate'));
|
|
181
178
|
const request = CapUrn.fromString(testUrn('op=generate'));
|
|
@@ -190,7 +187,7 @@ function test003_directionMatching() {
|
|
|
190
187
|
assert(wildcardCap.accepts(request), 'Wildcard direction should match any');
|
|
191
188
|
}
|
|
192
189
|
|
|
193
|
-
// TEST004:
|
|
190
|
+
// TEST004: Test that unquoted keys and values are normalized to lowercase
|
|
194
191
|
function test004_unquotedValuesLowercased() {
|
|
195
192
|
const cap = CapUrn.fromString('cap:IN="media:void";OP=Generate;EXT=PDF;OUT="media:record;textable"');
|
|
196
193
|
assertEqual(cap.getTag('op'), 'generate', 'Unquoted value should be lowercased');
|
|
@@ -199,33 +196,33 @@ function test004_unquotedValuesLowercased() {
|
|
|
199
196
|
assertEqual(cap.getTag('OP'), 'generate', 'Key lookup should be case-insensitive');
|
|
200
197
|
}
|
|
201
198
|
|
|
202
|
-
// TEST005:
|
|
199
|
+
// TEST005: Test that quoted values preserve case while unquoted are lowercased
|
|
203
200
|
function test005_quotedValuesPreserveCase() {
|
|
204
201
|
const cap = CapUrn.fromString('cap:in="media:void";key="HelloWorld";out="media:void"');
|
|
205
202
|
assertEqual(cap.getTag('key'), 'HelloWorld', 'Quoted value should preserve case');
|
|
206
203
|
}
|
|
207
204
|
|
|
208
|
-
// TEST006:
|
|
205
|
+
// TEST006: Test that quoted values can contain special characters (semicolons, equals, spaces)
|
|
209
206
|
function test006_quotedValueSpecialChars() {
|
|
210
207
|
const cap = CapUrn.fromString('cap:in="media:void";key="val;ue=with spaces";out="media:void"');
|
|
211
208
|
assertEqual(cap.getTag('key'), 'val;ue=with spaces', 'Quoted value should preserve special chars');
|
|
212
209
|
}
|
|
213
210
|
|
|
214
|
-
// TEST007:
|
|
211
|
+
// TEST007: Test that escape sequences in quoted values (\" and \\) are parsed correctly
|
|
215
212
|
function test007_quotedValueEscapeSequences() {
|
|
216
213
|
const s = String.raw`cap:in="media:void";key="val\"ue\\test";out="media:void"`;
|
|
217
214
|
const cap = CapUrn.fromString(s);
|
|
218
215
|
assertEqual(cap.getTag('key'), 'val"ue\\test', 'Escaped quote and backslash should be unescaped');
|
|
219
216
|
}
|
|
220
217
|
|
|
221
|
-
// TEST008:
|
|
218
|
+
// TEST008: Test that mixed quoted and unquoted values in same URN parse correctly
|
|
222
219
|
function test008_mixedQuotedUnquoted() {
|
|
223
220
|
const cap = CapUrn.fromString('cap:a=simple;b="Quoted";in="media:void";out="media:void"');
|
|
224
221
|
assertEqual(cap.getTag('a'), 'simple', 'Unquoted value should be lowercase');
|
|
225
222
|
assertEqual(cap.getTag('b'), 'Quoted', 'Quoted value should preserve case');
|
|
226
223
|
}
|
|
227
224
|
|
|
228
|
-
// TEST009:
|
|
225
|
+
// TEST009: Test that unterminated quote produces UnterminatedQuote error
|
|
229
226
|
function test009_unterminatedQuoteError() {
|
|
230
227
|
let threw = false;
|
|
231
228
|
try {
|
|
@@ -238,7 +235,7 @@ function test009_unterminatedQuoteError() {
|
|
|
238
235
|
assert(threw, 'Unterminated quote should produce CapUrnError');
|
|
239
236
|
}
|
|
240
237
|
|
|
241
|
-
// TEST010:
|
|
238
|
+
// TEST010: Test that invalid escape sequences (like \n, \x) produce InvalidEscapeSequence error
|
|
242
239
|
function test010_invalidEscapeSequenceError() {
|
|
243
240
|
let threw = false;
|
|
244
241
|
try {
|
|
@@ -252,7 +249,7 @@ function test010_invalidEscapeSequenceError() {
|
|
|
252
249
|
assert(threw, 'Invalid escape sequence should produce CapUrnError');
|
|
253
250
|
}
|
|
254
251
|
|
|
255
|
-
// TEST011:
|
|
252
|
+
// TEST011: Test that serialization uses smart quoting (no quotes for simple lowercase, quotes for special chars/uppercase)
|
|
256
253
|
function test011_serializationSmartQuoting() {
|
|
257
254
|
const cap = CapUrn.fromString('cap:a=simple;b="Has Space";in="media:void";out="media:void"');
|
|
258
255
|
const s = cap.toString();
|
|
@@ -261,7 +258,7 @@ function test011_serializationSmartQuoting() {
|
|
|
261
258
|
assert(s.includes('b="Has Space"'), 'Value with space should be quoted');
|
|
262
259
|
}
|
|
263
260
|
|
|
264
|
-
// TEST012:
|
|
261
|
+
// TEST012: Test that simple cap URN round-trips (parse -> serialize -> parse equals original)
|
|
265
262
|
function test012_roundTripSimple() {
|
|
266
263
|
const original = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
267
264
|
const serialized = original.toString();
|
|
@@ -269,7 +266,7 @@ function test012_roundTripSimple() {
|
|
|
269
266
|
assert(original.equals(reparsed), 'Simple round-trip should produce equal URN');
|
|
270
267
|
}
|
|
271
268
|
|
|
272
|
-
// TEST013:
|
|
269
|
+
// TEST013: Test that quoted values round-trip preserving case and spaces
|
|
273
270
|
function test013_roundTripQuoted() {
|
|
274
271
|
const original = CapUrn.fromString('cap:in="media:void";key="HelloWorld";out="media:void"');
|
|
275
272
|
const serialized = original.toString();
|
|
@@ -278,7 +275,7 @@ function test013_roundTripQuoted() {
|
|
|
278
275
|
assertEqual(reparsed.getTag('key'), 'HelloWorld', 'Quoted value should survive round-trip');
|
|
279
276
|
}
|
|
280
277
|
|
|
281
|
-
// TEST014:
|
|
278
|
+
// TEST014: Test that escape sequences round-trip correctly
|
|
282
279
|
function test014_roundTripEscapes() {
|
|
283
280
|
const s = String.raw`cap:in="media:void";key="val\"ue\\test";out="media:void"`;
|
|
284
281
|
const original = CapUrn.fromString(s);
|
|
@@ -288,7 +285,7 @@ function test014_roundTripEscapes() {
|
|
|
288
285
|
assertEqual(reparsed.getTag('key'), 'val"ue\\test', 'Escaped value should survive round-trip');
|
|
289
286
|
}
|
|
290
287
|
|
|
291
|
-
// TEST015: cap: prefix required
|
|
288
|
+
// TEST015: Test that cap: prefix is required and case-insensitive
|
|
292
289
|
function test015_capPrefixRequired() {
|
|
293
290
|
assertThrows(
|
|
294
291
|
() => CapUrn.fromString('in="media:void";out="media:void";op=generate'),
|
|
@@ -300,7 +297,7 @@ function test015_capPrefixRequired() {
|
|
|
300
297
|
assertEqual(cap.getTag('op'), 'generate', 'Should parse with valid cap: prefix');
|
|
301
298
|
}
|
|
302
299
|
|
|
303
|
-
// TEST016:
|
|
300
|
+
// TEST016: Test that trailing semicolon is equivalent (same hash, same string, matches)
|
|
304
301
|
function test016_trailingSemicolonEquivalence() {
|
|
305
302
|
const cap1 = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
306
303
|
const cap2 = CapUrn.fromString(testUrn('op=generate;ext=pdf') + ';');
|
|
@@ -308,49 +305,51 @@ function test016_trailingSemicolonEquivalence() {
|
|
|
308
305
|
assertEqual(cap1.toString(), cap2.toString(), 'Canonical forms should match');
|
|
309
306
|
}
|
|
310
307
|
|
|
311
|
-
// TEST017:
|
|
308
|
+
// TEST017: Test tag matching: exact match, subset match, wildcard match, value mismatch
|
|
312
309
|
function test017_tagMatching() {
|
|
313
310
|
const cap = CapUrn.fromString(testUrn('op=generate;ext=pdf;target=thumbnail'));
|
|
314
311
|
|
|
315
|
-
// Exact match
|
|
312
|
+
// Exact match — both directions accept
|
|
316
313
|
const exact = CapUrn.fromString(testUrn('op=generate;ext=pdf;target=thumbnail'));
|
|
317
314
|
assert(cap.accepts(exact), 'Should accept exact match');
|
|
315
|
+
assert(exact.accepts(cap), 'Exact match should accept in reverse too');
|
|
318
316
|
|
|
319
|
-
//
|
|
317
|
+
// Routing direction: request(op=generate) accepts cap(op,ext,target)
|
|
320
318
|
const subset = CapUrn.fromString(testUrn('op=generate'));
|
|
321
|
-
assert(
|
|
319
|
+
assert(subset.accepts(cap), 'General request should accept more specific instance');
|
|
320
|
+
assert(!cap.accepts(subset), 'Specific pattern should reject subset instance');
|
|
322
321
|
|
|
323
|
-
//
|
|
322
|
+
// Routing direction: request(ext=*) accepts cap(ext=pdf)
|
|
324
323
|
const wildcard = CapUrn.fromString(testUrn('ext=*'));
|
|
325
|
-
assert(
|
|
324
|
+
assert(wildcard.accepts(cap), 'Wildcard request should accept specific instance');
|
|
326
325
|
|
|
327
|
-
//
|
|
326
|
+
// Conflicting value — neither direction accepts
|
|
328
327
|
const mismatch = CapUrn.fromString(testUrn('op=extract'));
|
|
329
328
|
assert(!cap.accepts(mismatch), 'Should not accept value mismatch');
|
|
329
|
+
assert(!mismatch.accepts(cap), 'Reverse mismatch should also reject');
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
// TEST018:
|
|
332
|
+
// TEST018: Test that quoted values with different case do NOT match (case-sensitive)
|
|
333
333
|
function test018_matchingCaseSensitiveValues() {
|
|
334
334
|
const cap = CapUrn.fromString('cap:in="media:void";key="HelloWorld";out="media:void"');
|
|
335
335
|
const request = CapUrn.fromString('cap:in="media:void";key=helloworld;out="media:void"');
|
|
336
336
|
assert(!cap.accepts(request), 'Quoted HelloWorld should not match unquoted helloworld');
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
// TEST019: Missing tags
|
|
339
|
+
// TEST019: Missing tag in instance causes rejection — pattern's tags are constraints
|
|
340
340
|
function test019_missingTagHandling() {
|
|
341
341
|
const cap = CapUrn.fromString(testUrn('op=generate'));
|
|
342
|
-
|
|
343
|
-
// Request with tag cap doesn't have -> cap's missing tag is implicit wildcard
|
|
344
342
|
const request = CapUrn.fromString(testUrn('ext=pdf'));
|
|
345
|
-
assert(cap.accepts(request), '
|
|
343
|
+
assert(!cap.accepts(request), 'Pattern requiring op should reject instance missing op');
|
|
344
|
+
assert(!request.accepts(cap), 'Pattern requiring ext should reject instance missing ext');
|
|
346
345
|
|
|
347
|
-
// Cap with extra tags can accept subset requests
|
|
348
346
|
const cap2 = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
349
347
|
const request2 = CapUrn.fromString(testUrn('op=generate'));
|
|
350
|
-
assert(cap2.accepts(request2), '
|
|
348
|
+
assert(!cap2.accepts(request2), 'Specific pattern should reject instance missing ext');
|
|
349
|
+
assert(request2.accepts(cap2), 'General request should accept more specific instance');
|
|
351
350
|
}
|
|
352
351
|
|
|
353
|
-
// TEST020:
|
|
352
|
+
// TEST020: Test specificity calculation (direction specs use MediaUrn tag count, wildcards don't count)
|
|
354
353
|
function test020_specificity() {
|
|
355
354
|
// Direction specs contribute their MediaUrn tag count:
|
|
356
355
|
// MEDIA_VOID = "media:void" -> 1 tag (void)
|
|
@@ -369,7 +368,7 @@ function test020_specificity() {
|
|
|
369
368
|
assertEqual(cap4.specificity(), 2, 'record(1) + op(1) (in wildcard doesn\'t count)');
|
|
370
369
|
}
|
|
371
370
|
|
|
372
|
-
// TEST021:
|
|
371
|
+
// TEST021: Test builder creates cap URN with correct tags and direction specs
|
|
373
372
|
function test021_builder() {
|
|
374
373
|
const cap = new CapUrnBuilder()
|
|
375
374
|
.inSpec('media:void')
|
|
@@ -383,7 +382,7 @@ function test021_builder() {
|
|
|
383
382
|
assertEqual(cap.getOutSpec(), 'media:object', 'Builder should set outSpec');
|
|
384
383
|
}
|
|
385
384
|
|
|
386
|
-
// TEST022:
|
|
385
|
+
// TEST022: Test builder requires both in_spec and out_spec
|
|
387
386
|
function test022_builderRequiresDirection() {
|
|
388
387
|
assertThrows(
|
|
389
388
|
() => new CapUrnBuilder().tag('op', 'test').build(),
|
|
@@ -397,7 +396,7 @@ function test022_builderRequiresDirection() {
|
|
|
397
396
|
);
|
|
398
397
|
}
|
|
399
398
|
|
|
400
|
-
// TEST023:
|
|
399
|
+
// TEST023: Test builder lowercases keys but preserves value case
|
|
401
400
|
function test023_builderPreservesCase() {
|
|
402
401
|
const cap = new CapUrnBuilder()
|
|
403
402
|
.inSpec('media:void')
|
|
@@ -408,27 +407,27 @@ function test023_builderPreservesCase() {
|
|
|
408
407
|
assertEqual(cap.getTag('MyKey'), 'MyValue', 'getTag should be case-insensitive for keys');
|
|
409
408
|
}
|
|
410
409
|
|
|
411
|
-
// TEST024: Directional accepts
|
|
410
|
+
// TEST024: Directional accepts — pattern's tags are constraints, instance must satisfy
|
|
412
411
|
function test024_compatibility() {
|
|
413
|
-
// General cap accepts specific request (missing tags = wildcards)
|
|
414
|
-
const general = CapUrn.fromString(testUrn('op=generate'));
|
|
415
|
-
const specific = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
416
|
-
assert(general.accepts(specific), 'General cap should accept specific request');
|
|
417
|
-
// Specific cap also accepts general request (cap has extra tag, not blocking)
|
|
418
|
-
assert(specific.accepts(general), 'Specific cap accepts general request (extra tags ok)');
|
|
419
|
-
|
|
420
|
-
// Different op values: neither accepts the other
|
|
421
412
|
const cap1 = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
413
|
+
const cap2 = CapUrn.fromString(testUrn('op=generate;format=*'));
|
|
422
414
|
const cap3 = CapUrn.fromString(testUrn('type=image;op=extract'));
|
|
415
|
+
|
|
416
|
+
assert(!cap1.accepts(cap2), 'Pattern requiring ext should reject instance missing ext');
|
|
417
|
+
assert(!cap2.accepts(cap1), 'Pattern requiring format should reject instance missing format');
|
|
423
418
|
assert(!cap1.accepts(cap3), 'Different op should not accept');
|
|
424
|
-
assert(!cap3.accepts(cap1), 'Different op should not accept
|
|
419
|
+
assert(!cap3.accepts(cap1), 'Different op should not accept in reverse');
|
|
425
420
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
assert(!cap1.accepts(
|
|
421
|
+
const general = CapUrn.fromString(testUrn('op=generate'));
|
|
422
|
+
assert(general.accepts(cap1), 'General request should accept more specific instance');
|
|
423
|
+
assert(!cap1.accepts(general), 'Specific pattern should reject general instance');
|
|
424
|
+
|
|
425
|
+
const broadIn = CapUrn.fromString('cap:in="media:";op=generate;out="media:record;textable"');
|
|
426
|
+
assert(!cap1.accepts(broadIn), 'Specific input should not accept broad wildcard input');
|
|
427
|
+
assert(broadIn.accepts(cap1), 'Wildcard input should accept specific input');
|
|
429
428
|
}
|
|
430
429
|
|
|
431
|
-
// TEST025:
|
|
430
|
+
// TEST025: Test find_best_match returns most specific matching cap
|
|
432
431
|
function test025_bestMatch() {
|
|
433
432
|
const caps = [
|
|
434
433
|
CapUrn.fromString('cap:in=*;out=*;op=*'),
|
|
@@ -441,7 +440,7 @@ function test025_bestMatch() {
|
|
|
441
440
|
assertEqual(best.getTag('ext'), 'pdf', 'Best match should be the most specific (ext=pdf)');
|
|
442
441
|
}
|
|
443
442
|
|
|
444
|
-
// TEST026: merge combines tags, subset keeps only specified
|
|
443
|
+
// TEST026: Test merge combines tags from both caps, subset keeps only specified tags
|
|
445
444
|
function test026_mergeAndSubset() {
|
|
446
445
|
const cap1 = CapUrn.fromString(testUrn('op=generate'));
|
|
447
446
|
const cap2 = CapUrn.fromString('cap:in="media:textable";ext=pdf;format=binary;out="media:"');
|
|
@@ -460,7 +459,7 @@ function test026_mergeAndSubset() {
|
|
|
460
459
|
assertEqual(sub.getInSpec(), 'media:textable', 'Subset should preserve inSpec');
|
|
461
460
|
}
|
|
462
461
|
|
|
463
|
-
// TEST027:
|
|
462
|
+
// TEST027: Test with_wildcard_tag sets tag to wildcard, including in/out
|
|
464
463
|
function test027_wildcardTag() {
|
|
465
464
|
const cap = CapUrn.fromString(testUrn('ext=pdf'));
|
|
466
465
|
const wildcardExt = cap.withWildcardTag('ext');
|
|
@@ -473,16 +472,14 @@ function test027_wildcardTag() {
|
|
|
473
472
|
assertEqual(wildcardOut.getOutSpec(), '*', 'Should set out to wildcard');
|
|
474
473
|
}
|
|
475
474
|
|
|
476
|
-
// TEST028:
|
|
475
|
+
// TEST028: Test empty cap URN defaults to media: wildcard
|
|
477
476
|
function test028_emptyCapUrnNotAllowed() {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
'Empty cap URN should fail with MISSING_IN_SPEC'
|
|
482
|
-
);
|
|
477
|
+
const empty = CapUrn.fromString('cap:');
|
|
478
|
+
assertEqual(empty.getInSpec(), MEDIA_IDENTITY, 'Empty cap should default in to media:');
|
|
479
|
+
assertEqual(empty.getOutSpec(), MEDIA_IDENTITY, 'Empty cap should default out to media:');
|
|
483
480
|
}
|
|
484
481
|
|
|
485
|
-
// TEST029:
|
|
482
|
+
// TEST029: Test minimal valid cap URN has just in and out, empty tags
|
|
486
483
|
function test029_minimalCapUrn() {
|
|
487
484
|
const minimal = CapUrn.fromString('cap:in="media:void";out="media:void"');
|
|
488
485
|
assertEqual(Object.keys(minimal.tags).length, 0, 'Should have no other tags');
|
|
@@ -490,14 +487,14 @@ function test029_minimalCapUrn() {
|
|
|
490
487
|
assertEqual(minimal.getOutSpec(), 'media:void', 'Should have outSpec');
|
|
491
488
|
}
|
|
492
489
|
|
|
493
|
-
// TEST030:
|
|
490
|
+
// TEST030: Test extended characters (forward slashes, colons) in tag values
|
|
494
491
|
function test030_extendedCharacterSupport() {
|
|
495
492
|
const cap = CapUrn.fromString(testUrn('url=https://example_org/api;path=/some/file'));
|
|
496
493
|
assertEqual(cap.getTag('url'), 'https://example_org/api', 'Should support colons and slashes');
|
|
497
494
|
assertEqual(cap.getTag('path'), '/some/file', 'Should support forward slashes');
|
|
498
495
|
}
|
|
499
496
|
|
|
500
|
-
// TEST031:
|
|
497
|
+
// TEST031: Test wildcard rejected in keys but accepted in values
|
|
501
498
|
function test031_wildcardRestrictions() {
|
|
502
499
|
assertThrows(
|
|
503
500
|
() => CapUrn.fromString(testUrn('*=value')),
|
|
@@ -509,13 +506,13 @@ function test031_wildcardRestrictions() {
|
|
|
509
506
|
const cap = CapUrn.fromString(testUrn('key=*'));
|
|
510
507
|
assertEqual(cap.getTag('key'), '*', 'Should accept wildcard in value');
|
|
511
508
|
|
|
512
|
-
// Wildcard in in/out
|
|
509
|
+
// Wildcard in in/out normalizes to media:
|
|
513
510
|
const capWild = CapUrn.fromString('cap:in=*;out=*;key=value');
|
|
514
|
-
assertEqual(capWild.getInSpec(),
|
|
515
|
-
assertEqual(capWild.getOutSpec(),
|
|
511
|
+
assertEqual(capWild.getInSpec(), MEDIA_IDENTITY, 'Wildcard inSpec should normalize to media:');
|
|
512
|
+
assertEqual(capWild.getOutSpec(), MEDIA_IDENTITY, 'Wildcard outSpec should normalize to media:');
|
|
516
513
|
}
|
|
517
514
|
|
|
518
|
-
// TEST032:
|
|
515
|
+
// TEST032: Test duplicate keys are rejected with DuplicateKey error
|
|
519
516
|
function test032_duplicateKeyRejection() {
|
|
520
517
|
assertThrows(
|
|
521
518
|
() => CapUrn.fromString(testUrn('key=value1;key=value2')),
|
|
@@ -524,7 +521,7 @@ function test032_duplicateKeyRejection() {
|
|
|
524
521
|
);
|
|
525
522
|
}
|
|
526
523
|
|
|
527
|
-
// TEST033:
|
|
524
|
+
// TEST033: Test pure numeric keys rejected, mixed alphanumeric allowed, numeric values allowed
|
|
528
525
|
function test033_numericKeyRestriction() {
|
|
529
526
|
assertThrows(
|
|
530
527
|
() => CapUrn.fromString(testUrn('123=value')),
|
|
@@ -538,7 +535,7 @@ function test033_numericKeyRestriction() {
|
|
|
538
535
|
assertEqual(cap2.getTag('x123key'), 'value', 'Mixed alphanumeric key should be allowed');
|
|
539
536
|
}
|
|
540
537
|
|
|
541
|
-
// TEST034:
|
|
538
|
+
// TEST034: Test empty values are rejected
|
|
542
539
|
function test034_emptyValueError() {
|
|
543
540
|
let threw = false;
|
|
544
541
|
try {
|
|
@@ -551,7 +548,7 @@ function test034_emptyValueError() {
|
|
|
551
548
|
assert(threw, 'Empty value (key=) should be rejected');
|
|
552
549
|
}
|
|
553
550
|
|
|
554
|
-
// TEST035:
|
|
551
|
+
// TEST035: Test has_tag is case-sensitive for values, case-insensitive for keys, works for in/out
|
|
555
552
|
function test035_hasTagCaseSensitive() {
|
|
556
553
|
const cap = CapUrn.fromString('cap:in="media:void";key="Value";out="media:void"');
|
|
557
554
|
assert(cap.hasTag('key', 'Value'), 'hasTag should match exact value');
|
|
@@ -563,26 +560,24 @@ function test035_hasTagCaseSensitive() {
|
|
|
563
560
|
assert(cap.hasTag('out', 'media:void'), 'hasTag should work for out');
|
|
564
561
|
}
|
|
565
562
|
|
|
566
|
-
// TEST036:
|
|
563
|
+
// TEST036: Test with_tag preserves value case
|
|
567
564
|
function test036_withTagPreservesValue() {
|
|
568
565
|
const cap = CapUrn.fromString('cap:in="media:void";out="media:void"');
|
|
569
566
|
const modified = cap.withTag('key', 'MyValue');
|
|
570
567
|
assertEqual(modified.getTag('key'), 'MyValue', 'withTag should preserve value case');
|
|
571
568
|
}
|
|
572
569
|
|
|
573
|
-
// TEST037:
|
|
574
|
-
// Note: In JS, withTag does not currently reject empty values (it stores them).
|
|
575
|
-
// The Rust implementation rejects empty values. We test the JS behavior as-is.
|
|
570
|
+
// TEST037: Test with_tag rejects empty value
|
|
576
571
|
function test037_withTagRejectsEmptyValue() {
|
|
577
|
-
// The JS implementation does not throw for empty values in withTag.
|
|
578
|
-
// This test verifies the current behavior: withTag stores empty string.
|
|
579
|
-
// If the implementation is updated to reject empty values, update this test.
|
|
580
572
|
const cap = CapUrn.fromString('cap:in="media:void";out="media:void"');
|
|
581
|
-
|
|
582
|
-
|
|
573
|
+
assertThrows(
|
|
574
|
+
() => cap.withTag('key', ''),
|
|
575
|
+
ErrorCodes.EMPTY_VALUE,
|
|
576
|
+
'withTag should reject empty string values'
|
|
577
|
+
);
|
|
583
578
|
}
|
|
584
579
|
|
|
585
|
-
// TEST038:
|
|
580
|
+
// TEST038: Test semantic equivalence of unquoted and quoted simple lowercase values
|
|
586
581
|
function test038_semanticEquivalence() {
|
|
587
582
|
const c1 = CapUrn.fromString('cap:in="media:void";key=simple;out="media:void"');
|
|
588
583
|
const c2 = CapUrn.fromString('cap:in="media:void";key="simple";out="media:void"');
|
|
@@ -590,7 +585,7 @@ function test038_semanticEquivalence() {
|
|
|
590
585
|
assertEqual(c1.getTag('key'), c2.getTag('key'), 'Values should be identical');
|
|
591
586
|
}
|
|
592
587
|
|
|
593
|
-
// TEST039:
|
|
588
|
+
// TEST039: Test get_tag returns direction specs (in/out) with case-insensitive lookup
|
|
594
589
|
function test039_getTagReturnsDirectionSpecs() {
|
|
595
590
|
const cap = CapUrn.fromString(`cap:in="${MEDIA_VOID}";out="${MEDIA_OBJECT}"`);
|
|
596
591
|
assertEqual(cap.getTag('in'), MEDIA_VOID, 'getTag(in) should return inSpec');
|
|
@@ -599,77 +594,81 @@ function test039_getTagReturnsDirectionSpecs() {
|
|
|
599
594
|
assertEqual(cap.getTag('OUT'), MEDIA_OBJECT, 'getTag(OUT) should return outSpec (case-insensitive)');
|
|
600
595
|
}
|
|
601
596
|
|
|
602
|
-
// TEST040:
|
|
597
|
+
// TEST040: Matching semantics - exact match succeeds
|
|
603
598
|
function test040_matchingSemanticsExactMatch() {
|
|
604
599
|
const cap = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
605
600
|
const request = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
606
601
|
assert(cap.accepts(request), 'Exact match should accept');
|
|
607
602
|
}
|
|
608
603
|
|
|
609
|
-
// TEST041:
|
|
604
|
+
// TEST041: Matching semantics - cap missing tag matches (implicit wildcard)
|
|
610
605
|
function test041_matchingSemanticsCapMissingTag() {
|
|
611
606
|
const cap = CapUrn.fromString(testUrn('op=generate'));
|
|
612
607
|
const request = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
613
|
-
assert(cap.accepts(request), '
|
|
608
|
+
assert(cap.accepts(request), 'General pattern with only op should accept specific instance');
|
|
609
|
+
assert(!request.accepts(cap), 'Pattern requiring ext should reject instance missing ext');
|
|
614
610
|
}
|
|
615
611
|
|
|
616
|
-
// TEST042:
|
|
612
|
+
// TEST042: Pattern rejects instance missing required tags
|
|
617
613
|
function test042_matchingSemanticsCapHasExtraTag() {
|
|
618
614
|
const cap = CapUrn.fromString(testUrn('op=generate;ext=pdf;version=2'));
|
|
619
615
|
const request = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
620
|
-
assert(cap.accepts(request), '
|
|
616
|
+
assert(!cap.accepts(request), 'Pattern requiring version should reject instance missing version');
|
|
617
|
+
assert(request.accepts(cap), 'General request should accept refined instance');
|
|
621
618
|
}
|
|
622
619
|
|
|
623
|
-
// TEST043:
|
|
620
|
+
// TEST043: Matching semantics - request wildcard matches specific cap value
|
|
624
621
|
function test043_matchingSemanticsRequestHasWildcard() {
|
|
625
622
|
const cap = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
626
623
|
const request = CapUrn.fromString(testUrn('op=generate;ext=*'));
|
|
627
624
|
assert(cap.accepts(request), 'Request wildcard should match specific cap value');
|
|
628
625
|
}
|
|
629
626
|
|
|
630
|
-
// TEST044:
|
|
627
|
+
// TEST044: Matching semantics - cap wildcard matches specific request value
|
|
631
628
|
function test044_matchingSemanticsCapHasWildcard() {
|
|
632
629
|
const cap = CapUrn.fromString(testUrn('op=generate;ext=*'));
|
|
633
630
|
const request = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
634
631
|
assert(cap.accepts(request), 'Cap wildcard should match specific request value');
|
|
635
632
|
}
|
|
636
633
|
|
|
637
|
-
// TEST045:
|
|
634
|
+
// TEST045: Matching semantics - value mismatch does not match
|
|
638
635
|
function test045_matchingSemanticsValueMismatch() {
|
|
639
636
|
const cap = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
640
637
|
const request = CapUrn.fromString(testUrn('op=generate;ext=docx'));
|
|
641
638
|
assert(!cap.accepts(request), 'Value mismatch should not accept');
|
|
642
639
|
}
|
|
643
640
|
|
|
644
|
-
// TEST046:
|
|
641
|
+
// TEST046: Matching semantics - fallback pattern (cap missing tag = implicit wildcard)
|
|
645
642
|
function test046_matchingSemanticsFallbackPattern() {
|
|
646
643
|
const cap = CapUrn.fromString('cap:in="media:binary";op=generate_thumbnail;out="media:binary"');
|
|
647
644
|
const request = CapUrn.fromString('cap:ext=wav;in="media:binary";op=generate_thumbnail;out="media:binary"');
|
|
648
|
-
assert(cap.accepts(request), '
|
|
645
|
+
assert(cap.accepts(request), 'General pattern without ext should accept specific instance');
|
|
646
|
+
assert(!request.accepts(cap), 'Pattern requiring ext should reject instance missing ext');
|
|
649
647
|
}
|
|
650
648
|
|
|
651
|
-
// TEST047:
|
|
649
|
+
// TEST047: Matching semantics - thumbnail fallback with void input
|
|
652
650
|
function test047_matchingSemanticsThumbnailVoidInput() {
|
|
653
651
|
const cap = CapUrn.fromString('cap:in="media:void";op=generate_thumbnail;out="media:image;png;thumbnail"');
|
|
654
652
|
const request = CapUrn.fromString('cap:ext=pdf;in="media:void";op=generate_thumbnail;out="media:image"');
|
|
655
653
|
assert(cap.accepts(request), 'Void input cap should accept request; cap output conforms to less-specific request output');
|
|
656
654
|
}
|
|
657
655
|
|
|
658
|
-
// TEST048:
|
|
656
|
+
// TEST048: Matching semantics - wildcard direction matches anything
|
|
659
657
|
function test048_matchingSemanticsWildcardDirection() {
|
|
660
658
|
const cap = CapUrn.fromString('cap:in=*;out=*');
|
|
661
659
|
const request = CapUrn.fromString(testUrn('op=generate;ext=pdf'));
|
|
662
660
|
assert(cap.accepts(request), 'Wildcard cap should accept any request');
|
|
663
661
|
}
|
|
664
662
|
|
|
665
|
-
// TEST049:
|
|
663
|
+
// TEST049: Non-overlapping tags — neither direction accepts
|
|
666
664
|
function test049_matchingSemanticsCrossDimension() {
|
|
667
665
|
const cap = CapUrn.fromString(testUrn('op=generate'));
|
|
668
666
|
const request = CapUrn.fromString(testUrn('ext=pdf'));
|
|
669
|
-
assert(cap.accepts(request), '
|
|
667
|
+
assert(!cap.accepts(request), 'Pattern requiring op should reject instance missing op');
|
|
668
|
+
assert(!request.accepts(cap), 'Pattern requiring ext should reject instance missing ext');
|
|
670
669
|
}
|
|
671
670
|
|
|
672
|
-
// TEST050:
|
|
671
|
+
// TEST050: Matching semantics - direction mismatch prevents matching
|
|
673
672
|
function test050_matchingSemanticsDirectionMismatch() {
|
|
674
673
|
const cap = CapUrn.fromString(
|
|
675
674
|
`cap:in="${MEDIA_STRING}";op=generate;out="${MEDIA_OBJECT}"`
|
|
@@ -758,7 +757,7 @@ function test891_directionSemanticSpecificity() {
|
|
|
758
757
|
|
|
759
758
|
// TEST053: N/A for JS (Rust-only validation infrastructure)
|
|
760
759
|
|
|
761
|
-
// TEST054:
|
|
760
|
+
// TEST054: XV5 - Test inline media spec redefinition of existing registry spec is detected and rejected
|
|
762
761
|
function test054_xv5InlineSpecRedefinitionDetected() {
|
|
763
762
|
const registryLookup = (mediaUrn) => mediaUrn === MEDIA_STRING;
|
|
764
763
|
const mediaSpecs = [
|
|
@@ -775,7 +774,7 @@ function test054_xv5InlineSpecRedefinitionDetected() {
|
|
|
775
774
|
assert(result.redefines && result.redefines.includes(MEDIA_STRING), 'Should identify MEDIA_STRING as redefined');
|
|
776
775
|
}
|
|
777
776
|
|
|
778
|
-
// TEST055:
|
|
777
|
+
// TEST055: XV5 - Test new inline media spec (not in registry) is allowed
|
|
779
778
|
function test055_xv5NewInlineSpecAllowed() {
|
|
780
779
|
const registryLookup = (mediaUrn) => mediaUrn === MEDIA_STRING;
|
|
781
780
|
const mediaSpecs = [
|
|
@@ -790,7 +789,7 @@ function test055_xv5NewInlineSpecAllowed() {
|
|
|
790
789
|
assert(result.valid, 'New spec not in registry should pass validation');
|
|
791
790
|
}
|
|
792
791
|
|
|
793
|
-
// TEST056:
|
|
792
|
+
// TEST056: XV5 - Test empty media_specs (no inline specs) passes XV5 validation
|
|
794
793
|
function test056_xv5EmptyMediaSpecsAllowed() {
|
|
795
794
|
const registryLookup = (mediaUrn) => mediaUrn === MEDIA_STRING;
|
|
796
795
|
assert(validateNoMediaSpecRedefinitionSync({}, registryLookup).valid, 'Empty object should pass');
|
|
@@ -802,7 +801,7 @@ function test056_xv5EmptyMediaSpecsAllowed() {
|
|
|
802
801
|
// media_urn.rs: TEST060-TEST078
|
|
803
802
|
// ============================================================================
|
|
804
803
|
|
|
805
|
-
// TEST060:
|
|
804
|
+
// TEST060: Test wrong prefix fails with InvalidPrefix error showing expected and actual prefix
|
|
806
805
|
function test060_wrongPrefixFails() {
|
|
807
806
|
assertThrowsMediaUrn(
|
|
808
807
|
() => MediaUrn.fromString('cap:string'),
|
|
@@ -811,7 +810,7 @@ function test060_wrongPrefixFails() {
|
|
|
811
810
|
);
|
|
812
811
|
}
|
|
813
812
|
|
|
814
|
-
// TEST061:
|
|
813
|
+
// TEST061: Test is_binary returns true when textable tag is absent (binary = not textable)
|
|
815
814
|
function test061_isBinary() {
|
|
816
815
|
// Binary types: no textable tag
|
|
817
816
|
assert(MediaUrn.fromString(MEDIA_IDENTITY).isBinary(), 'MEDIA_IDENTITY (media:) should be binary');
|
|
@@ -827,8 +826,7 @@ function test061_isBinary() {
|
|
|
827
826
|
assert(!MediaUrn.fromString(MEDIA_MD).isBinary(), 'MEDIA_MD should not be binary');
|
|
828
827
|
}
|
|
829
828
|
|
|
830
|
-
// TEST062:
|
|
831
|
-
// TEST062: is_record returns true if record marker tag is present (key-value structure)
|
|
829
|
+
// TEST062: Test is_record returns true when record marker tag is present indicating key-value structure
|
|
832
830
|
function test062_isRecord() {
|
|
833
831
|
assert(MediaUrn.fromString(MEDIA_OBJECT).isRecord(), 'MEDIA_OBJECT should be record');
|
|
834
832
|
assert(MediaUrn.fromString('media:custom;record').isRecord(), 'custom;record should be record');
|
|
@@ -839,7 +837,7 @@ function test062_isRecord() {
|
|
|
839
837
|
assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isRecord(), 'MEDIA_STRING_LIST should not be record');
|
|
840
838
|
}
|
|
841
839
|
|
|
842
|
-
// TEST063: is_scalar returns true
|
|
840
|
+
// TEST063: Test is_scalar returns true when list marker tag is absent (scalar is default)
|
|
843
841
|
function test063_isScalar() {
|
|
844
842
|
assert(MediaUrn.fromString(MEDIA_STRING).isScalar(), 'MEDIA_STRING should be scalar');
|
|
845
843
|
assert(MediaUrn.fromString(MEDIA_INTEGER).isScalar(), 'MEDIA_INTEGER should be scalar');
|
|
@@ -852,8 +850,7 @@ function test063_isScalar() {
|
|
|
852
850
|
assert(!MediaUrn.fromString(MEDIA_OBJECT_LIST).isScalar(), 'MEDIA_OBJECT_LIST should not be scalar');
|
|
853
851
|
}
|
|
854
852
|
|
|
855
|
-
// TEST064:
|
|
856
|
-
// false for MEDIA_STRING, MEDIA_OBJECT
|
|
853
|
+
// TEST064: Test is_list returns true when list marker tag is present indicating ordered collection
|
|
857
854
|
function test064_isList() {
|
|
858
855
|
assert(MediaUrn.fromString(MEDIA_STRING_LIST).isList(), 'MEDIA_STRING_LIST should be list');
|
|
859
856
|
assert(MediaUrn.fromString(MEDIA_INTEGER_LIST).isList(), 'MEDIA_INTEGER_LIST should be list');
|
|
@@ -862,7 +859,7 @@ function test064_isList() {
|
|
|
862
859
|
assert(!MediaUrn.fromString(MEDIA_OBJECT).isList(), 'MEDIA_OBJECT should not be list');
|
|
863
860
|
}
|
|
864
861
|
|
|
865
|
-
// TEST065: is_opaque returns true
|
|
862
|
+
// TEST065: Test is_opaque returns true when record marker is absent (opaque is default)
|
|
866
863
|
function test065_isOpaque() {
|
|
867
864
|
assert(MediaUrn.fromString(MEDIA_STRING).isOpaque(), 'MEDIA_STRING should be opaque');
|
|
868
865
|
assert(MediaUrn.fromString(MEDIA_STRING_LIST).isOpaque(), 'MEDIA_STRING_LIST (list but no record) should be opaque');
|
|
@@ -873,13 +870,13 @@ function test065_isOpaque() {
|
|
|
873
870
|
assert(!MediaUrn.fromString(MEDIA_JSON).isOpaque(), 'MEDIA_JSON should not be opaque');
|
|
874
871
|
}
|
|
875
872
|
|
|
876
|
-
// TEST066:
|
|
873
|
+
// TEST066: Test is_json returns true only when json marker tag is present for JSON representation
|
|
877
874
|
function test066_isJson() {
|
|
878
875
|
assert(MediaUrn.fromString(MEDIA_JSON).isJson(), 'MEDIA_JSON should be json');
|
|
879
876
|
assert(!MediaUrn.fromString(MEDIA_OBJECT).isJson(), 'MEDIA_OBJECT should not be json');
|
|
880
877
|
}
|
|
881
878
|
|
|
882
|
-
// TEST067: is_text returns true only
|
|
879
|
+
// TEST067: Test is_text returns true only when textable marker tag is present
|
|
883
880
|
function test067_isText() {
|
|
884
881
|
assert(MediaUrn.fromString(MEDIA_STRING).isText(), 'MEDIA_STRING should be text');
|
|
885
882
|
assert(MediaUrn.fromString(MEDIA_INTEGER).isText(), 'MEDIA_INTEGER should be text');
|
|
@@ -890,7 +887,7 @@ function test067_isText() {
|
|
|
890
887
|
assert(!MediaUrn.fromString(MEDIA_OBJECT).isText(), 'MEDIA_OBJECT (no textable) should not be text');
|
|
891
888
|
}
|
|
892
889
|
|
|
893
|
-
// TEST068:
|
|
890
|
+
// TEST068: Test is_void returns true when void flag or type=void tag is present
|
|
894
891
|
function test068_isVoid() {
|
|
895
892
|
assert(MediaUrn.fromString('media:void').isVoid(), 'media:void should be void');
|
|
896
893
|
assert(!MediaUrn.fromString(MEDIA_STRING).isVoid(), 'MEDIA_STRING should not be void');
|
|
@@ -898,7 +895,7 @@ function test068_isVoid() {
|
|
|
898
895
|
|
|
899
896
|
// TEST069-TEST070: N/A for JS (Rust-only binary_media_urn_for_ext/text_media_urn_for_ext)
|
|
900
897
|
|
|
901
|
-
// TEST071:
|
|
898
|
+
// TEST071: Test to_string roundtrip ensures serialization and deserialization preserve URN structure
|
|
902
899
|
function test071_toStringRoundtrip() {
|
|
903
900
|
const constants = [MEDIA_STRING, MEDIA_INTEGER, MEDIA_OBJECT, MEDIA_IDENTITY, MEDIA_PDF, MEDIA_JSON];
|
|
904
901
|
for (const constant of constants) {
|
|
@@ -908,7 +905,7 @@ function test071_toStringRoundtrip() {
|
|
|
908
905
|
}
|
|
909
906
|
}
|
|
910
907
|
|
|
911
|
-
// TEST072:
|
|
908
|
+
// TEST072: Test all media URN constants parse successfully as valid media URNs
|
|
912
909
|
function test072_constantsParse() {
|
|
913
910
|
const constants = [
|
|
914
911
|
MEDIA_STRING, MEDIA_INTEGER, MEDIA_NUMBER, MEDIA_BOOLEAN,
|
|
@@ -928,7 +925,7 @@ function test072_constantsParse() {
|
|
|
928
925
|
|
|
929
926
|
// TEST073: N/A for JS (Rust has binary_media_urn_for_ext/text_media_urn_for_ext)
|
|
930
927
|
|
|
931
|
-
// TEST074:
|
|
928
|
+
// TEST074: Test media URN conforms_to using tagged URN semantics with specific and generic requirements
|
|
932
929
|
function test074_mediaUrnMatching() {
|
|
933
930
|
const pdfUrn = MediaUrn.fromString(MEDIA_PDF);
|
|
934
931
|
const pdfPattern = MediaUrn.fromString('media:pdf');
|
|
@@ -942,7 +939,7 @@ function test074_mediaUrnMatching() {
|
|
|
942
939
|
assert(pdfUrn.conformsTo(pdfUrn), 'Same URN should conform to itself');
|
|
943
940
|
}
|
|
944
941
|
|
|
945
|
-
// TEST075:
|
|
942
|
+
// TEST075: Test accepts with implicit wildcards where handlers with fewer tags can handle more requests
|
|
946
943
|
function test075_accepts() {
|
|
947
944
|
const handler = MediaUrn.fromString(MEDIA_PDF);
|
|
948
945
|
const sameReq = MediaUrn.fromString(MEDIA_PDF);
|
|
@@ -953,7 +950,7 @@ function test075_accepts() {
|
|
|
953
950
|
assert(generalHandler.accepts(specificReq), 'General handler should accept specific request');
|
|
954
951
|
}
|
|
955
952
|
|
|
956
|
-
// TEST076:
|
|
953
|
+
// TEST076: Test specificity increases with more tags for ranking conformance
|
|
957
954
|
function test076_specificity() {
|
|
958
955
|
const s1 = MediaUrn.fromString('media:');
|
|
959
956
|
const s2 = MediaUrn.fromString('media:pdf');
|
|
@@ -962,7 +959,7 @@ function test076_specificity() {
|
|
|
962
959
|
assert(s3.specificity() > s2.specificity(), 'image;png;thumbnail should be more specific than pdf');
|
|
963
960
|
}
|
|
964
961
|
|
|
965
|
-
// TEST077:
|
|
962
|
+
// TEST077: Test serde roundtrip serializes to JSON string and deserializes back correctly
|
|
966
963
|
function test077_serdeRoundtrip() {
|
|
967
964
|
const original = MediaUrn.fromString(MEDIA_PDF);
|
|
968
965
|
const json = JSON.stringify({ urn: original.toString() });
|
|
@@ -971,7 +968,7 @@ function test077_serdeRoundtrip() {
|
|
|
971
968
|
assert(original.equals(restored), 'JSON round-trip should preserve MediaUrn');
|
|
972
969
|
}
|
|
973
970
|
|
|
974
|
-
// TEST078:
|
|
971
|
+
// TEST078: conforms_to behavior between MEDIA_OBJECT and MEDIA_STRING
|
|
975
972
|
function test078_debugMatchingBehavior() {
|
|
976
973
|
const objUrn = MediaUrn.fromString(MEDIA_OBJECT);
|
|
977
974
|
const strUrn = MediaUrn.fromString(MEDIA_STRING);
|
|
@@ -986,7 +983,7 @@ function test078_debugMatchingBehavior() {
|
|
|
986
983
|
// TEST089: N/A for JS
|
|
987
984
|
// TEST090: N/A for JS
|
|
988
985
|
|
|
989
|
-
// TEST091:
|
|
986
|
+
// TEST091: Test resolving custom media URN from local media_specs takes precedence over registry
|
|
990
987
|
function test091_resolveCustomMediaSpec() {
|
|
991
988
|
const mediaSpecs = [
|
|
992
989
|
{ urn: 'media:custom-json', media_type: 'application/json', title: 'Custom JSON', profile_uri: 'https://example.com/schema/custom' }
|
|
@@ -996,7 +993,7 @@ function test091_resolveCustomMediaSpec() {
|
|
|
996
993
|
assertEqual(spec.profile, 'https://example.com/schema/custom', 'Should have custom profile');
|
|
997
994
|
}
|
|
998
995
|
|
|
999
|
-
// TEST092:
|
|
996
|
+
// TEST092: Test resolving custom record media spec with schema from local media_specs
|
|
1000
997
|
function test092_resolveCustomWithSchema() {
|
|
1001
998
|
const mediaSpecs = [
|
|
1002
999
|
{
|
|
@@ -1013,7 +1010,7 @@ function test092_resolveCustomWithSchema() {
|
|
|
1013
1010
|
assertEqual(spec.schema.type, 'object', 'Schema should have correct type');
|
|
1014
1011
|
}
|
|
1015
1012
|
|
|
1016
|
-
// TEST093:
|
|
1013
|
+
// TEST093: Test resolving unknown media URN fails with UnresolvableMediaUrn error
|
|
1017
1014
|
function test093_resolveUnresolvableFailsHard() {
|
|
1018
1015
|
let caught = false;
|
|
1019
1016
|
try {
|
|
@@ -1032,43 +1029,43 @@ function test093_resolveUnresolvableFailsHard() {
|
|
|
1032
1029
|
// TEST097: N/A for JS (Rust validation function)
|
|
1033
1030
|
// TEST098: N/A for JS
|
|
1034
1031
|
|
|
1035
|
-
// TEST099:
|
|
1032
|
+
// TEST099: Test ResolvedMediaSpec is_binary returns true when textable tag is absent
|
|
1036
1033
|
function test099_resolvedIsBinary() {
|
|
1037
1034
|
const spec = new MediaSpec('application/octet-stream', null, null, 'Binary', null, MEDIA_IDENTITY);
|
|
1038
1035
|
assert(spec.isBinary(), 'Resolved binary spec should be binary');
|
|
1039
1036
|
}
|
|
1040
1037
|
|
|
1041
|
-
// TEST100:
|
|
1038
|
+
// TEST100: Test ResolvedMediaSpec is_record returns true when record marker is present
|
|
1042
1039
|
function test100_resolvedIsRecord() {
|
|
1043
1040
|
const spec = new MediaSpec('application/json', null, null, 'Object', null, MEDIA_OBJECT);
|
|
1044
1041
|
assert(spec.isRecord(), 'Resolved object spec should be record');
|
|
1045
1042
|
}
|
|
1046
1043
|
|
|
1047
|
-
// TEST101:
|
|
1044
|
+
// TEST101: Test ResolvedMediaSpec is_scalar returns true when list marker is absent
|
|
1048
1045
|
function test101_resolvedIsScalar() {
|
|
1049
1046
|
const spec = new MediaSpec('text/plain', null, null, 'String', null, MEDIA_STRING);
|
|
1050
1047
|
assert(spec.isScalar(), 'Resolved string spec should be scalar');
|
|
1051
1048
|
}
|
|
1052
1049
|
|
|
1053
|
-
// TEST102:
|
|
1050
|
+
// TEST102: Test ResolvedMediaSpec is_list returns true when list marker is present
|
|
1054
1051
|
function test102_resolvedIsList() {
|
|
1055
1052
|
const spec = new MediaSpec('text/plain', null, null, 'String List', null, MEDIA_STRING_LIST);
|
|
1056
1053
|
assert(spec.isList(), 'Resolved string_list spec should be list');
|
|
1057
1054
|
}
|
|
1058
1055
|
|
|
1059
|
-
// TEST103:
|
|
1056
|
+
// TEST103: Test ResolvedMediaSpec is_json returns true when json tag is present
|
|
1060
1057
|
function test103_resolvedIsJson() {
|
|
1061
1058
|
const spec = new MediaSpec('application/json', null, null, 'JSON', null, MEDIA_JSON);
|
|
1062
1059
|
assert(spec.isJSON(), 'Resolved json spec should be JSON');
|
|
1063
1060
|
}
|
|
1064
1061
|
|
|
1065
|
-
// TEST104:
|
|
1062
|
+
// TEST104: Test ResolvedMediaSpec is_text returns true when textable tag is present
|
|
1066
1063
|
function test104_resolvedIsText() {
|
|
1067
1064
|
const spec = new MediaSpec('text/plain', null, null, 'String', null, MEDIA_STRING);
|
|
1068
1065
|
assert(spec.isText(), 'Resolved string spec should be text');
|
|
1069
1066
|
}
|
|
1070
1067
|
|
|
1071
|
-
// TEST105:
|
|
1068
|
+
// TEST105: Test metadata propagates from media spec def to resolved media spec
|
|
1072
1069
|
function test105_metadataPropagation() {
|
|
1073
1070
|
const mediaSpecs = [
|
|
1074
1071
|
{
|
|
@@ -1091,7 +1088,7 @@ function test105_metadataPropagation() {
|
|
|
1091
1088
|
assertEqual(resolved.metadata.display_index, 5, 'Should propagate display_index');
|
|
1092
1089
|
}
|
|
1093
1090
|
|
|
1094
|
-
// TEST106:
|
|
1091
|
+
// TEST106: Test metadata and validation can coexist in media spec definition
|
|
1095
1092
|
function test106_metadataWithValidation() {
|
|
1096
1093
|
const mediaSpecs = [
|
|
1097
1094
|
{
|
|
@@ -1110,7 +1107,7 @@ function test106_metadataWithValidation() {
|
|
|
1110
1107
|
assertEqual(resolved.metadata.category_key, 'inference', 'Should have category_key');
|
|
1111
1108
|
}
|
|
1112
1109
|
|
|
1113
|
-
// TEST107:
|
|
1110
|
+
// TEST107: Test extensions field propagates from media spec def to resolved
|
|
1114
1111
|
function test107_extensionsPropagation() {
|
|
1115
1112
|
const mediaSpecs = [
|
|
1116
1113
|
{
|
|
@@ -1126,7 +1123,7 @@ function test107_extensionsPropagation() {
|
|
|
1126
1123
|
assertEqual(resolved.extensions[0], 'pdf', 'Should have pdf extension');
|
|
1127
1124
|
}
|
|
1128
1125
|
|
|
1129
|
-
// TEST108:
|
|
1126
|
+
// TEST108: Test creating new cap with URN, title, and command verifies correct initialization
|
|
1130
1127
|
function test108_extensionsSerialization() {
|
|
1131
1128
|
// Test that MediaSpec can hold extensions correctly
|
|
1132
1129
|
const spec = new MediaSpec('application/pdf', null, null, 'PDF', null, 'media:pdf', null, null, ['pdf']);
|
|
@@ -1134,7 +1131,7 @@ function test108_extensionsSerialization() {
|
|
|
1134
1131
|
assertEqual(spec.extensions[0], 'pdf', 'Should have pdf extension');
|
|
1135
1132
|
}
|
|
1136
1133
|
|
|
1137
|
-
// TEST109:
|
|
1134
|
+
// TEST109: Test creating cap with metadata initializes and retrieves metadata correctly
|
|
1138
1135
|
function test109_extensionsWithMetadataAndValidation() {
|
|
1139
1136
|
const mediaSpecs = [
|
|
1140
1137
|
{
|
|
@@ -1153,7 +1150,7 @@ function test109_extensionsWithMetadataAndValidation() {
|
|
|
1153
1150
|
assertEqual(resolved.extensions[0], 'json', 'Should have json extension');
|
|
1154
1151
|
}
|
|
1155
1152
|
|
|
1156
|
-
// TEST110:
|
|
1153
|
+
// TEST110: Test cap matching with subset semantics for request fulfillment
|
|
1157
1154
|
function test110_multipleExtensions() {
|
|
1158
1155
|
const mediaSpecs = [
|
|
1159
1156
|
{
|
|
@@ -1173,7 +1170,7 @@ function test110_multipleExtensions() {
|
|
|
1173
1170
|
// cap_matrix.rs: TEST117-TEST131
|
|
1174
1171
|
// ============================================================================
|
|
1175
1172
|
|
|
1176
|
-
// TEST117:
|
|
1173
|
+
// TEST117: Test registering cap set and finding by exact and subset matching
|
|
1177
1174
|
function test117_capBlockMoreSpecificWins() {
|
|
1178
1175
|
const providerRegistry = new CapMatrix();
|
|
1179
1176
|
const cartridgeRegistry = new CapMatrix();
|
|
@@ -1203,7 +1200,7 @@ function test117_capBlockMoreSpecificWins() {
|
|
|
1203
1200
|
assertEqual(best.cap.title, 'Cartridge PDF Thumbnail Generator (specific)', 'Should get cartridge cap');
|
|
1204
1201
|
}
|
|
1205
1202
|
|
|
1206
|
-
// TEST118:
|
|
1203
|
+
// TEST118: Test selecting best cap set based on specificity ranking With is_dispatchable semantics: - Provider must satisfy ALL request constraints - General request matches specific provider (provider refines request) - Specific request does NOT match general provider (provider lacks constraints)
|
|
1207
1204
|
function test118_capBlockTieGoesToFirst() {
|
|
1208
1205
|
const registry1 = new CapMatrix();
|
|
1209
1206
|
const registry2 = new CapMatrix();
|
|
@@ -1224,7 +1221,7 @@ function test118_capBlockTieGoesToFirst() {
|
|
|
1224
1221
|
assertEqual(best.registryName, 'first', 'On tie, first registry should win');
|
|
1225
1222
|
}
|
|
1226
1223
|
|
|
1227
|
-
// TEST119:
|
|
1224
|
+
// TEST119: Test invalid URN returns InvalidUrn error
|
|
1228
1225
|
function test119_capBlockPollsAll() {
|
|
1229
1226
|
const registry1 = new CapMatrix();
|
|
1230
1227
|
const registry2 = new CapMatrix();
|
|
@@ -1251,7 +1248,7 @@ function test119_capBlockPollsAll() {
|
|
|
1251
1248
|
assertEqual(best.registryName, 'r3', 'Most specific registry should win');
|
|
1252
1249
|
}
|
|
1253
1250
|
|
|
1254
|
-
// TEST120:
|
|
1251
|
+
// TEST120: Test accepts_request checks if registry can handle a capability request
|
|
1255
1252
|
function test120_capBlockNoMatch() {
|
|
1256
1253
|
const registry = new CapMatrix();
|
|
1257
1254
|
const composite = new CapBlock();
|
|
@@ -1266,7 +1263,7 @@ function test120_capBlockNoMatch() {
|
|
|
1266
1263
|
}
|
|
1267
1264
|
}
|
|
1268
1265
|
|
|
1269
|
-
// TEST121: CapBlock
|
|
1266
|
+
// TEST121: Test CapBlock selects more specific cap over less specific regardless of registry order
|
|
1270
1267
|
function test121_capBlockFallbackScenario() {
|
|
1271
1268
|
const providerRegistry = new CapMatrix();
|
|
1272
1269
|
const cartridgeRegistry = new CapMatrix();
|
|
@@ -1298,7 +1295,7 @@ function test121_capBlockFallbackScenario() {
|
|
|
1298
1295
|
assertEqual(bestWav.registryName, 'providers', 'Provider should win for wav (fallback)');
|
|
1299
1296
|
}
|
|
1300
1297
|
|
|
1301
|
-
// TEST122: CapBlock
|
|
1298
|
+
// TEST122: Test CapBlock breaks specificity ties by first registered registry
|
|
1302
1299
|
function test122_capBlockCanMethod() {
|
|
1303
1300
|
const providerRegistry = new CapMatrix();
|
|
1304
1301
|
const providerHost = new MockCapSet('test_provider');
|
|
@@ -1315,7 +1312,7 @@ function test122_capBlockCanMethod() {
|
|
|
1315
1312
|
assert(!composite.acceptsRequest(matrixTestUrn('op=nonexistent')), 'Should not accept non-matching cap');
|
|
1316
1313
|
}
|
|
1317
1314
|
|
|
1318
|
-
// TEST123: CapBlock
|
|
1315
|
+
// TEST123: Test CapBlock polls all registries to find most specific match
|
|
1319
1316
|
function test123_capBlockRegistryManagement() {
|
|
1320
1317
|
const composite = new CapBlock();
|
|
1321
1318
|
const registry1 = new CapMatrix();
|
|
@@ -1334,7 +1331,7 @@ function test123_capBlockRegistryManagement() {
|
|
|
1334
1331
|
assertEqual(composite.getRegistry('nonexistent'), null, 'Should return null for non-existent');
|
|
1335
1332
|
}
|
|
1336
1333
|
|
|
1337
|
-
// TEST124:
|
|
1334
|
+
// TEST124: Test CapBlock returns error when no registries match the request
|
|
1338
1335
|
function test124_capGraphBasicConstruction() {
|
|
1339
1336
|
const registry = new CapMatrix();
|
|
1340
1337
|
const mockHost = { executeCap: async () => ({ textOutput: 'mock' }) };
|
|
@@ -1353,7 +1350,7 @@ function test124_capGraphBasicConstruction() {
|
|
|
1353
1350
|
assertEqual(graph.stats().edgeCount, 2, 'Expected 2 edges in stats');
|
|
1354
1351
|
}
|
|
1355
1352
|
|
|
1356
|
-
// TEST125:
|
|
1353
|
+
// TEST125: Test CapBlock prefers specific cartridge over generic provider fallback
|
|
1357
1354
|
function test125_capGraphOutgoingIncoming() {
|
|
1358
1355
|
const registry = new CapMatrix();
|
|
1359
1356
|
const mockHost = { executeCap: async () => ({ textOutput: 'mock' }) };
|
|
@@ -1371,7 +1368,7 @@ function test125_capGraphOutgoingIncoming() {
|
|
|
1371
1368
|
assertEqual(graph.getIncoming('media:object').length, 1, 'object should have 1 incoming');
|
|
1372
1369
|
}
|
|
1373
1370
|
|
|
1374
|
-
// TEST126:
|
|
1371
|
+
// TEST126: Test composite can method returns CapCaller for capability execution
|
|
1375
1372
|
function test126_capGraphCanConvert() {
|
|
1376
1373
|
const registry = new CapMatrix();
|
|
1377
1374
|
const mockHost = { executeCap: async () => ({ textOutput: 'mock' }) };
|
|
@@ -1392,7 +1389,7 @@ function test126_capGraphCanConvert() {
|
|
|
1392
1389
|
assert(!graph.canConvert('media:nonexistent', 'media:string'), 'Nonexistent node');
|
|
1393
1390
|
}
|
|
1394
1391
|
|
|
1395
|
-
// TEST127: CapGraph
|
|
1392
|
+
// TEST127: Test CapGraph adds nodes and edges from capability definitions
|
|
1396
1393
|
function test127_capGraphFindPath() {
|
|
1397
1394
|
const registry = new CapMatrix();
|
|
1398
1395
|
const mockHost = { executeCap: async () => ({ textOutput: 'mock' }) };
|
|
@@ -1425,7 +1422,7 @@ function test127_capGraphFindPath() {
|
|
|
1425
1422
|
assertEqual(path.length, 0, 'Same spec path should be empty');
|
|
1426
1423
|
}
|
|
1427
1424
|
|
|
1428
|
-
// TEST128: CapGraph
|
|
1425
|
+
// TEST128: Test CapGraph tracks outgoing and incoming edges for spec conversions
|
|
1429
1426
|
function test128_capGraphFindAllPaths() {
|
|
1430
1427
|
const registry = new CapMatrix();
|
|
1431
1428
|
const mockHost = { executeCap: async () => ({ textOutput: 'mock' }) };
|
|
@@ -1445,7 +1442,7 @@ function test128_capGraphFindAllPaths() {
|
|
|
1445
1442
|
assertEqual(paths[1].length, 2, 'Longer path second (via string)');
|
|
1446
1443
|
}
|
|
1447
1444
|
|
|
1448
|
-
// TEST129: CapGraph
|
|
1445
|
+
// TEST129: Test CapGraph detects direct and indirect conversion paths between specs
|
|
1449
1446
|
function test129_capGraphGetDirectEdges() {
|
|
1450
1447
|
const registry1 = new CapMatrix();
|
|
1451
1448
|
const registry2 = new CapMatrix();
|
|
@@ -1470,7 +1467,7 @@ function test129_capGraphGetDirectEdges() {
|
|
|
1470
1467
|
assert(edges[0].specificity > edges[1].specificity, 'First edge should have higher specificity');
|
|
1471
1468
|
}
|
|
1472
1469
|
|
|
1473
|
-
// TEST130: CapGraph
|
|
1470
|
+
// TEST130: Test CapGraph finds shortest path for spec conversion chain
|
|
1474
1471
|
function test130_capGraphStats() {
|
|
1475
1472
|
const registry = new CapMatrix();
|
|
1476
1473
|
const mockHost = { executeCap: async () => ({ textOutput: 'mock' }) };
|
|
@@ -1491,7 +1488,7 @@ function test130_capGraphStats() {
|
|
|
1491
1488
|
assertEqual(stats.outputUrnCount, 3, '3 output URNs');
|
|
1492
1489
|
}
|
|
1493
1490
|
|
|
1494
|
-
// TEST131: CapGraph
|
|
1491
|
+
// TEST131: Test CapGraph finds all conversion paths sorted by length
|
|
1495
1492
|
function test131_capGraphWithCapBlock() {
|
|
1496
1493
|
const providerRegistry = new CapMatrix();
|
|
1497
1494
|
const cartridgeRegistry = new CapMatrix();
|
|
@@ -1525,7 +1522,7 @@ function test131_capGraphWithCapBlock() {
|
|
|
1525
1522
|
// caller.rs: TEST156-TEST159
|
|
1526
1523
|
// ============================================================================
|
|
1527
1524
|
|
|
1528
|
-
// TEST156:
|
|
1525
|
+
// TEST156: Test creating StdinSource Data variant with byte vector
|
|
1529
1526
|
function test156_stdinSourceFromData() {
|
|
1530
1527
|
const testData = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]); // "Hello"
|
|
1531
1528
|
const source = StdinSource.fromData(testData);
|
|
@@ -1536,7 +1533,7 @@ function test156_stdinSourceFromData() {
|
|
|
1536
1533
|
assertEqual(source.data, testData, 'Should store data');
|
|
1537
1534
|
}
|
|
1538
1535
|
|
|
1539
|
-
// TEST157:
|
|
1536
|
+
// TEST157: Test creating StdinSource FileReference variant with all required fields
|
|
1540
1537
|
function test157_stdinSourceFromFileReference() {
|
|
1541
1538
|
const trackedFileId = 'tracked-file-123';
|
|
1542
1539
|
const originalPath = '/path/to/original.pdf';
|
|
@@ -1553,7 +1550,7 @@ function test157_stdinSourceFromFileReference() {
|
|
|
1553
1550
|
assertEqual(source.mediaUrn, mediaUrn, 'Should store mediaUrn');
|
|
1554
1551
|
}
|
|
1555
1552
|
|
|
1556
|
-
// TEST158: StdinSource Data with empty vector stores and retrieves correctly
|
|
1553
|
+
// TEST158: Test StdinSource Data with empty vector stores and retrieves correctly
|
|
1557
1554
|
function test158_stdinSourceWithEmptyData() {
|
|
1558
1555
|
const emptyData = new Uint8Array(0);
|
|
1559
1556
|
const source = StdinSource.fromData(emptyData);
|
|
@@ -1561,7 +1558,7 @@ function test158_stdinSourceWithEmptyData() {
|
|
|
1561
1558
|
assertEqual(source.data.length, 0, 'Data length should be 0');
|
|
1562
1559
|
}
|
|
1563
1560
|
|
|
1564
|
-
// TEST159: StdinSource Data with binary content like PNG header bytes
|
|
1561
|
+
// TEST159: Test StdinSource Data with binary content like PNG header bytes
|
|
1565
1562
|
function test159_stdinSourceWithBinaryContent() {
|
|
1566
1563
|
const pngHeader = new Uint8Array([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
|
|
1567
1564
|
const source = StdinSource.fromData(pngHeader);
|
|
@@ -1575,27 +1572,27 @@ function test159_stdinSourceWithBinaryContent() {
|
|
|
1575
1572
|
// caller.rs: TEST274-TEST283
|
|
1576
1573
|
// ============================================================================
|
|
1577
1574
|
|
|
1578
|
-
// TEST274: CapArgumentValue
|
|
1575
|
+
// TEST274: Test CapArgumentValue::new stores media_urn and raw byte value
|
|
1579
1576
|
function test274_capArgumentValueNew() {
|
|
1580
1577
|
const arg = new CapArgumentValue('media:model-spec;textable', new Uint8Array([103, 112, 116, 45, 52]));
|
|
1581
1578
|
assertEqual(arg.mediaUrn, 'media:model-spec;textable', 'mediaUrn must match');
|
|
1582
1579
|
assertEqual(arg.value.length, 5, 'value must have 5 bytes');
|
|
1583
1580
|
}
|
|
1584
1581
|
|
|
1585
|
-
// TEST275: CapArgumentValue
|
|
1582
|
+
// TEST275: Test CapArgumentValue::from_str converts string to UTF-8 bytes
|
|
1586
1583
|
function test275_capArgumentValueFromStr() {
|
|
1587
1584
|
const arg = CapArgumentValue.fromStr('media:string;textable', 'hello world');
|
|
1588
1585
|
assertEqual(arg.mediaUrn, 'media:string;textable', 'mediaUrn must match');
|
|
1589
1586
|
assertEqual(new TextDecoder().decode(arg.value), 'hello world', 'value must decode correctly');
|
|
1590
1587
|
}
|
|
1591
1588
|
|
|
1592
|
-
// TEST276: CapArgumentValue
|
|
1589
|
+
// TEST276: Test CapArgumentValue::value_as_str succeeds for UTF-8 data
|
|
1593
1590
|
function test276_capArgumentValueAsStrValid() {
|
|
1594
1591
|
const arg = CapArgumentValue.fromStr('media:string', 'test');
|
|
1595
1592
|
assertEqual(arg.valueAsStr(), 'test', 'valueAsStr must return test');
|
|
1596
1593
|
}
|
|
1597
1594
|
|
|
1598
|
-
// TEST277: CapArgumentValue
|
|
1595
|
+
// TEST277: Test CapArgumentValue::value_as_str fails for non-UTF-8 binary data
|
|
1599
1596
|
function test277_capArgumentValueAsStrInvalidUtf8() {
|
|
1600
1597
|
const arg = new CapArgumentValue('media:pdf', new Uint8Array([0xFF, 0xFE, 0x80]));
|
|
1601
1598
|
let threw = false;
|
|
@@ -1607,7 +1604,7 @@ function test277_capArgumentValueAsStrInvalidUtf8() {
|
|
|
1607
1604
|
assert(threw, 'non-UTF-8 data must fail on valueAsStr with fatal decoder');
|
|
1608
1605
|
}
|
|
1609
1606
|
|
|
1610
|
-
// TEST278: CapArgumentValue with empty value stores empty
|
|
1607
|
+
// TEST278: Test CapArgumentValue::new with empty value stores empty vec
|
|
1611
1608
|
function test278_capArgumentValueEmpty() {
|
|
1612
1609
|
const arg = new CapArgumentValue('media:void', new Uint8Array([]));
|
|
1613
1610
|
assertEqual(arg.value.length, 0, 'empty value must have 0 bytes');
|
|
@@ -1616,13 +1613,13 @@ function test278_capArgumentValueEmpty() {
|
|
|
1616
1613
|
|
|
1617
1614
|
// TEST279-281: N/A for JS (Rust Debug/Clone/Send traits)
|
|
1618
1615
|
|
|
1619
|
-
// TEST282: CapArgumentValue
|
|
1616
|
+
// TEST282: Test CapArgumentValue::from_str with Unicode string preserves all characters
|
|
1620
1617
|
function test282_capArgumentValueUnicode() {
|
|
1621
1618
|
const arg = CapArgumentValue.fromStr('media:string', 'hello \u4e16\u754c \ud83c\udf0d');
|
|
1622
1619
|
assertEqual(arg.valueAsStr(), 'hello \u4e16\u754c \ud83c\udf0d', 'Unicode must roundtrip');
|
|
1623
1620
|
}
|
|
1624
1621
|
|
|
1625
|
-
// TEST283: CapArgumentValue with large binary payload preserves all bytes
|
|
1622
|
+
// TEST283: Test CapArgumentValue with large binary payload preserves all bytes
|
|
1626
1623
|
function test283_capArgumentValueLargeBinary() {
|
|
1627
1624
|
const data = new Uint8Array(10000);
|
|
1628
1625
|
for (let i = 0; i < 10000; i++) {
|
|
@@ -1641,7 +1638,7 @@ function test283_capArgumentValueLargeBinary() {
|
|
|
1641
1638
|
|
|
1642
1639
|
const { TaggedUrn } = require('tagged-urn');
|
|
1643
1640
|
|
|
1644
|
-
// TEST304: MEDIA_AVAILABILITY_OUTPUT constant parses as valid media URN with correct tags
|
|
1641
|
+
// TEST304: Test MEDIA_AVAILABILITY_OUTPUT constant parses as valid media URN with correct tags
|
|
1645
1642
|
function test304_mediaAvailabilityOutputConstant() {
|
|
1646
1643
|
const urn = TaggedUrn.fromString(MEDIA_AVAILABILITY_OUTPUT);
|
|
1647
1644
|
assert(urn.getTag('textable') !== undefined, 'model-availability must be textable');
|
|
@@ -1651,7 +1648,7 @@ function test304_mediaAvailabilityOutputConstant() {
|
|
|
1651
1648
|
assert(urn.conformsTo(reparsed), 'roundtrip must match original');
|
|
1652
1649
|
}
|
|
1653
1650
|
|
|
1654
|
-
// TEST305: MEDIA_PATH_OUTPUT constant parses as valid media URN with correct tags
|
|
1651
|
+
// TEST305: Test MEDIA_PATH_OUTPUT constant parses as valid media URN with correct tags
|
|
1655
1652
|
function test305_mediaPathOutputConstant() {
|
|
1656
1653
|
const urn = TaggedUrn.fromString(MEDIA_PATH_OUTPUT);
|
|
1657
1654
|
assert(urn.getTag('textable') !== undefined, 'model-path must be textable');
|
|
@@ -1661,7 +1658,7 @@ function test305_mediaPathOutputConstant() {
|
|
|
1661
1658
|
assert(urn.conformsTo(reparsed), 'roundtrip must match original');
|
|
1662
1659
|
}
|
|
1663
1660
|
|
|
1664
|
-
// TEST306: MEDIA_AVAILABILITY_OUTPUT and MEDIA_PATH_OUTPUT are distinct URNs
|
|
1661
|
+
// TEST306: Test MEDIA_AVAILABILITY_OUTPUT and MEDIA_PATH_OUTPUT are distinct URNs
|
|
1665
1662
|
function test306_availabilityAndPathOutputDistinct() {
|
|
1666
1663
|
assert(MEDIA_AVAILABILITY_OUTPUT !== MEDIA_PATH_OUTPUT, 'Must be distinct');
|
|
1667
1664
|
const avail = TaggedUrn.fromString(MEDIA_AVAILABILITY_OUTPUT);
|
|
@@ -1675,7 +1672,7 @@ function test306_availabilityAndPathOutputDistinct() {
|
|
|
1675
1672
|
assert(!matchResult, 'availability must not conform to path');
|
|
1676
1673
|
}
|
|
1677
1674
|
|
|
1678
|
-
// TEST307: model_availability_urn builds valid cap URN with correct op and media specs
|
|
1675
|
+
// TEST307: Test model_availability_urn builds valid cap URN with correct op and media specs
|
|
1679
1676
|
function test307_modelAvailabilityUrn() {
|
|
1680
1677
|
const urn = modelAvailabilityUrn();
|
|
1681
1678
|
assert(urn.hasTag('op', 'model-availability'), 'Must have op=model-availability');
|
|
@@ -1687,7 +1684,7 @@ function test307_modelAvailabilityUrn() {
|
|
|
1687
1684
|
assert(outSpec.conformsTo(expectedOut), 'output must conform to MEDIA_AVAILABILITY_OUTPUT');
|
|
1688
1685
|
}
|
|
1689
1686
|
|
|
1690
|
-
// TEST308: model_path_urn builds valid cap URN with correct op and media specs
|
|
1687
|
+
// TEST308: Test model_path_urn builds valid cap URN with correct op and media specs
|
|
1691
1688
|
function test308_modelPathUrn() {
|
|
1692
1689
|
const urn = modelPathUrn();
|
|
1693
1690
|
assert(urn.hasTag('op', 'model-path'), 'Must have op=model-path');
|
|
@@ -1699,23 +1696,27 @@ function test308_modelPathUrn() {
|
|
|
1699
1696
|
assert(outSpec.conformsTo(expectedOut), 'output must conform to MEDIA_PATH_OUTPUT');
|
|
1700
1697
|
}
|
|
1701
1698
|
|
|
1702
|
-
// TEST309: model_availability_urn and model_path_urn produce distinct URNs
|
|
1699
|
+
// TEST309: Test model_availability_urn and model_path_urn produce distinct URNs
|
|
1703
1700
|
function test309_modelAvailabilityAndPathAreDistinct() {
|
|
1704
1701
|
const avail = modelAvailabilityUrn();
|
|
1705
1702
|
const path = modelPathUrn();
|
|
1706
1703
|
assert(avail.toString() !== path.toString(), 'availability and path must be distinct');
|
|
1707
1704
|
}
|
|
1708
1705
|
|
|
1709
|
-
// TEST310: llm_generate_text_urn
|
|
1706
|
+
// TEST310: llm_generate_text_urn() produces a valid cap URN with textable in/out specs
|
|
1710
1707
|
function test310_llmGenerateTextUrn() {
|
|
1711
1708
|
const urn = llmGenerateTextUrn();
|
|
1712
1709
|
assert(urn.hasTag('op', 'generate_text'), 'Must have op=generate_text');
|
|
1713
1710
|
assert(urn.getTag('llm') !== undefined, 'Must have llm tag');
|
|
1714
1711
|
assert(urn.getTag('ml-model') !== undefined, 'Must have ml-model tag');
|
|
1712
|
+
assert(TaggedUrn.fromString(urn.getInSpec()).conformsTo(TaggedUrn.fromString(MEDIA_STRING)),
|
|
1713
|
+
'in_spec must conform to MEDIA_STRING');
|
|
1714
|
+
assert(TaggedUrn.fromString(urn.getOutSpec()).conformsTo(TaggedUrn.fromString(MEDIA_STRING)),
|
|
1715
|
+
'out_spec must conform to MEDIA_STRING');
|
|
1715
1716
|
}
|
|
1716
1717
|
|
|
1717
|
-
//
|
|
1718
|
-
function
|
|
1718
|
+
// Mirror-specific coverage: llm_generate_text_urn input/output specs conform to MEDIA_STRING
|
|
1719
|
+
function testLlmGenerateTextUrnSpecs() {
|
|
1719
1720
|
const urn = llmGenerateTextUrn();
|
|
1720
1721
|
const inSpec = TaggedUrn.fromString(urn.getInSpec());
|
|
1721
1722
|
const expectedIn = TaggedUrn.fromString(MEDIA_STRING);
|
|
@@ -1725,7 +1726,7 @@ function test311_llmGenerateTextUrnSpecs() {
|
|
|
1725
1726
|
assert(outSpec.conformsTo(expectedOut), 'out_spec must conform to MEDIA_STRING');
|
|
1726
1727
|
}
|
|
1727
1728
|
|
|
1728
|
-
// TEST312:
|
|
1729
|
+
// TEST312: Test all URN builders produce parseable cap URNs
|
|
1729
1730
|
function test312_allUrnBuildersProduceValidUrns() {
|
|
1730
1731
|
const avail = modelAvailabilityUrn();
|
|
1731
1732
|
const path = modelPathUrn();
|
|
@@ -2090,7 +2091,7 @@ const sampleRegistry = {
|
|
|
2090
2091
|
}
|
|
2091
2092
|
};
|
|
2092
2093
|
|
|
2093
|
-
// TEST320:
|
|
2094
|
+
// TEST320-335: CartridgeRepoServer and CartridgeRepoClient tests
|
|
2094
2095
|
function test320_cartridgeInfoConstruction() {
|
|
2095
2096
|
const data = {
|
|
2096
2097
|
id: 'testcartridge',
|
|
@@ -2117,7 +2118,7 @@ function test320_cartridgeInfoConstruction() {
|
|
|
2117
2118
|
assert(cartridge.caps[0].urn === 'cap:in="media:void";op=test;out="media:void"', 'Cap URN should match');
|
|
2118
2119
|
}
|
|
2119
2120
|
|
|
2120
|
-
// TEST321:
|
|
2121
|
+
// TEST321: CartridgeInfo.is_signed() returns true when signature is present
|
|
2121
2122
|
function test321_cartridgeInfoIsSigned() {
|
|
2122
2123
|
const signed = new CartridgeInfo({id: 'test', teamId: 'TEAM', signedAt: '2026-01-01', caps: []});
|
|
2123
2124
|
assert(signed.isSigned() === true, 'Cartridge with teamId and signedAt should be signed');
|
|
@@ -2129,7 +2130,7 @@ function test321_cartridgeInfoIsSigned() {
|
|
|
2129
2130
|
assert(unsigned2.isSigned() === false, 'Cartridge without signedAt should not be signed');
|
|
2130
2131
|
}
|
|
2131
2132
|
|
|
2132
|
-
// TEST322:
|
|
2133
|
+
// TEST322: CartridgeInfo.build_for_platform() returns the build matching the current platform
|
|
2133
2134
|
function test322_cartridgeInfoBuildForPlatform() {
|
|
2134
2135
|
const withBuilds = new CartridgeInfo({
|
|
2135
2136
|
id: 'test', version: '1.0.0', caps: [],
|
|
@@ -2163,7 +2164,7 @@ function test322_cartridgeInfoBuildForPlatform() {
|
|
|
2163
2164
|
assert(noBuilds.availablePlatforms().length === 0, 'Should have no platforms');
|
|
2164
2165
|
}
|
|
2165
2166
|
|
|
2166
|
-
// TEST323: CartridgeRepoServer
|
|
2167
|
+
// TEST323: CartridgeRepoServer validates registry JSON schema version
|
|
2167
2168
|
function test323_cartridgeRepoServerValidateRegistry() {
|
|
2168
2169
|
// Valid registry
|
|
2169
2170
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
@@ -2190,7 +2191,7 @@ function test323_cartridgeRepoServerValidateRegistry() {
|
|
|
2190
2191
|
assert(threw, 'Should throw for missing cartridges');
|
|
2191
2192
|
}
|
|
2192
2193
|
|
|
2193
|
-
// TEST324: CartridgeRepoServer
|
|
2194
|
+
// TEST324: CartridgeRepoServer transforms v3 registry JSON into flat cartridge array
|
|
2194
2195
|
function test324_cartridgeRepoServerTransformToArray() {
|
|
2195
2196
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
2196
2197
|
const cartridges = server.transformToCartridgeArray();
|
|
@@ -2215,7 +2216,7 @@ function test324_cartridgeRepoServerTransformToArray() {
|
|
|
2215
2216
|
assert(pdf.caps.length === 2, 'Should have 2 caps');
|
|
2216
2217
|
}
|
|
2217
2218
|
|
|
2218
|
-
// TEST325: CartridgeRepoServer
|
|
2219
|
+
// TEST325: CartridgeRepoServer.get_cartridges() returns all parsed cartridges
|
|
2219
2220
|
function test325_cartridgeRepoServerGetCartridges() {
|
|
2220
2221
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
2221
2222
|
const response = server.getCartridges();
|
|
@@ -2225,7 +2226,7 @@ function test325_cartridgeRepoServerGetCartridges() {
|
|
|
2225
2226
|
assert(response.cartridges.length === 2, 'Should have 2 cartridges');
|
|
2226
2227
|
}
|
|
2227
2228
|
|
|
2228
|
-
// TEST326: CartridgeRepoServer
|
|
2229
|
+
// TEST326: CartridgeRepoServer.get_cartridge() returns cartridge matching the given ID
|
|
2229
2230
|
function test326_cartridgeRepoServerGetCartridgeById() {
|
|
2230
2231
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
2231
2232
|
|
|
@@ -2237,7 +2238,7 @@ function test326_cartridgeRepoServerGetCartridgeById() {
|
|
|
2237
2238
|
assert(notFound === undefined, 'Should return undefined for missing cartridge');
|
|
2238
2239
|
}
|
|
2239
2240
|
|
|
2240
|
-
// TEST327: CartridgeRepoServer
|
|
2241
|
+
// TEST327: CartridgeRepoServer.search_cartridges() filters by text query against name and description
|
|
2241
2242
|
function test327_cartridgeRepoServerSearchCartridges() {
|
|
2242
2243
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
2243
2244
|
|
|
@@ -2252,7 +2253,7 @@ function test327_cartridgeRepoServerSearchCartridges() {
|
|
|
2252
2253
|
assert(noResults.length === 0, 'Should return empty for no matches');
|
|
2253
2254
|
}
|
|
2254
2255
|
|
|
2255
|
-
// TEST328: CartridgeRepoServer
|
|
2256
|
+
// TEST328: CartridgeRepoServer.get_by_category() filters cartridges by category tag
|
|
2256
2257
|
function test328_cartridgeRepoServerGetByCategory() {
|
|
2257
2258
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
2258
2259
|
|
|
@@ -2265,7 +2266,7 @@ function test328_cartridgeRepoServerGetByCategory() {
|
|
|
2265
2266
|
assert(textCartridges[0].id === 'txtcartridge', 'Should be txtcartridge');
|
|
2266
2267
|
}
|
|
2267
2268
|
|
|
2268
|
-
// TEST329: CartridgeRepoServer
|
|
2269
|
+
// TEST329: CartridgeRepoServer.get_suggestions_for_cap() finds cartridges providing a given cap URN
|
|
2269
2270
|
function test329_cartridgeRepoServerGetByCap() {
|
|
2270
2271
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
2271
2272
|
|
|
@@ -2280,7 +2281,7 @@ function test329_cartridgeRepoServerGetByCap() {
|
|
|
2280
2281
|
assert(metadataCartridges.length === 1, 'Should find metadata cap');
|
|
2281
2282
|
}
|
|
2282
2283
|
|
|
2283
|
-
// TEST330: CartridgeRepoClient
|
|
2284
|
+
// TEST330: CartridgeRepoClient updates its local cache from server response
|
|
2284
2285
|
function test330_cartridgeRepoClientUpdateCache() {
|
|
2285
2286
|
const client = new CartridgeRepoClient(3600);
|
|
2286
2287
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
@@ -2294,7 +2295,7 @@ function test330_cartridgeRepoClientUpdateCache() {
|
|
|
2294
2295
|
assert(cache.capToCartridges.size > 0, 'Should have cap mappings');
|
|
2295
2296
|
}
|
|
2296
2297
|
|
|
2297
|
-
// TEST331: CartridgeRepoClient
|
|
2298
|
+
// TEST331: CartridgeRepoClient.get_suggestions_for_cap() returns cartridge suggestions for a cap URN
|
|
2298
2299
|
function test331_cartridgeRepoClientGetSuggestions() {
|
|
2299
2300
|
const client = new CartridgeRepoClient(3600);
|
|
2300
2301
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
@@ -2311,7 +2312,7 @@ function test331_cartridgeRepoClientGetSuggestions() {
|
|
|
2311
2312
|
assert(suggestions[0].capTitle === 'Disbind PDF', 'Should have cap title');
|
|
2312
2313
|
}
|
|
2313
2314
|
|
|
2314
|
-
// TEST332: CartridgeRepoClient
|
|
2315
|
+
// TEST332: CartridgeRepoClient.get_cartridge() retrieves a specific cartridge by ID from cache
|
|
2315
2316
|
function test332_cartridgeRepoClientGetCartridge() {
|
|
2316
2317
|
const client = new CartridgeRepoClient(3600);
|
|
2317
2318
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
@@ -2327,7 +2328,7 @@ function test332_cartridgeRepoClientGetCartridge() {
|
|
|
2327
2328
|
assert(notFound === null, 'Should return null for missing cartridge');
|
|
2328
2329
|
}
|
|
2329
2330
|
|
|
2330
|
-
// TEST333: CartridgeRepoClient
|
|
2331
|
+
// TEST333: CartridgeRepoClient.get_all_caps() returns aggregate cap URNs from all cached cartridges
|
|
2331
2332
|
function test333_cartridgeRepoClientGetAllCaps() {
|
|
2332
2333
|
const client = new CartridgeRepoClient(3600);
|
|
2333
2334
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
@@ -2341,7 +2342,7 @@ function test333_cartridgeRepoClientGetAllCaps() {
|
|
|
2341
2342
|
assert(caps.every(c => typeof c === 'string'), 'All caps should be strings');
|
|
2342
2343
|
}
|
|
2343
2344
|
|
|
2344
|
-
// TEST334: CartridgeRepoClient
|
|
2345
|
+
// TEST334: CartridgeRepoClient.needs_sync() returns true when cache TTL has expired
|
|
2345
2346
|
function test334_cartridgeRepoClientNeedsSync() {
|
|
2346
2347
|
const client = new CartridgeRepoClient(1); // 1 second TTL
|
|
2347
2348
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
@@ -2362,7 +2363,7 @@ function test334_cartridgeRepoClientNeedsSync() {
|
|
|
2362
2363
|
// Note: Can't test this synchronously, would need async test
|
|
2363
2364
|
}
|
|
2364
2365
|
|
|
2365
|
-
// TEST335:
|
|
2366
|
+
// TEST335: Server creates registry response and client consumes it end-to-end
|
|
2366
2367
|
function test335_cartridgeRepoServerClientIntegration() {
|
|
2367
2368
|
// Server creates API response
|
|
2368
2369
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
@@ -2392,11 +2393,11 @@ function test335_cartridgeRepoServerClientIntegration() {
|
|
|
2392
2393
|
}
|
|
2393
2394
|
|
|
2394
2395
|
// ============================================================================
|
|
2395
|
-
// media_urn.rs:
|
|
2396
|
+
// media_urn.rs: TEST1294-TEST1302 (MediaUrn predicates)
|
|
2396
2397
|
// ============================================================================
|
|
2397
2398
|
|
|
2398
|
-
//
|
|
2399
|
-
function
|
|
2399
|
+
// TEST1312: is_image returns true only when image marker tag is present
|
|
2400
|
+
function test1312_isImage() {
|
|
2400
2401
|
assert(MediaUrn.fromString(MEDIA_PNG).isImage(), 'MEDIA_PNG should be image');
|
|
2401
2402
|
assert(MediaUrn.fromString('media:image;png;thumbnail').isImage(), 'media:image;png;thumbnail should be image');
|
|
2402
2403
|
assert(MediaUrn.fromString('media:image;jpg').isImage(), 'media:image;jpg should be image');
|
|
@@ -2407,8 +2408,8 @@ function test546_isImage() {
|
|
|
2407
2408
|
assert(!MediaUrn.fromString(MEDIA_VIDEO).isImage(), 'MEDIA_VIDEO should not be image');
|
|
2408
2409
|
}
|
|
2409
2410
|
|
|
2410
|
-
//
|
|
2411
|
-
function
|
|
2411
|
+
// TEST1313: is_audio returns true only when audio marker tag is present
|
|
2412
|
+
function test1313_isAudio() {
|
|
2412
2413
|
assert(MediaUrn.fromString(MEDIA_AUDIO).isAudio(), 'MEDIA_AUDIO should be audio');
|
|
2413
2414
|
assert(MediaUrn.fromString(MEDIA_AUDIO_SPEECH).isAudio(), 'MEDIA_AUDIO_SPEECH should be audio');
|
|
2414
2415
|
assert(MediaUrn.fromString('media:audio;mp3').isAudio(), 'media:audio;mp3 should be audio');
|
|
@@ -2418,8 +2419,8 @@ function test547_isAudio() {
|
|
|
2418
2419
|
assert(!MediaUrn.fromString(MEDIA_STRING).isAudio(), 'MEDIA_STRING should not be audio');
|
|
2419
2420
|
}
|
|
2420
2421
|
|
|
2421
|
-
//
|
|
2422
|
-
function
|
|
2422
|
+
// TEST1314: is_video returns true only when video marker tag is present
|
|
2423
|
+
function test1314_isVideo() {
|
|
2423
2424
|
assert(MediaUrn.fromString(MEDIA_VIDEO).isVideo(), 'MEDIA_VIDEO should be video');
|
|
2424
2425
|
assert(MediaUrn.fromString('media:video;mp4').isVideo(), 'media:video;mp4 should be video');
|
|
2425
2426
|
// Non-video types
|
|
@@ -2428,8 +2429,8 @@ function test548_isVideo() {
|
|
|
2428
2429
|
assert(!MediaUrn.fromString(MEDIA_STRING).isVideo(), 'MEDIA_STRING should not be video');
|
|
2429
2430
|
}
|
|
2430
2431
|
|
|
2431
|
-
//
|
|
2432
|
-
function
|
|
2432
|
+
// TEST1315: is_numeric returns true only when numeric marker tag is present
|
|
2433
|
+
function test1315_isNumeric() {
|
|
2433
2434
|
assert(MediaUrn.fromString(MEDIA_INTEGER).isNumeric(), 'MEDIA_INTEGER should be numeric');
|
|
2434
2435
|
assert(MediaUrn.fromString(MEDIA_NUMBER).isNumeric(), 'MEDIA_NUMBER should be numeric');
|
|
2435
2436
|
assert(MediaUrn.fromString(MEDIA_INTEGER_LIST).isNumeric(), 'MEDIA_INTEGER_LIST should be numeric');
|
|
@@ -2440,8 +2441,8 @@ function test549_isNumeric() {
|
|
|
2440
2441
|
assert(!MediaUrn.fromString(MEDIA_IDENTITY).isNumeric(), 'MEDIA_IDENTITY should not be numeric');
|
|
2441
2442
|
}
|
|
2442
2443
|
|
|
2443
|
-
//
|
|
2444
|
-
function
|
|
2444
|
+
// TEST1298: is_bool returns true only when bool marker tag is present
|
|
2445
|
+
function test1298_isBool() {
|
|
2445
2446
|
assert(MediaUrn.fromString(MEDIA_BOOLEAN).isBool(), 'MEDIA_BOOLEAN should be bool');
|
|
2446
2447
|
assert(MediaUrn.fromString(MEDIA_BOOLEAN_LIST).isBool(), 'MEDIA_BOOLEAN_LIST should be bool');
|
|
2447
2448
|
// MEDIA_DECISION is now a JSON record type (not bool)
|
|
@@ -2452,8 +2453,8 @@ function test550_isBool() {
|
|
|
2452
2453
|
assert(!MediaUrn.fromString(MEDIA_IDENTITY).isBool(), 'MEDIA_IDENTITY should not be bool');
|
|
2453
2454
|
}
|
|
2454
2455
|
|
|
2455
|
-
//
|
|
2456
|
-
function
|
|
2456
|
+
// TEST1299: is_file_path returns true for scalar file-path, false for array
|
|
2457
|
+
function test1299_isFilePath() {
|
|
2457
2458
|
assert(MediaUrn.fromString(MEDIA_FILE_PATH).isFilePath(), 'MEDIA_FILE_PATH should be file-path');
|
|
2458
2459
|
// Array file-path is NOT isFilePath (it's isFilePathArray)
|
|
2459
2460
|
assert(!MediaUrn.fromString(MEDIA_FILE_PATH_ARRAY).isFilePath(), 'MEDIA_FILE_PATH_ARRAY should not be isFilePath');
|
|
@@ -2462,8 +2463,8 @@ function test551_isFilePath() {
|
|
|
2462
2463
|
assert(!MediaUrn.fromString(MEDIA_IDENTITY).isFilePath(), 'MEDIA_IDENTITY should not be file-path');
|
|
2463
2464
|
}
|
|
2464
2465
|
|
|
2465
|
-
//
|
|
2466
|
-
function
|
|
2466
|
+
// TEST1300: is_file_path_array returns true for list file-path, false for scalar
|
|
2467
|
+
function test1300_isFilePathArray() {
|
|
2467
2468
|
assert(MediaUrn.fromString(MEDIA_FILE_PATH_ARRAY).isFilePathArray(), 'MEDIA_FILE_PATH_ARRAY should be file-path-array');
|
|
2468
2469
|
// Scalar file-path is NOT isFilePathArray
|
|
2469
2470
|
assert(!MediaUrn.fromString(MEDIA_FILE_PATH).isFilePathArray(), 'MEDIA_FILE_PATH should not be isFilePathArray');
|
|
@@ -2471,8 +2472,8 @@ function test552_isFilePathArray() {
|
|
|
2471
2472
|
assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isFilePathArray(), 'MEDIA_STRING_LIST should not be file-path-array');
|
|
2472
2473
|
}
|
|
2473
2474
|
|
|
2474
|
-
//
|
|
2475
|
-
function
|
|
2475
|
+
// TEST1301: is_any_file_path returns true for both scalar and array file-path
|
|
2476
|
+
function test1301_isAnyFilePath() {
|
|
2476
2477
|
assert(MediaUrn.fromString(MEDIA_FILE_PATH).isAnyFilePath(), 'MEDIA_FILE_PATH should be any-file-path');
|
|
2477
2478
|
assert(MediaUrn.fromString(MEDIA_FILE_PATH_ARRAY).isAnyFilePath(), 'MEDIA_FILE_PATH_ARRAY should be any-file-path');
|
|
2478
2479
|
// Non-file-path types
|
|
@@ -2480,9 +2481,9 @@ function test553_isAnyFilePath() {
|
|
|
2480
2481
|
assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isAnyFilePath(), 'MEDIA_STRING_LIST should not be any-file-path');
|
|
2481
2482
|
}
|
|
2482
2483
|
|
|
2483
|
-
//
|
|
2484
|
-
//
|
|
2485
|
-
function
|
|
2484
|
+
// Mirror-specific coverage: isCollection returns true when collection marker tag is present
|
|
2485
|
+
// Mirror-specific coverage: N/A for JS (MEDIA_COLLECTION constants removed - no longer exists)
|
|
2486
|
+
function testisCollection() {
|
|
2486
2487
|
// Skip - collection types removed from capdag
|
|
2487
2488
|
}
|
|
2488
2489
|
|
|
@@ -2492,8 +2493,8 @@ function test554_isCollection() {
|
|
|
2492
2493
|
|
|
2493
2494
|
// TEST557: N/A for JS (audio_media_urn_for_ext helper not in JS)
|
|
2494
2495
|
|
|
2495
|
-
//
|
|
2496
|
-
function
|
|
2496
|
+
// TEST1302: predicates are consistent with constants — every constant triggers exactly the expected predicates
|
|
2497
|
+
function test1302_predicateConstantConsistency() {
|
|
2497
2498
|
// MEDIA_INTEGER must be numeric, text, scalar, NOT binary/bool/image/audio/video
|
|
2498
2499
|
const intUrn = MediaUrn.fromString(MEDIA_INTEGER);
|
|
2499
2500
|
assert(intUrn.isNumeric(), 'MEDIA_INTEGER must be numeric');
|
|
@@ -2529,11 +2530,11 @@ function test558_predicateConstantConsistency() {
|
|
|
2529
2530
|
}
|
|
2530
2531
|
|
|
2531
2532
|
// ============================================================================
|
|
2532
|
-
// cap_urn.rs:
|
|
2533
|
+
// cap_urn.rs: TEST1303-TEST1307 (CapUrn tier tests)
|
|
2533
2534
|
// ============================================================================
|
|
2534
2535
|
|
|
2535
|
-
//
|
|
2536
|
-
function
|
|
2536
|
+
// TEST1303: without_tag removes tag, ignores in/out, case-insensitive for keys
|
|
2537
|
+
function test1303_withoutTag() {
|
|
2537
2538
|
const cap = CapUrn.fromString('cap:in="media:void";op=test;ext=pdf;out="media:void"');
|
|
2538
2539
|
const removed = cap.withoutTag('ext');
|
|
2539
2540
|
assertEqual(removed.getTag('ext'), undefined, 'withoutTag should remove ext');
|
|
@@ -2554,8 +2555,8 @@ function test559_withoutTag() {
|
|
|
2554
2555
|
assert(same3.equals(cap), 'Removing non-existent tag is no-op');
|
|
2555
2556
|
}
|
|
2556
2557
|
|
|
2557
|
-
//
|
|
2558
|
-
function
|
|
2558
|
+
// TEST1304: with_in_spec and with_out_spec change direction specs
|
|
2559
|
+
function test1304_withInOutSpec() {
|
|
2559
2560
|
const cap = CapUrn.fromString('cap:in="media:void";op=test;out="media:void"');
|
|
2560
2561
|
|
|
2561
2562
|
const changedIn = cap.withInSpec('media:');
|
|
@@ -2568,17 +2569,17 @@ function test560_withInOutSpec() {
|
|
|
2568
2569
|
assertEqual(changedOut.getOutSpec(), 'media:string', 'withOutSpec should change outSpec');
|
|
2569
2570
|
|
|
2570
2571
|
// Chain both
|
|
2571
|
-
const changedBoth = cap.withInSpec('media:pdf').withOutSpec(
|
|
2572
|
+
const changedBoth = cap.withInSpec('media:pdf').withOutSpec(MEDIA_TXT);
|
|
2572
2573
|
assertEqual(changedBoth.getInSpec(), 'media:pdf', 'Chain should set inSpec');
|
|
2573
|
-
assertEqual(changedBoth.getOutSpec(),
|
|
2574
|
+
assertEqual(changedBoth.getOutSpec(), MEDIA_TXT, 'Chain should set outSpec');
|
|
2574
2575
|
}
|
|
2575
2576
|
|
|
2576
2577
|
// TEST561: N/A for JS (in_media_urn/out_media_urn not in JS CapUrn)
|
|
2577
2578
|
|
|
2578
2579
|
// TEST562: N/A for JS (canonical_option not in JS CapUrn)
|
|
2579
2580
|
|
|
2580
|
-
//
|
|
2581
|
-
function
|
|
2581
|
+
// TEST1305: CapMatcher::find_all_matches returns all matching caps sorted by specificity
|
|
2582
|
+
function test1305_findAllMatches() {
|
|
2582
2583
|
const caps = [
|
|
2583
2584
|
CapUrn.fromString('cap:in="media:void";op=test;out="media:void"'),
|
|
2584
2585
|
CapUrn.fromString('cap:in="media:void";op=test;ext=pdf;out="media:void"'),
|
|
@@ -2595,8 +2596,8 @@ function test563_findAllMatches() {
|
|
|
2595
2596
|
assertEqual(matches[0].getTag('ext'), 'pdf', 'Most specific match should have ext=pdf');
|
|
2596
2597
|
}
|
|
2597
2598
|
|
|
2598
|
-
//
|
|
2599
|
-
function
|
|
2599
|
+
// TEST1306: CapMatcher::are_compatible detects bidirectional overlap
|
|
2600
|
+
function test1306_areCompatible() {
|
|
2600
2601
|
const caps1 = [
|
|
2601
2602
|
CapUrn.fromString('cap:in="media:void";op=test;out="media:void"'),
|
|
2602
2603
|
];
|
|
@@ -2620,8 +2621,8 @@ function test564_areCompatible() {
|
|
|
2620
2621
|
|
|
2621
2622
|
// TEST565: N/A for JS (tags_to_string not in JS CapUrn)
|
|
2622
2623
|
|
|
2623
|
-
//
|
|
2624
|
-
function
|
|
2624
|
+
// TEST1307: with_tag silently ignores in/out keys
|
|
2625
|
+
function test1307_withTagIgnoresInOut() {
|
|
2625
2626
|
const cap = CapUrn.fromString('cap:in="media:void";op=test;out="media:void"');
|
|
2626
2627
|
// Attempting to set in/out via withTag is silently ignored
|
|
2627
2628
|
const same = cap.withTag('in', 'media:');
|
|
@@ -2631,41 +2632,129 @@ function test566_withTagIgnoresInOut() {
|
|
|
2631
2632
|
assertEqual(same2.getOutSpec(), 'media:void', 'withTag must not change out_spec');
|
|
2632
2633
|
}
|
|
2633
2634
|
|
|
2635
|
+
// TEST1294: RULE11 - void-input cap with stdin source rejected
|
|
2636
|
+
function test1294_rule11VoidInputWithStdinRejected() {
|
|
2637
|
+
const urn = CapUrn.fromString('cap:in="media:void";op=test;out="media:string"');
|
|
2638
|
+
const cap = new Cap(urn, 'Test', 'test-cmd');
|
|
2639
|
+
const stdinSource = ArgSource.fromJSON({ stdin: 'media:string' });
|
|
2640
|
+
cap.args = [new CapArg('media:string', true, [stdinSource])];
|
|
2641
|
+
try {
|
|
2642
|
+
validateCapArgs(cap);
|
|
2643
|
+
assert(false, 'Should have thrown RULE11 for void input with stdin');
|
|
2644
|
+
} catch (e) {
|
|
2645
|
+
assert(e instanceof ValidationError, 'Should be ValidationError');
|
|
2646
|
+
assert(e.message.includes('RULE11'), 'Should mention RULE11: ' + e.message);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
// TEST1295: RULE11 - non-void-input cap without stdin source rejected
|
|
2651
|
+
function test1295_rule11NonVoidInputWithoutStdinRejected() {
|
|
2652
|
+
const urn = CapUrn.fromString('cap:in="media:string";op=test;out="media:string"');
|
|
2653
|
+
const cap = new Cap(urn, 'Test', 'test-cmd');
|
|
2654
|
+
const posSource = ArgSource.fromJSON({ cli_flag: '--name' });
|
|
2655
|
+
cap.args = [new CapArg('media:string', true, [posSource])];
|
|
2656
|
+
try {
|
|
2657
|
+
validateCapArgs(cap);
|
|
2658
|
+
assert(false, 'Should have thrown RULE11 for non-void input without stdin');
|
|
2659
|
+
} catch (e) {
|
|
2660
|
+
assert(e instanceof ValidationError, 'Should be ValidationError');
|
|
2661
|
+
assert(e.message.includes('RULE11'), 'Should mention RULE11: ' + e.message);
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
// TEST1296: RULE11 - void-input cap with only cli_flag sources passes
|
|
2666
|
+
function test1296_rule11VoidInputCliFlagOnly() {
|
|
2667
|
+
const urn = CapUrn.fromString('cap:in="media:void";op=test;out="media:string"');
|
|
2668
|
+
const cap = new Cap(urn, 'Test', 'test-cmd');
|
|
2669
|
+
const flagSource = ArgSource.fromJSON({ cli_flag: '--name' });
|
|
2670
|
+
cap.args = [new CapArg('media:string', true, [flagSource])];
|
|
2671
|
+
// Should not throw
|
|
2672
|
+
validateCapArgs(cap);
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
// TEST1297: RULE11 - non-void-input cap with stdin source passes
|
|
2676
|
+
function test1297_rule11NonVoidInputWithStdin() {
|
|
2677
|
+
const urn = CapUrn.fromString('cap:in="media:string";op=test;out="media:string"');
|
|
2678
|
+
const cap = new Cap(urn, 'Test', 'test-cmd');
|
|
2679
|
+
const stdinSource = ArgSource.fromJSON({ stdin: 'media:string' });
|
|
2680
|
+
cap.args = [new CapArg('media:string', true, [stdinSource])];
|
|
2681
|
+
// Should not throw
|
|
2682
|
+
validateCapArgs(cap);
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2634
2685
|
// TEST567: N/A for JS (conforms_to_str/accepts_str not in JS CapUrn)
|
|
2635
2686
|
|
|
2636
2687
|
// ============================================================================
|
|
2637
2688
|
// cap_urn.rs: TEST639-TEST653 (Cap URN wildcard tests)
|
|
2638
2689
|
// ============================================================================
|
|
2639
2690
|
|
|
2640
|
-
//
|
|
2641
|
-
|
|
2642
|
-
|
|
2691
|
+
// TEST639: cap: (empty) defaults to in=media:;out=media:
|
|
2692
|
+
function test639_emptyCapDefaultsToMediaWildcard() {
|
|
2693
|
+
const cap = CapUrn.fromString('cap:');
|
|
2694
|
+
assertEqual(cap.getInSpec(), MEDIA_IDENTITY, 'Empty cap should default in to media:');
|
|
2695
|
+
assertEqual(cap.getOutSpec(), MEDIA_IDENTITY, 'Empty cap should default out to media:');
|
|
2696
|
+
assertEqual(Object.keys(cap.tags).length, 0, 'Empty cap should have no extra tags');
|
|
2697
|
+
}
|
|
2643
2698
|
|
|
2644
|
-
//
|
|
2699
|
+
// TEST640: cap:in defaults out to media:
|
|
2700
|
+
function test640_inOnlyDefaultsOutToMedia() {
|
|
2701
|
+
const cap = CapUrn.fromString('cap:in');
|
|
2702
|
+
assertEqual(cap.getInSpec(), MEDIA_IDENTITY, 'Bare in should normalize to media:');
|
|
2703
|
+
assertEqual(cap.getOutSpec(), MEDIA_IDENTITY, 'Missing out should default to media:');
|
|
2704
|
+
}
|
|
2645
2705
|
|
|
2646
|
-
//
|
|
2706
|
+
// TEST641: cap:out defaults in to media:
|
|
2707
|
+
function test641_outOnlyDefaultsInToMedia() {
|
|
2708
|
+
const cap = CapUrn.fromString('cap:out');
|
|
2709
|
+
assertEqual(cap.getInSpec(), MEDIA_IDENTITY, 'Missing in should default to media:');
|
|
2710
|
+
assertEqual(cap.getOutSpec(), MEDIA_IDENTITY, 'Bare out should normalize to media:');
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
// TEST642: cap:in;out both become media:
|
|
2714
|
+
function test642_inOutWithoutValuesBecomeMedia() {
|
|
2715
|
+
const cap = CapUrn.fromString('cap:in;out');
|
|
2716
|
+
assertEqual(cap.getInSpec(), MEDIA_IDENTITY, 'Bare in should normalize to media:');
|
|
2717
|
+
assertEqual(cap.getOutSpec(), MEDIA_IDENTITY, 'Bare out should normalize to media:');
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
// TEST643: cap:in=*;out=* becomes media:
|
|
2647
2721
|
function test643_explicitAsteriskIsWildcard() {
|
|
2648
2722
|
const cap = CapUrn.fromString('cap:in=*;out=*');
|
|
2649
|
-
assertEqual(cap.getInSpec(),
|
|
2650
|
-
assertEqual(cap.getOutSpec(),
|
|
2723
|
+
assertEqual(cap.getInSpec(), MEDIA_IDENTITY, 'in=* should normalize to media:');
|
|
2724
|
+
assertEqual(cap.getOutSpec(), MEDIA_IDENTITY, 'out=* should normalize to media:');
|
|
2651
2725
|
}
|
|
2652
2726
|
|
|
2653
2727
|
// TEST644: cap:in=media:;out=* has specific in, wildcard out
|
|
2654
2728
|
function test644_specificInWildcardOut() {
|
|
2655
2729
|
const cap = CapUrn.fromString('cap:in=media:;out=*');
|
|
2656
2730
|
assertEqual(cap.getInSpec(), 'media:', 'Should have specific in');
|
|
2657
|
-
assertEqual(cap.getOutSpec(), '
|
|
2731
|
+
assertEqual(cap.getOutSpec(), 'media:', 'Wildcard out should normalize to media:');
|
|
2658
2732
|
}
|
|
2659
2733
|
|
|
2660
2734
|
// TEST645: cap:in=*;out=media:text has wildcard in, specific out
|
|
2661
2735
|
function test645_wildcardInSpecificOut() {
|
|
2662
2736
|
const cap = CapUrn.fromString('cap:in=*;out=media:text');
|
|
2663
|
-
assertEqual(cap.getInSpec(), '
|
|
2737
|
+
assertEqual(cap.getInSpec(), 'media:', 'Wildcard in should normalize to media:');
|
|
2664
2738
|
assertEqual(cap.getOutSpec(), 'media:text', 'Should have specific out');
|
|
2665
2739
|
}
|
|
2666
2740
|
|
|
2667
|
-
// TEST646:
|
|
2668
|
-
|
|
2741
|
+
// TEST646: cap:in=foo fails (invalid media URN)
|
|
2742
|
+
function test646_invalidInSpecFails() {
|
|
2743
|
+
assertThrows(
|
|
2744
|
+
() => CapUrn.fromString('cap:in=foo;out=media:'),
|
|
2745
|
+
ErrorCodes.INVALID_IN_SPEC,
|
|
2746
|
+
'Invalid in spec should fail hard'
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
// TEST647: cap:in=media:;out=bar fails (invalid media URN)
|
|
2751
|
+
function test647_invalidOutSpecFails() {
|
|
2752
|
+
assertThrows(
|
|
2753
|
+
() => CapUrn.fromString('cap:in=media:;out=bar'),
|
|
2754
|
+
ErrorCodes.INVALID_OUT_SPEC,
|
|
2755
|
+
'Invalid out spec should fail hard'
|
|
2756
|
+
);
|
|
2757
|
+
}
|
|
2669
2758
|
|
|
2670
2759
|
// TEST648: Wildcard in/out match specific caps
|
|
2671
2760
|
function test648_wildcardAcceptsSpecific() {
|
|
@@ -2687,7 +2776,7 @@ function test649_specificityScoring() {
|
|
|
2687
2776
|
|
|
2688
2777
|
// TEST650: N/A for JS (JS requires in/out, cap:in;out;op=test would fail parsing)
|
|
2689
2778
|
|
|
2690
|
-
// TEST651: All identity forms
|
|
2779
|
+
// TEST651: All identity forms produce the same CapUrn
|
|
2691
2780
|
function test651_identityFormsEquivalent() {
|
|
2692
2781
|
const forms = [
|
|
2693
2782
|
'cap:in=*;out=*',
|
|
@@ -2707,7 +2796,7 @@ function test651_identityFormsEquivalent() {
|
|
|
2707
2796
|
|
|
2708
2797
|
// TEST652: N/A for JS (CAP_IDENTITY constant not in JS)
|
|
2709
2798
|
|
|
2710
|
-
// TEST653: Identity (no
|
|
2799
|
+
// TEST653: Identity (no tags) does not match specific requests via routing
|
|
2711
2800
|
function test653_identityRoutingIsolation() {
|
|
2712
2801
|
const identity = CapUrn.fromString('cap:in=*;out=*');
|
|
2713
2802
|
const specificRequest = CapUrn.fromString('cap:in="media:void";op=test;out="media:void"');
|
|
@@ -4217,11 +4306,11 @@ function testRenderer_buildStrandGraphData_nestedForEachThrows() {
|
|
|
4217
4306
|
// must throw the same error to surface the issue rather than
|
|
4218
4307
|
// render a malformed graph.
|
|
4219
4308
|
const payload = {
|
|
4220
|
-
source_spec: 'media:a;list
|
|
4309
|
+
source_spec: 'media:a;list',
|
|
4221
4310
|
target_spec: 'media:a',
|
|
4222
4311
|
steps: [
|
|
4223
|
-
makeForEachStep('media:a;list;list'),
|
|
4224
4312
|
makeForEachStep('media:a;list'),
|
|
4313
|
+
makeForEachStep('media:a'),
|
|
4225
4314
|
makeCapStep('cap:in="media:a";op=x;out="media:a"', 'x', 'media:a', 'media:a', false, false),
|
|
4226
4315
|
],
|
|
4227
4316
|
};
|
|
@@ -5409,7 +5498,7 @@ async function runTests() {
|
|
|
5409
5498
|
runTest('TEST308: model_path_urn', test308_modelPathUrn);
|
|
5410
5499
|
runTest('TEST309: model_availability_and_path_are_distinct', test309_modelAvailabilityAndPathAreDistinct);
|
|
5411
5500
|
runTest('TEST310: llm_generate_text_urn', test310_llmGenerateTextUrn);
|
|
5412
|
-
runTest('
|
|
5501
|
+
runTest('llm_generate_text_urn_specs', testLlmGenerateTextUrnSpecs);
|
|
5413
5502
|
runTest('TEST312: all_urn_builders_produce_valid_urns', test312_allUrnBuildersProduceValidUrns);
|
|
5414
5503
|
|
|
5415
5504
|
// JS-specific tests (no Rust number)
|
|
@@ -5449,41 +5538,41 @@ async function runTests() {
|
|
|
5449
5538
|
runTest('TEST334: cartridge_repo_client_needs_sync', test334_cartridgeRepoClientNeedsSync);
|
|
5450
5539
|
runTest('TEST335: cartridge_repo_server_client_integration', test335_cartridgeRepoServerClientIntegration);
|
|
5451
5540
|
|
|
5452
|
-
// media_urn.rs:
|
|
5541
|
+
// media_urn.rs: TEST1312-TEST1315, TEST1298-TEST1302 (MediaUrn predicates)
|
|
5453
5542
|
console.log('\n--- media_urn.rs (predicates) ---');
|
|
5454
|
-
runTest('
|
|
5455
|
-
runTest('
|
|
5456
|
-
runTest('
|
|
5457
|
-
runTest('
|
|
5458
|
-
runTest('
|
|
5459
|
-
runTest('
|
|
5460
|
-
runTest('
|
|
5461
|
-
runTest('
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
console.log(' SKIP TEST557: N/A for JS (audio_media_urn_for_ext helper)');
|
|
5466
|
-
runTest('TEST558: predicate_constant_consistency', test558_predicateConstantConsistency);
|
|
5467
|
-
|
|
5468
|
-
// cap_urn.rs: TEST559-TEST567 (CapUrn tier tests)
|
|
5543
|
+
runTest('TEST1312: is_image', test1312_isImage);
|
|
5544
|
+
runTest('TEST1313: is_audio', test1313_isAudio);
|
|
5545
|
+
runTest('TEST1314: is_video', test1314_isVideo);
|
|
5546
|
+
runTest('TEST1315: is_numeric', test1315_isNumeric);
|
|
5547
|
+
runTest('TEST1298: is_bool', test1298_isBool);
|
|
5548
|
+
runTest('TEST1299: is_file_path', test1299_isFilePath);
|
|
5549
|
+
runTest('TEST1300: is_file_path_array', test1300_isFilePathArray);
|
|
5550
|
+
runTest('TEST1301: is_any_file_path', test1301_isAnyFilePath);
|
|
5551
|
+
runTest('TEST1302: predicate_constant_consistency', test1302_predicateConstantConsistency);
|
|
5552
|
+
|
|
5553
|
+
// cap_urn.rs: TEST1303-TEST1307 (CapUrn tier tests)
|
|
5469
5554
|
console.log('\n--- cap_urn.rs (tier tests) ---');
|
|
5470
|
-
runTest('
|
|
5471
|
-
runTest('
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
runTest('
|
|
5475
|
-
runTest('
|
|
5476
|
-
|
|
5477
|
-
runTest('
|
|
5478
|
-
|
|
5555
|
+
runTest('TEST1303: without_tag', test1303_withoutTag);
|
|
5556
|
+
runTest('TEST1304: with_in_out_spec', test1304_withInOutSpec);
|
|
5557
|
+
runTest('TEST1305: find_all_matches', test1305_findAllMatches);
|
|
5558
|
+
runTest('TEST1306: are_compatible', test1306_areCompatible);
|
|
5559
|
+
runTest('TEST1307: with_tag_ignores_in_out', test1307_withTagIgnoresInOut);
|
|
5560
|
+
runTest('TEST1294: rule11_void_input_with_stdin_rejected', test1294_rule11VoidInputWithStdinRejected);
|
|
5561
|
+
runTest('TEST1295: rule11_non_void_input_without_stdin_rejected', test1295_rule11NonVoidInputWithoutStdinRejected);
|
|
5562
|
+
runTest('TEST1296: rule11_void_input_cli_flag_only', test1296_rule11VoidInputCliFlagOnly);
|
|
5563
|
+
runTest('TEST1297: rule11_non_void_input_with_stdin', test1297_rule11NonVoidInputWithStdin);
|
|
5479
5564
|
|
|
5480
5565
|
// cap_urn.rs: TEST639-TEST653 (Cap URN wildcard tests)
|
|
5481
5566
|
console.log('\n--- cap_urn.rs (wildcard tests) ---');
|
|
5482
|
-
|
|
5567
|
+
runTest('TEST639: empty_cap_defaults_to_media_wildcard', test639_emptyCapDefaultsToMediaWildcard);
|
|
5568
|
+
runTest('TEST640: in_only_defaults_out_to_media', test640_inOnlyDefaultsOutToMedia);
|
|
5569
|
+
runTest('TEST641: out_only_defaults_in_to_media', test641_outOnlyDefaultsInToMedia);
|
|
5570
|
+
runTest('TEST642: in_out_without_values_become_media', test642_inOutWithoutValuesBecomeMedia);
|
|
5483
5571
|
runTest('TEST643: explicit_asterisk_is_wildcard', test643_explicitAsteriskIsWildcard);
|
|
5484
5572
|
runTest('TEST644: specific_in_wildcard_out', test644_specificInWildcardOut);
|
|
5485
5573
|
runTest('TEST645: wildcard_in_specific_out', test645_wildcardInSpecificOut);
|
|
5486
|
-
|
|
5574
|
+
runTest('TEST646: invalid_in_spec_fails', test646_invalidInSpecFails);
|
|
5575
|
+
runTest('TEST647: invalid_out_spec_fails', test647_invalidOutSpecFails);
|
|
5487
5576
|
runTest('TEST648: wildcard_accepts_specific', test648_wildcardAcceptsSpecific);
|
|
5488
5577
|
runTest('TEST649: specificity_scoring', test649_specificityScoring);
|
|
5489
5578
|
console.log(' SKIP TEST650: N/A for JS (requires in/out)');
|
|
@@ -5628,7 +5717,7 @@ async function runTests() {
|
|
|
5628
5717
|
runTest('RENDERER: buildStrand_standaloneCollect', testRenderer_buildStrandGraphData_standaloneCollect);
|
|
5629
5718
|
runTest('RENDERER: buildStrand_unclosedForEachBody', testRenderer_buildStrandGraphData_unclosedForEachBody);
|
|
5630
5719
|
runTest('RENDERER: buildStrand_nestedForEachThrows', testRenderer_buildStrandGraphData_nestedForEachThrows);
|
|
5631
|
-
runTest('RENDERER: collapseStrand_singleCapBody',
|
|
5720
|
+
runTest('RENDERER: collapseStrand_singleCapBody', testRenderer_collapseStrand_singleCapBodyKeepsCapOwnLabel);
|
|
5632
5721
|
runTest('RENDERER: collapseStrand_unclosedForEachBody', testRenderer_collapseStrand_unclosedForEachBodyCollapses);
|
|
5633
5722
|
runTest('RENDERER: collapseStrand_standaloneCollect', testRenderer_collapseStrand_standaloneCollectCollapses);
|
|
5634
5723
|
runTest('RENDERER: collapseStrand_seqCapBeforeForeach', testRenderer_collapseStrand_sequenceProducingCapBeforeForeach);
|