Skip to content

Commit 705dcff

Browse files
authored
Merge pull request #929 from FirelyTeam/copilot/fix-923-2
923 Make original cql operand name accessible through DefinitionInvoker
2 parents fd69ac3 + 968566f commit 705dcff

22 files changed

+577
-297
lines changed

copilot-instructions.md renamed to .github/copilot-instructions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ Use this header format with "Firely, NCQA":
8282
6. **When adding new utility files or functionality that are only used internally, keep those types as `internal`, not `public`** - Only expose public APIs when they are intended for external consumption
8383
7. **When creating new files or modifying existing ones, always remove unused usings at the top of the file** - Keep using statements clean and only include what is actually used
8484
8. **Do not add duplicate usings in files where the using is already included globally in `GlobalUsings.cs`** - Check GlobalUsings.cs first to avoid redundant using statements
85+
9. **Always use the latest C# language features** when appropriate:
86+
- Use collection expressions `[]` instead of `new[] { ... }` for arrays and collections
87+
- Use target-typed `new()` expressions when the type is clear from context
88+
- Use pattern matching and switch expressions where applicable
89+
- Use record types for immutable data structures
90+
- Use nullable reference types and null-conditional operators
91+
- Use string interpolation instead of `string.Format` or concatenation
8592

8693
### Project References
8794
- When adding internal access, ensure the requesting project is appropriate for internal API usage

Cql-Sdk-All.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
131131
build-elm.ps1 = build-elm.ps1
132132
build-local-nuget.ps1 = build-local-nuget.ps1
133133
build-publicapi.ps1 = build-publicapi.ps1
134-
copilot-instructions.md = copilot-instructions.md
134+
.github\copilot-instructions.md = .github\copilot-instructions.md
135135
LICENSE = LICENSE
136136
NuGet.config = NuGet.config
137137
README.md = README.md

Cql/CoreTests/AssertExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* available at https://raw.githubusercontent.com/FirelyTeam/firely-cql-sdk/main/LICENSE
77
*/
88
#nullable enable
9+
using System.Diagnostics;
910
using System.Text.RegularExpressions;
1011

1112
namespace CoreTests;
@@ -26,6 +27,7 @@ public static void DoesNotThrow(
2627
}
2728
}
2829

