Skip to content

Commit 29a601e

Browse files
kirankumarkolliadamnovaNaluTripician
authored
ContractTests: Fixes failing contract tests on .NET 8 (#5442)
# Pull Request Template ## Description Please include a summary of the change and which issue is fixed. Include samples if adding new API, and include relevant motivation and context. List any dependencies that are required for this change. ## Type of change Please delete options that are not relevant. - [] Bug fix (non-breaking change which fixes an issue) - [] New feature (non-breaking change which adds functionality) - [] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [] This change requires a documentation update ## Closing issues To automatically close an issue: closes #IssueNumber --------- Co-authored-by: Adam Novak <125261085+adamnova@users.noreply.github.com> Co-authored-by: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com>
1 parent 73b7f29 commit 29a601e

10 files changed

Lines changed: 1884 additions & 26 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -1,19 +1,90 @@
11
namespace Microsoft.Azure.Cosmos.Encryption.Tests.Contracts
22
{
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using System.Runtime.Versioning;
37
using Microsoft.VisualStudio.TestTools.UnitTesting;
48

59
[TestCategory("Windows")]
610
[TestCategory("UpdateContract")]
711
[TestClass]
812
public class ContractEnforcementTests
913
{
14+
/// <summary>
15+
/// This test validates the public API surface against a baseline contract.
16+
///
17+
/// IMPORTANT: Because the library multi-targets (netstandard2.0 + net8.0) and uses
18+
/// conditional compilation (#if NET8_0_OR_GREATER), the API surface differs between
19+
/// .NET versions. Therefore:
20+
///
21+
/// - When running on net6.0: validates against DotNetSDKEncryptionCustomAPI.json
22+
/// - When running on net8.0: validates against DotNetSDKEncryptionCustomAPI.net8.json
23+
///
24+
/// To update baselines, run: UpdateContracts.ps1 from the repository root.
25+
/// This script runs tests on BOTH net6.0 and net8.0 to generate both baselines.
26+
/// </summary>
1027
[TestMethod]
1128
public void ContractChanges()
1229
{
30+
int? currentMajorVersion = GetCurrentMajorVersion();
31+
32+
// Resolve a framework-specific baseline if available; otherwise fall back to the generic baseline.
33+
string baseline = ResolveFrameworkSpecificBaseline(
34+
baseFileName: "DotNetSDKEncryptionCustomAPI",
35+
defaultFileName: "DotNetSDKEncryptionCustomAPI.json",
36+
currentMajorVersion: currentMajorVersion);
37+
38+
// Validate that the baseline file exists
39+
string baselinePath = Path.Combine("Contracts", baseline);
40+
if (!File.Exists(baselinePath))
41+
{
42+
Assert.Fail($"Baseline file not found: {baselinePath}. " +
43+
$"This indicates the baseline for the current target framework (.NET {currentMajorVersion ?? 6}) is missing. " +
44+
$"Run UpdateContracts.ps1 from the repository root to generate all required baseline files.");
45+
}
46+
47+
// For breaking changes output, always use framework-specific name if we have a version
48+
// (doesn't need to exist yet since the test creates it)
49+
string breakingChangesPath = currentMajorVersion.HasValue
50+
? $"DotNetSDKEncryptionCustomAPIChanges.net{currentMajorVersion}.json"
51+
: "DotNetSDKEncryptionCustomAPIChanges.json";
52+
1353
Cosmos.Tests.Contracts.ContractEnforcement.ValidateContractContainBreakingChanges(
1454
dllName: "Microsoft.Azure.Cosmos.Encryption.Custom",
15-
baselinePath: "DotNetSDKEncryptionCustomAPI.json",
16-
breakingChangesPath: "DotNetSDKEncryptionCustomAPIChanges.json");
55+
baselinePath: baseline,
56+
breakingChangesPath: breakingChangesPath);
57+
}
58+
59+
private static string ResolveFrameworkSpecificBaseline(string baseFileName, string defaultFileName, int? currentMajorVersion)
60+
{
61+
string contractsDir = "Contracts";
62+
string[] candidates = {
63+
currentMajorVersion is null ? null : $"{baseFileName}.net{currentMajorVersion}.json",
64+
defaultFileName
65+
};
66+
67+
string existing = candidates
68+
.Where(name => !string.IsNullOrWhiteSpace(name))
69+
.Select(name => Path.Combine(contractsDir, name))
70+
.FirstOrDefault(File.Exists);
71+
72+
// Return just the file name as expected by ContractEnforcement
73+
return existing != null ? Path.GetFileName(existing) : defaultFileName;
74+
}
75+
76+
private static int? GetCurrentMajorVersion()
77+
{
78+
// Read the TFM from the current test assembly TargetFrameworkAttribute
79+
TargetFrameworkAttribute attr = Assembly.GetExecutingAssembly().GetCustomAttribute<TargetFrameworkAttribute>();
80+
if (attr?.FrameworkName == null)
81+
{
82+
return null;
83+
}
84+
85+
// Example: ".NETCoreApp,Version=v8.0" -> 8
86+
FrameworkName fx = new FrameworkName(attr.FrameworkName);
87+
return fx.Version.Major;
1788
}
1889
}
1990
}

Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.net8.json

