16
16
using Hl7 . Cql . Elm ;
17
17
using Hl7 . Cql . Fhir ;
18
18
using Hl7 . Cql . Invocation . Toolkit . Extensions ;
19
+ using Hl7 . Cql . Primitives ;
19
20
using Hl7 . Cql . Runtime ;
20
21
21
22
namespace CoreTests ;
@@ -392,4 +393,210 @@ public void DefinitionInvokerParameters_Tests()
392
393
keywordTestInvoker . Parameters [ 1 ] . Name . Should ( ) . Be ( "ref" , "Second parameter should preserve original CQL name even if it's a C# keyword" ) ;
393
394
keywordTestInvoker . Parameters [ 2 ] . Name . Should ( ) . Be ( "class" , "Third parameter should preserve original CQL name even if it's a C# keyword" ) ;
394
395
}
396
+
397
+ [ TestMethod ]
398
+ public void TestLargeTupleParameterInvocation_9Items ( )
399
+ {
400
+ // Arrange: Create a CQL library with a function that takes a large tuple parameter (9 items)
401
+ var cqlLibraryString = CqlLibraryString . Parse (
402
+ """
403
+ library LargeTupleInvocationTest version '1.0.0'
404
+ using FHIR version '4.0.1'
405
+
406
+ context Patient
407
+
408
+ define function "ProcessLargeTuple"(largeTuple Tuple{
409
+ item1 String,
410
+ item2 Integer,
411
+ item3 Boolean,
412
+ item4 Decimal,
413
+ item5 String,
414
+ item6 Integer,
415
+ item7 Boolean,
416
+ item8 Decimal,
417
+ item9 String
418
+ }):
419
+ 'Processed: ' + largeTuple.item1 + ', ' + largeTuple.item9
420
+ """ ) ;
421
+
422
+ var cqlToolkit = new CqlToolkit ( )
423
+ . AddCqlLibraries ( cqlLibraryString ) ;
424
+
425
+ using var librarySetInvoker = cqlToolkit . CreateLibrarySetInvoker ( ) ;
426
+ var ctx = FhirCqlContext . ForBundle ( ) ;
427
+ var libraryInvoker = librarySetInvoker . LibraryInvokers [ cqlLibraryString ] ;
428
+
429
+ // Act: Inspect the function signature that was actually created by the CQL compiler
430
+ var processTupleFunction = libraryInvoker . Definitions . Values
431
+ . FirstOrDefault ( d => d . DefinitionName == "ProcessLargeTuple" ) ;
432
+
433
+ // Assert: Function should be found and have the expected structure
434
+ processTupleFunction . Should ( ) . NotBeNull ( "ProcessLargeTuple function should exist" ) ;
435
+ processTupleFunction ! . Parameters . Should ( ) . HaveCount ( 1 , "Function should have exactly one parameter" ) ;
436
+
437
+ var parameterType = processTupleFunction . Parameters [ 0 ] . Type ;
438
+ parameterType . Should ( ) . NotBeNull ( "Parameter should have a type" ) ;
439
+
440
+ // Let's examine what type the CQL compiler actually generated
441
+ var underlyingType = parameterType . IsGenericType && parameterType . GetGenericTypeDefinition ( ) == typeof ( Nullable < > )
442
+ ? parameterType . GetGenericArguments ( ) [ 0 ]
443
+ : parameterType ;
444
+
445
+ // Verify this is indeed a large tuple with nested structure
446
+ underlyingType . IsCqlValueTuple ( ) . Should ( ) . BeTrue ( "The underlying ValueTuple should be recognized as a CQL value tuple" ) ;
447
+ typeof ( ITuple ) . IsAssignableFrom ( underlyingType ) . Should ( ) . BeTrue ( "The underlying ValueTuple should implement ITuple" ) ;
448
+
449
+ // Create a tuple instance that matches the discovered structure
450
+ var metadata = new CqlTupleMetadata ( [
451
+ typeof ( string ) , typeof ( int ? ) , typeof ( bool ? ) , typeof ( decimal ? ) , typeof ( string ) ,
452
+ typeof ( int ? ) , typeof ( bool ? ) , typeof ( decimal ? ) , typeof ( string )
453
+ ] , [ "item1" , "item2" , "item3" , "item4" , "item5" , "item6" , "item7" , "item8" , "item9" ] ) ;
454
+
455
+ // Create the large tuple (9 items) that will have the nested structure
456
+ var largeTuple9 = ( metadata , "first" , ( int ? ) 42 , ( bool ? ) true , ( decimal ? ) 3.14m , "middle" ,
457
+ ( int ? ) 100 , ( bool ? ) false , ( decimal ? ) 99.99m , "last" ) ;
458
+
459
+ // Verify the created tuple matches the expected underlying type
460
+ largeTuple9 . GetType ( ) . Should ( ) . Be ( underlyingType , "Created tuple type should match the underlying function parameter type" ) ;
461
+
462
+ // Act: Invoke the function with the large tuple
463
+ var result = processTupleFunction . Invoke ( ctx , largeTuple9 ) ;
464
+
465
+ // Assert: Function should execute successfully with large tuple
466
+ result . Should ( ) . NotBeNull ( "Function invocation should return a result" ) ;
467
+ result . Should ( ) . Be ( "Processed: first, last" , "Function should process the large tuple correctly" ) ;
468
+
469
+ // Verify that large tuple signature matching works properly for finding definitions
470
+ var signature = processTupleFunction . DefinitionSignature ;
471
+ var foundBySignature = libraryInvoker . Definitions . GetValueOrDefault ( signature ) ;
472
+
473
+ foundBySignature . Should ( ) . Be ( processTupleFunction , "Should be able to find function by its signature" ) ;
474
+ foundBySignature ! . DefinitionSignature . ParameterTypes . Should ( ) . HaveCount ( 1 , "Signature should show one parameter" ) ;
475
+ foundBySignature . DefinitionSignature . ParameterTypes [ 0 ] . Should ( ) . Be ( parameterType , "Signature should include correct parameter type" ) ;
476
+ }
477
+
478
+ [ TestMethod ]
479
+ public void TestLargeTupleParameterSignature_15Items ( )
480
+ {
481
+ // Arrange: Create a CQL library with a function that takes a very large tuple parameter (15 items)
482
+ var cqlLibraryString = CqlLibraryString . Parse (
483
+ """
484
+ library VerifyLargeTupleSignatureTest version '1.0.0'
485
+ using FHIR version '4.0.1'
486
+
487
+ context Patient
488
+
489
+ define function "ProcessVeryLargeTuple"(veryLargeTuple Tuple{
490
+ item1 String, item2 Integer, item3 Boolean, item4 Decimal, item5 String,
491
+ item6 Integer, item7 Boolean, item8 Decimal, item9 String, item10 Integer,
492
+ item11 Boolean, item12 Decimal, item13 String, item14 Integer, item15 Boolean
493
+ }): 'Processed 15 items'
494
+ """ ) ;
495
+
496
+ var cqlToolkit = new CqlToolkit ( )
497
+ . AddCqlLibraries ( cqlLibraryString ) ;
498
+
499
+ using var librarySetInvoker = cqlToolkit . CreateLibrarySetInvoker ( ) ;
500
+ var libraryInvoker = librarySetInvoker . LibraryInvokers [ cqlLibraryString ] ;
501
+
502
+ // Act: Find the function and inspect its signature
503
+ var processFunction = libraryInvoker . Definitions . Values
504
+ . FirstOrDefault ( d => d . DefinitionName == "ProcessVeryLargeTuple" ) ;
505
+
506
+ // Assert: Function should be found with very large tuple parameter
507
+ processFunction . Should ( ) . NotBeNull ( "ProcessVeryLargeTuple function should exist" ) ;
508
+ processFunction ! . Parameters . Should ( ) . HaveCount ( 1 , "Function should have exactly one parameter" ) ;
509
+
510
+ var parameterType = processFunction . Parameters [ 0 ] . Type ;
511
+
512
+ // Get underlying type (removing Nullable wrapper if present)
513
+ var underlyingType = parameterType ! . IsGenericType && parameterType . GetGenericTypeDefinition ( ) == typeof ( Nullable < > )
514
+ ? parameterType . GetGenericArguments ( ) [ 0 ]
515
+ : parameterType ;
516
+
517
+ // Verify this is a large tuple that uses nested ValueTuple structures
518
+ underlyingType . IsCqlValueTuple ( ) . Should ( ) . BeTrue ( "15-item tuple parameter should be recognized as a CQL value tuple" ) ;
519
+ typeof ( ITuple ) . IsAssignableFrom ( underlyingType ) . Should ( ) . BeTrue ( "15-item tuple should implement ITuple" ) ;
520
+
521
+ // Verify the function signature can be used for lookup
522
+ var signature = processFunction . DefinitionSignature ;
523
+ var foundBySignature = libraryInvoker . Definitions . GetValueOrDefault ( signature ) ;
524
+
525
+ foundBySignature . Should ( ) . Be ( processFunction , "Should be able to find very large tuple function by its signature" ) ;
526
+
527
+ // The fact that we can create a function with 15 tuple items and find it by signature
528
+ // demonstrates that large tuple signature discovery works correctly with the invocation toolkit
529
+ }
530
+
531
+ [ TestMethod ]
532
+ public void TestMultipleLargeTupleFunctions_SignatureDistinction ( )
533
+ {
534
+ // Arrange: Create CQL library with multiple functions having different large tuple signatures
535
+ var cqlLibraryString = CqlLibraryString . Parse (
536
+ """
537
+ library MultipleLargeTupleFunctionsTest version '1.0.0'
538
+ using FHIR version '4.0.1'
539
+
540
+ context Patient
541
+
542
+ define function "ProcessTuple9"(tuple9 Tuple{
543
+ a String, b Integer, c Boolean, d Decimal, e String,
544
+ f Integer, g Boolean, h Decimal, i String
545
+ }): 'Tuple9 processed'
546
+
547
+ define function "ProcessTuple10"(tuple10 Tuple{
548
+ a String, b Integer, c Boolean, d Decimal, e String,
549
+ f Integer, g Boolean, h Decimal, i String, j Integer
550
+ }): 'Tuple10 processed'
551
+ """ ) ;
552
+
553
+ var cqlToolkit = new CqlToolkit ( )
554
+ . AddCqlLibraries ( cqlLibraryString ) ;
555
+
556
+ using var librarySetInvoker = cqlToolkit . CreateLibrarySetInvoker ( ) ;
557
+ var libraryInvoker = librarySetInvoker . LibraryInvokers [ cqlLibraryString ] ;
558
+
559
+ // Act: Find both functions and verify they have different signatures
560
+ var tuple9Function = libraryInvoker . Definitions . Values
561
+ . FirstOrDefault ( d => d . DefinitionName == "ProcessTuple9" ) ;
562
+ var tuple10Function = libraryInvoker . Definitions . Values
563
+ . FirstOrDefault ( d => d . DefinitionName == "ProcessTuple10" ) ;
564
+
565
+ // Assert: Both functions should be found with distinct signatures
566
+ tuple9Function . Should ( ) . NotBeNull ( "ProcessTuple9 should be found" ) ;
567
+ tuple10Function . Should ( ) . NotBeNull ( "ProcessTuple10 should be found" ) ;
568
+
569
+ tuple9Function ! . Parameters . Should ( ) . HaveCount ( 1 , "ProcessTuple9 should have 1 parameter" ) ;
570
+ tuple10Function ! . Parameters . Should ( ) . HaveCount ( 1 , "ProcessTuple10 should have 1 parameter" ) ;
571
+
572
+ // Verify they have different parameter types (9 vs 10 items)
573
+ var param9Type = tuple9Function . Parameters [ 0 ] . Type ;
574
+ var param10Type = tuple10Function . Parameters [ 0 ] . Type ;
575
+
576
+ param9Type . Should ( ) . NotBe ( param10Type , "9-item and 10-item tuple functions should have different parameter types" ) ;
577
+
578
+ // Both underlying types should be recognized as CQL value tuples
579
+ var underlying9 = param9Type ! . IsGenericType && param9Type . GetGenericTypeDefinition ( ) == typeof ( Nullable < > )
580
+ ? param9Type . GetGenericArguments ( ) [ 0 ] : param9Type ;
581
+ var underlying10 = param10Type ! . IsGenericType && param10Type . GetGenericTypeDefinition ( ) == typeof ( Nullable < > )
582
+ ? param10Type . GetGenericArguments ( ) [ 0 ] : param10Type ;
583
+
584
+ underlying9 . IsCqlValueTuple ( ) . Should ( ) . BeTrue ( "9-item tuple parameter should be recognized as CQL value tuple" ) ;
585
+ underlying10 . IsCqlValueTuple ( ) . Should ( ) . BeTrue ( "10-item tuple parameter should be recognized as CQL value tuple" ) ;
586
+
587
+ // Verify signature distinction works by looking up functions by their complete signatures
588
+ var signature9 = tuple9Function . DefinitionSignature ;
589
+ var signature10 = tuple10Function . DefinitionSignature ;
590
+
591
+ signature9 . Should ( ) . NotBe ( signature10 , "Functions should have distinct signatures" ) ;
592
+
593
+ var foundBySignature9 = libraryInvoker . Definitions . GetValueOrDefault ( signature9 ) ;
594
+ var foundBySignature10 = libraryInvoker . Definitions . GetValueOrDefault ( signature10 ) ;
595
+
596
+ foundBySignature9 . Should ( ) . Be ( tuple9Function , "Should find ProcessTuple9 by its signature" ) ;
597
+ foundBySignature10 . Should ( ) . Be ( tuple10Function , "Should find ProcessTuple10 by its signature" ) ;
598
+
599
+ // This demonstrates that the invocation toolkit can distinguish between
600
+ // different large tuple signatures and find the correct definitions
601
+ }
395
602
}
0 commit comments