Skip to content

Commit 6dbd53c

Browse files
ContractTests: Fixes contract enforcement to be machine agnostic (#5453)
We noticed a few issues in the contracts that made the generation less reliable and a little finnicky, this PR addresses possible different order of certain properties in the contract between .net versions and migrates Encryption.Custom to use only suffixed versions for better clarity. #4678 --------- Co-authored-by: Kiran Kumar Kolli <kirankk@microsoft.com>
1 parent 79adf1b commit 6dbd53c

15 files changed

Lines changed: 951 additions & 741 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
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;
73
using Microsoft.VisualStudio.TestTools.UnitTesting;
84

95
[TestCategory("Windows")]
@@ -18,73 +14,22 @@ public class ContractEnforcementTests
1814
/// conditional compilation (#if NET8_0_OR_GREATER), the API surface differs between
1915
/// .NET versions. Therefore:
2016
///
21-
/// - When running on net6.0: validates against DotNetSDKEncryptionCustomAPI.json
17+
/// - When running on net6.0: validates against DotNetSDKEncryptionCustomAPI.net6.json
2218
/// - When running on net8.0: validates against DotNetSDKEncryptionCustomAPI.net8.json
2319
///
20+
/// There is NO generic fallback - each supported framework MUST have its own baseline.
21+
///
2422
/// To update baselines, run: UpdateContracts.ps1 from the repository root.
2523
/// This script runs tests on BOTH net6.0 and net8.0 to generate both baselines.
2624
/// </summary>
2725
[TestMethod]
2826
public void ContractChanges()
2927
{
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-
53-
Cosmos.Tests.Contracts.ContractEnforcement.ValidateContractContainBreakingChanges(
28+
Cosmos.Tests.Contracts.ContractEnforcement.ValidateContract(
5429
dllName: "Microsoft.Azure.Cosmos.Encryption.Custom",
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;
30+
contractType: Cosmos.Tests.Contracts.ContractType.Standard,
31+
baselinePattern: "DotNetSDKEncryptionCustomAPI",
32+
breakingChangesPattern: "DotNetSDKEncryptionCustomAPIChanges");
8833
}
8934
}
9035
}

Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.json renamed to Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.net6.json

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

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

Lines changed: 55 additions & 55 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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
<ItemGroup>
1515
<Compile Include="..\..\..\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Contracts\ContractEnforcement.cs" Link="Contracts\ContractEnforcement.cs" />
16+
<Compile Include="..\..\..\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Contracts\ContractType.cs" Link="Contracts\ContractType.cs" />
1617
</ItemGroup>
1718

1819
<ItemGroup>
Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
namespace Microsoft.Azure.Cosmos.Encryption.Tests
22
{
3-
using VisualStudio.TestTools.UnitTesting;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
44

55
[TestCategory("Windows")]
66
[TestCategory("UpdateContract")]
77
[TestClass]
88
public class ContractEnforcementTests
99
{
10+
/// <summary>
11+
/// This test validates the public API surface against a baseline contract.
12+
///
13+
/// IMPORTANT: Because tests run on multiple .NET versions (net6.0 and net8.0),
14+
/// the contract validation uses framework-specific baselines to ensure consistency:
15+
///
16+
/// - When running on net6.0: validates against DotNetSDKEncryptionAPI.net6.json
17+
/// - When running on net8.0: validates against DotNetSDKEncryptionAPI.net8.json
18+
///
19+
/// To update baselines, run: UpdateContracts.ps1 from the repository root.
20+
/// This script runs tests on BOTH net6.0 and net8.0 to generate both baselines.
21+
/// </summary>
1022
[TestMethod]
1123
public void ContractChanges()
1224
{
13-
Cosmos.Tests.Contracts.ContractEnforcement.ValidateContractContainBreakingChanges(
25+
Cosmos.Tests.Contracts.ContractEnforcement.ValidateContract(
1426
dllName: "Microsoft.Azure.Cosmos.Encryption",
15-
baselinePath: "DotNetSDKEncryptionAPI.json",
16-
breakingChangesPath: "DotNetSDKEncryptionAPIChanges.json");
27+
contractType: Cosmos.Tests.Contracts.ContractType.Standard,
28+
baselinePattern: "DotNetSDKEncryptionAPI",
29+
breakingChangesPattern: "DotNetSDKEncryptionAPIChanges");
1730
}
1831
}
1932
}

0 commit comments

Comments
 (0)