Lines changed: 1355 additions & 0 deletions
Large diffs are not rendered by default.

Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
</ItemGroup>
2828

2929
<ItemGroup>
30-
<None Include="Contracts\DotNetSDKEncryptionCustomAPI.json">
30+
<None Include="Contracts\DotNetSDKEncryptionCustomAPI*.json">
3131
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
3232
</None>
3333
</ItemGroup>

Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/ContractEnforcementTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
namespace Microsoft.Azure.Cosmos.Encryption.Tests
22
{
3-
using System;
4-
using Microsoft.VisualStudio.TestTools.UnitTesting;
5-
3+
using VisualStudio.TestTools.UnitTesting;
4+
65
[TestCategory("Windows")]
76
[TestCategory("UpdateContract")]
87
[TestClass]
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
{
2+
"Subclasses": {
3+
"Microsoft.Azure.Cosmos.Encryption.DataEncryptionAlgorithm;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
4+
"Subclasses": {},
5+
"Members": {
6+
"System.String AeadAes256CbcHmacSha256": {
7+
"Type": "Field",
8+
"Attributes": [],
9+
"MethodInfo": "System.String AeadAes256CbcHmacSha256;IsInitOnly:False;IsStatic:True;"
10+
}
11+
},
12+
"NestedTypes": {}
13+
},
14+
"Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
15+
"Subclasses": {},
16+
"Members": {
17+
"Microsoft.Azure.Cosmos.FeedIterator ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": {
18+
"Type": "Method",
19+
"Attributes": [
20+
"ExtensionAttribute"
21+
],
22+
"MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;"
23+
},
24+
"Microsoft.Azure.Cosmos.FeedIterator`1[T] ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": {
25+
"Type": "Method",
26+
"Attributes": [
27+
"ExtensionAttribute"
28+
],
29+
"MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator`1[T] ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;"
30+
},
31+
"Microsoft.Azure.Cosmos.QueryDefinition CreateQueryDefinition(Microsoft.Azure.Cosmos.Container, System.String)[System.Runtime.CompilerServices.ExtensionAttribute()]": {
32+
"Type": "Method",
33+
"Attributes": [
34+
"ExtensionAttribute"
35+
],
36+
"MethodInfo": "Microsoft.Azure.Cosmos.QueryDefinition CreateQueryDefinition(Microsoft.Azure.Cosmos.Container, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
37+
},
38+
"System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Container] InitializeEncryptionAsync(Microsoft.Azure.Cosmos.Container, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions+<InitializeEncryptionAsync>))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": {
39+
"Type": "Method",
40+
"Attributes": [
41+
"AsyncStateMachineAttribute",
42+
"ExtensionAttribute"
43+
],
44+
"MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Container] InitializeEncryptionAsync(Microsoft.Azure.Cosmos.Container, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
45+
}
46+
},
47+
"NestedTypes": {}
48+
},
49+
"Microsoft.Azure.Cosmos.Encryption.EncryptionCosmosClientExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
50+
"Subclasses": {},
51+
"Members": {
52+
"Microsoft.Azure.Cosmos.CosmosClient WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Azure.Core.Cryptography.IKeyEncryptionKeyResolver, System.String, System.Nullable`1[System.TimeSpan])[System.Runtime.CompilerServices.ExtensionAttribute()]": {
53+
"Type": "Method",
54+
"Attributes": [
55+
"ExtensionAttribute"
56+
],
57+
"MethodInfo": "Microsoft.Azure.Cosmos.CosmosClient WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Azure.Core.Cryptography.IKeyEncryptionKeyResolver, System.String, System.Nullable`1[System.TimeSpan]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
58+
}
59+
},
60+
"NestedTypes": {}
61+
},
62+
"Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
63+
"Subclasses": {},
64+
"Members": {
65+
"System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] CreateClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions+<CreateClientEncryptionKeyAsync>))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": {
66+
"Type": "Method",
67+
"Attributes": [
68+
"AsyncStateMachineAttribute",
69+
"ExtensionAttribute"
70+
],
71+
"MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] CreateClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
72+
},
73+
"System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] RewrapClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions+<RewrapClientEncryptionKeyAsync>))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": {
74+
"Type": "Method",
75+
"Attributes": [
76+
"AsyncStateMachineAttribute",
77+
"ExtensionAttribute"
78+
],
79+
"MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] RewrapClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
80+
}
81+
},
82+
"NestedTypes": {}
83+
},
84+
"Microsoft.Azure.Cosmos.Encryption.EncryptionType;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
85+
"Subclasses": {},
86+
"Members": {
87+
"System.String Deterministic": {
88+
"Type": "Field",
89+
"Attributes": [],
90+
"MethodInfo": "System.String Deterministic;IsInitOnly:False;IsStatic:True;"
91+
},
92+
"System.String Randomized": {
93+
"Type": "Field",
94+
"Attributes": [],
95+
"MethodInfo": "System.String Randomized;IsInitOnly:False;IsStatic:True;"
96+
}
97+
},
98+
"NestedTypes": {}
99+
},
100+
"Microsoft.Azure.Cosmos.Encryption.KeyEncryptionKeyResolverName;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
101+
"Subclasses": {},
102+
"Members": {
103+
"System.String AzureKeyVault": {
104+
"Type": "Field",
105+
"Attributes": [],
106+
"MethodInfo": "System.String AzureKeyVault;IsInitOnly:False;IsStatic:True;"
107+
}
108+
},
109+
"NestedTypes": {}
110+
},
111+
"Microsoft.Azure.Cosmos.Encryption.QueryDefinitionExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
112+
"Subclasses": {},
113+
"Members": {
114+
"System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.QueryDefinition] AddParameterAsync(Microsoft.Azure.Cosmos.QueryDefinition, System.String, System.Object, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.QueryDefinitionExtensions+<AddParameterAsync>))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": {
115+
"Type": "Method",
116+
"Attributes": [
117+
"AsyncStateMachineAttribute",
118+
"ExtensionAttribute"
119+
],
120+
"MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.QueryDefinition] AddParameterAsync(Microsoft.Azure.Cosmos.QueryDefinition, System.String, System.Object, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
121+
}
122+
},
123+
"NestedTypes": {}
124+
}
125+
},
126+
"Members": {
127+
"Boolean Equals(System.Object, System.Object)": {
128+
"Type": "Method",
129+
"Attributes": [],
130+
"MethodInfo": "Boolean Equals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
131+
},
132+
"Boolean Equals(System.Object)": {
133+
"Type": "Method",
134+
"Attributes": [],
135+
"MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
136+
},
137+
"Boolean ReferenceEquals(System.Object, System.Object)": {
138+
"Type": "Method",
139+
"Attributes": [],
140+
"MethodInfo": "Boolean ReferenceEquals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
141+
},
142+
"Int32 GetHashCode()": {
143+
"Type": "Method",
144+
"Attributes": [],
145+
"MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
146+
},
147+
"System.String ToString()": {
148+
"Type": "Method",
149+
"Attributes": [],
150+
"MethodInfo": "System.String ToString();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
151+
},
152+
"System.Type GetType()[System.Runtime.CompilerServices.IntrinsicAttribute()]": {
153+
"Type": "Method",
154+
"Attributes": [
155+
"IntrinsicAttribute"
156+
],
157+
"MethodInfo": "System.Type GetType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
158+
},
159+
"Void .ctor()": {
160+
"Type": "Constructor",
161+
"Attributes": [],
162+
"MethodInfo": "[Void .ctor(), Void .ctor()]"
163+
}
164+
},
165+
"NestedTypes": {}
166+
}

0 commit comments

Comments
 (0)