30+
[DebuggerStepThrough]
2931
public static void MultilinesAreEqual(
3032
this Assert assert,
3133
string expected,
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/*
2+
* Copyright (c) 2025, Firely, NCQA and contributors
3+
* See the file CONTRIBUTORS for details.
4+
*
5+
* This file is licensed under the BSD 3-Clause license
6+
* available at https://raw.githubusercontent.com/FirelyTeam/firely-cql-sdk/main/LICENSE
7+
*/
8+
9+
using Hl7.Cql.Runtime;
10+
11+
namespace CoreTests;
12+
13+
[TestClass]
14+
public class DefinitionSignatureTests
15+
{
16+
[TestMethod]
17+
public void Constructor_WithValidNameAndTypes_ShouldCreateInstance()
18+
{
19+
// Arrange
20+
const string name = "TestFunction";
21+
var paramTypes = new[] { typeof(int), typeof(string) };
22+
23+
// Act
24+
var signature = new DefinitionSignature(name, paramTypes);
25+
26+
// Assert
27+
Assert.AreEqual(name, signature.Name);
28+
CollectionAssert.AreEqual(paramTypes, signature.ParameterTypes);
29+
}
30+
31+
[TestMethod]
32+
public void Constructor_WithValidNameAndNoTypes_ShouldCreateInstance()
33+
{
34+
// Arrange
35+
const string name = "TestFunction";
36+
37+
// Act
38+
var signature = new DefinitionSignature(name);
39+
40+
// Assert
41+
Assert.AreEqual(name, signature.Name);
42+
Assert.AreEqual(0, signature.ParameterTypes.Length);
43+
}
44+
45+
[TestMethod]
46+
public void Constructor_WithNullName_ShouldThrowArgumentNullException()
47+
{
48+
// Act & Assert
49+
Assert.ThrowsException<ArgumentNullException>(() => new DefinitionSignature(null!, typeof(int)));
50+
}
51+
52+
[TestMethod]
53+
public void Constructor_WithNullParameterTypes_ShouldCreateEmptyArray()
54+
{
55+
// Arrange
56+
const string name = "TestFunction";
57+
58+
// Act
59+
var signature = new DefinitionSignature(name, null!);
60+
61+
// Assert
62+
Assert.AreEqual(name, signature.Name);
63+
Assert.AreEqual(0, signature.ParameterTypes.Length);
64+
}
65+
66+
[TestMethod]
67+
public void Equals_WithSameNameAndTypes_ShouldReturnTrue()
68+
{
69+
// Arrange
70+
var signature1 = new DefinitionSignature("TestFunction", typeof(int), typeof(string));
71+
var signature2 = new DefinitionSignature("TestFunction", typeof(int), typeof(string));
72+
73+
// Act & Assert
74+
Assert.IsTrue(signature1.Equals(signature2));
75+
Assert.IsTrue(signature1 == signature2);
76+
Assert.AreEqual(signature1.GetHashCode(), signature2.GetHashCode());
77+
}
78+
79+
[TestMethod]
80+
public void Equals_WithDifferentNames_ShouldReturnFalse()
81+
{
82+
// Arrange
83+
var signature1 = new DefinitionSignature("TestFunction1", typeof(int));
84+
var signature2 = new DefinitionSignature("TestFunction2", typeof(int));
85+
86+
// Act & Assert
87+
Assert.IsFalse(signature1.Equals(signature2));
88+
Assert.IsFalse(signature1 == signature2);
89+
}
90+
91+
[TestMethod]
92+
public void Equals_WithDifferentParameterTypes_ShouldReturnFalse()
93+
{
94+
// Arrange
95+
var signature1 = new DefinitionSignature("TestFunction", typeof(int));
96+
var signature2 = new DefinitionSignature("TestFunction", typeof(string));
97+
98+
// Act & Assert
99+
Assert.IsFalse(signature1.Equals(signature2));
100+
Assert.IsFalse(signature1 == signature2);
101+
}
102+
103+
[TestMethod]
104+
public void Equals_WithDifferentParameterCount_ShouldReturnFalse()
105+
{
106+
// Arrange
107+
var signature1 = new DefinitionSignature("TestFunction", typeof(int));
108+
var signature2 = new DefinitionSignature("TestFunction", typeof(int), typeof(string));
109+
110+
// Act & Assert
111+
Assert.IsFalse(signature1.Equals(signature2));
112+
Assert.IsFalse(signature1 == signature2);
113+
}
114+
115+
[TestMethod]
116+
public void GetHashCode_WithSameContent_ShouldBeSame()
117+
{
118+
// Arrange
119+
var signature1 = new DefinitionSignature("TestFunction", typeof(int), typeof(string));
120+
var signature2 = new DefinitionSignature("TestFunction", typeof(int), typeof(string));
121+
122+
// Act & Assert
123+
Assert.AreEqual(signature1.GetHashCode(), signature2.GetHashCode());
124+
}
125+
126+
[TestMethod]
127+
public void GetHashCode_WithDifferentContent_ShouldBeDifferent()
128+
{
129+
// Arrange
130+
var signature1 = new DefinitionSignature("TestFunction1", typeof(int));
131+
var signature2 = new DefinitionSignature("TestFunction2", typeof(int));
132+
133+
// Act & Assert
134+
Assert.AreNotEqual(signature1.GetHashCode(), signature2.GetHashCode());
135+
}
136+
137+
[TestMethod]
138+
public void GetHashCode_ShouldBeCached()
139+
{
140+
// Arrange
141+
var signature = new DefinitionSignature("TestFunction", typeof(int), typeof(string));
142+
143+
// Act
144+
var hash1 = signature.GetHashCode();
145+
var hash2 = signature.GetHashCode();
146+
147+
// Assert
148+
Assert.AreEqual(hash1, hash2);
149+
}
150+
151+
[TestMethod]
152+
public void ToString_WithNoParameters_ShouldReturnNameOnly()
153+
{
154+
// Arrange
155+
var signature = new DefinitionSignature("TestFunction");
156+
157+
// Act
158+
var result = signature.ToString();
159+
160+
// Assert
161+
Assert.AreEqual("TestFunction()", result);
162+
}
163+
164+
[TestMethod]
165+
public void ToString_WithParameters_ShouldIncludeParameterTypes()
166+
{
167+
// Arrange
168+
var signature = new DefinitionSignature("TestFunction", typeof(int), typeof(string));
169+
170+
// Act
171+
var result = signature.ToString();
172+
173+
// Assert
174+
Assert.AreEqual("TestFunction(System.Int32, System.String)", result);
175+
}
176+
177+
[TestMethod]
178+
public void ImplicitConversion_FromString_ShouldCreateSignature()
179+
{
180+
// Arrange
181+
const string functionName = "TestFunction";
182+
183+
// Act
184+
DefinitionSignature signature = functionName;
185+
186+
// Assert
187+
Assert.AreEqual(functionName, signature.Name);
188+
Assert.AreEqual(0, signature.ParameterTypes.Length);
189+
}
190+
191+
[TestMethod]
192+
public void Deconstruct_ShouldProvideNameAndParameterTypes()
193+
{
194+
// Arrange
195+
var signature = new DefinitionSignature("TestFunction", typeof(int), typeof(string));
196+
197+
// Act
198+
var (name, parameterTypes) = signature;
199+
200+
// Assert
201+
Assert.AreEqual("TestFunction", name);
202+
CollectionAssert.AreEqual(new[] { typeof(int), typeof(string) }, parameterTypes);
203+
}
204+
}

Cql/CoreTests/LibraryInvokerTestExtensions.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212

1313
namespace CoreTests;
1414

15-
internal static class LibraryInvokerTestExtensions
15+
internal static class LibraryInvokerTestExtensions // These are extensions used in other tests
1616
{
1717
/// <summary>
18-
/// Invokes the delegate <paramref name="define"/> in <paramref name="libraryName"/> with <paramref name="parameters"/>.
18+
/// Invokes the delegate <paramref name="define"/> in <paramref name="libraryInvoker"/> with <paramref name="parameters"/>.
1919
/// </summary>
2020
/// <typeparam name="T">The expected return type of the delegate.</typeparam>
2121
/// <param name="libraryInvoker">The delegates containing this definition.</param>
@@ -30,8 +30,14 @@ internal static class LibraryInvokerTestExtensions
3030
params object[] parameters)
3131
{
3232
var parameterTypes = parameters.Select(p => p.GetType()).ToArray();
33+
34+
// Find the definition that matches the name and parameter types
3335
var definitionSignature = new DefinitionSignature(define, parameterTypes);
34-
var definition = libraryInvoker.Definitions[definitionSignature];
36+
var definition = libraryInvoker.Definitions.GetValueOrDefault(definitionSignature);
37+
38+
if (definition == null)
39+
throw new InvalidOperationException($"No definition found with name '{define}' and parameter types [{string.Join(", ", parameterTypes.Select(t => t.Name))}]");
40+
3541
var resultObj = definition.Invoke(rtx, parameters);
3642
var result = (T?)resultObj;
3743
return result;

Cql/CoreTests/ParameterNameAttributeTests.cs

Lines changed: 0 additions & 107 deletions
This file was deleted.

0 commit comments

Comments
 (0)