Skip to content

Commit 2895be2

Browse files
committed
test: CosmosExtension for long values
Added unittests with a Cosmos DB connection. Defaults to use a local Cosmos DB emulator connection with a github action to spin one up.
1 parent 85ff53e commit 2895be2

File tree

4 files changed

+186
-7
lines changed

4 files changed

+186
-7
lines changed
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: .NET test with Cosmos DB Emulator
2+
on:
3+
workflow_dispatch:
4+
jobs:
5+
unit_tests:
6+
name: Run .NET unit tests
7+
runs-on: windows-latest
8+
steps:
9+
- name: Checkout (GitHub)
10+
uses: actions/checkout@v4
11+
- name: Start Azure Cosmos DB emulator
12+
run: |
13+
Write-Host "Launching Cosmos DB Emulator"
14+
Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator"
15+
Start-CosmosDbEmulator
16+
- name: Run .NET tests
17+
run: dotnet test -graphBuild:True .\Extensions\Cosmos\Cosmos.DataTransfer.CosmosExtension.UnitTests\
18+
env:
19+
Cosmos_Endpoint: https://localhost:8081/
20+
Cosmos_Key: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==

Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/Cosmos.DataTransfer.CosmosExtension.UnitTests.csproj

+5-7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
<Nullable>enable</Nullable>
77

88
<IsPackable>false</IsPackable>
9+
<CollectCoverage>true</CollectCoverage>
10+
<CoverletOutputFormat>cobertura</CoverletOutputFormat>
11+
<CoverletOutput>../Cosmos.DataTransfer.CosmosExtension/coverage.cobertura.xml</CoverletOutput>
912
</PropertyGroup>
1013

1114
<ItemGroup>
@@ -22,16 +25,11 @@
2225

2326
<ItemGroup>
2427
<ProjectReference Include="..\Cosmos.DataTransfer.CosmosExtension\Cosmos.DataTransfer.CosmosExtension.csproj" />
28+
<ProjectReference Include="..\..\Json\Cosmos.DataTransfer.JsonExtension.UnitTests\Cosmos.DataTransfer.JsonExtension.UnitTests.csproj" />
2529
</ItemGroup>
2630

2731
<ItemGroup>
28-
<None Update="Data\Nested.json">
29-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
30-
</None>
31-
<None Update="Data\IdName.json">
32-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
33-
</None>
34-
<None Update="Data\MixedTypes.json">
32+
<None Update="Data\*.json">
3533
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
3634
</None>
3735
</ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
using System.Text;
2+
using Cosmos.DataTransfer.Common.UnitTests;
3+
using Microsoft.Extensions.Logging.Abstractions;
4+
using Microsoft.Azure.Cosmos;
5+
using System.Net;
6+
7+
namespace Cosmos.DataTransfer.CosmosExtension.UnitTests;
8+
9+
[TestClass]
10+
public class CosmosDataSourceExtensionTests : IDisposable
11+
{
12+
private static readonly Database testDatabase;
13+
private static readonly CosmosClient cosmosClient;
14+
private static readonly string connectionString;
15+
private static readonly Action dismantleTestDb = () => {};
16+
private static readonly bool runTests = false;
17+
18+
// Sets up a connection to CosmosDB.
19+
// Default values here are to a local Cosmos DB emulator.
20+
// Use the two environment variables to pass a custom connection.
21+
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
22+
static CosmosDataSourceExtensionTests() {
23+
string? endpoint = Environment.GetEnvironmentVariable("Cosmos_Endpoint");
24+
string? accountKey = Environment.GetEnvironmentVariable("Cosmos_Key");
25+
26+
if (endpoint is null || accountKey is null) {
27+
Console.WriteLine("Connection details for Cosmos DB not found. Ignoring tests...");
28+
return;
29+
}
30+
31+
connectionString = $"AccountEndpoint={endpoint};AccountKey={accountKey};";
32+
33+
var fullname = typeof(CosmosDataSourceExtensionTests).Assembly.ManifestModule.Name;
34+
var dbname = typeof(CosmosDataSourceExtensionTests).Name + "-" + Guid.NewGuid().ToString().Substring(0, 8);
35+
36+
CosmosClient client = new(
37+
accountEndpoint: endpoint,
38+
authKeyOrResourceToken: accountKey,
39+
new CosmosClientOptions {
40+
ApplicationName = fullname
41+
}
42+
);
43+
cosmosClient = client;
44+
45+
DatabaseResponse db;
46+
try {
47+
_ = client.ReadAccountAsync().Result;
48+
db = client.CreateDatabaseAsync(dbname, (int?)null, new RequestOptions()).Result;
49+
} catch (Exception) {
50+
Console.WriteLine("Issues encountered connecting to Cosmos DB. Ignoring these tests...");
51+
return;
52+
}
53+
testDatabase = db.Database;
54+
runTests = true;
55+
dismantleTestDb = () => {
56+
57+
};
58+
}
59+
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
60+
61+
[ClassCleanup]
62+
public static void CleanUp() {
63+
if (runTests) {
64+
try {
65+
testDatabase.DeleteAsync().Wait();
66+
} catch (CosmosException e) when (e.StatusCode == HttpStatusCode.NotFound) {
67+
// Ignore; the database has already been deleted.
68+
} catch (AggregateException e) when (e.InnerException is CosmosException ce
69+
&& ce.StatusCode == HttpStatusCode.NotFound)
70+
{
71+
// Ignore; the database has already been deleted.
72+
}
73+
}
74+
}
75+
76+
public void Dispose()
77+
{
78+
CleanUp();
79+
}
80+
81+
private static (CosmosClient client, Database database, string connectionString) GetSetup() {
82+
if (!runTests) {
83+
throw new AssertInconclusiveException("Cosmos DB not active");
84+
}
85+
return (cosmosClient, testDatabase, connectionString);
86+
}
87+
88+
89+
[TestMethod]
90+
public async Task Test_Json_RoundTrip_WithLongValues() {
91+
var (_, database, connectionString) = GetSetup();
92+
var config = TestHelpers.CreateConfig(new Dictionary<string,string>() {
93+
{ "UseRbacAuth", "false" },
94+
{ "ConnectionString", connectionString },
95+
{ "Database", database.Id },
96+
{ "Container", "Test_Json_RoundTrip_WithLongValues" },
97+
{ "RecreateContainer", "true" },
98+
{ "PartitionKeyPath", "/partitionKey" }
99+
});
100+
101+
var sourceExtension = new CosmosDataSourceExtension();
102+
var sinkExtension = new CosmosDataSinkExtension();
103+
var items = new CosmosDictionaryDataItem[] {
104+
new CosmosDictionaryDataItem(new Dictionary<string, object?> {
105+
{ "partitionKey", "pk" },
106+
{ "id", "1" },
107+
{ "long", 638676324052177500L }
108+
})
109+
};
110+
111+
await sinkExtension.WriteAsync(items.ToAsyncEnumerable(), config,
112+
sourceExtension, NullLogger.Instance);
113+
114+
var fetched = await sourceExtension.ReadAsync(config, NullLogger.Instance).ToArrayAsync();
115+
Assert.AreEqual(1, fetched.Length);
116+
Assert.IsInstanceOfType(fetched[0].GetValue("long"), typeof(long));
117+
Assert.AreEqual(items[0].Items["long"], fetched[0].GetValue("long"));
118+
119+
var jsonSink = new JsonExtension.JsonFormatWriter();
120+
121+
var writer = new MemoryStream(500);
122+
await jsonSink.FormatDataAsync(fetched.ToAsyncEnumerable(), writer, config, NullLogger.Instance);
123+
var res = Encoding.UTF8.GetString(writer.ToArray());
124+
Assert.AreEqual("[{\"partitionKey\":\"pk\",\"id\":\"1\",\"long\":638676324052177500}]", res);
125+
}
126+
127+
[TestMethod]
128+
public async Task Test_FromJson_WithLongValues() {
129+
var (_, database, connectionString) = GetSetup();
130+
var config = TestHelpers.CreateConfig(new Dictionary<string,string>() {
131+
{ "UseRbacAuth", "false" },
132+
{ "ConnectionString", connectionString },
133+
{ "Database", database.Id },
134+
{ "Container", "Test_FromJson_WithLongValues" },
135+
{ "RecreateContainer", "true" },
136+
{ "PartitionKeyPath", "/partitionKey" },
137+
{ "FilePath", "Data/LongValue.json" }
138+
});
139+
140+
var jsonSource = new JsonExtension.JsonFormatReader();
141+
var cosmosSink = new CosmosDataSinkExtension();
142+
var cosmosSource = new CosmosDataSourceExtension();
143+
144+
var fileSource = new Common.FileDataSource();
145+
146+
await cosmosSink.WriteAsync(jsonSource.ParseDataAsync(fileSource, config, NullLogger.Instance),
147+
config, new JsonExtension.JsonFileSource(), NullLogger.Instance);
148+
149+
var fetched = await cosmosSource.ReadAsync(config, NullLogger.Instance).ToArrayAsync();
150+
Assert.IsInstanceOfType(fetched[0].GetValue("long"), typeof(long));
151+
Assert.AreEqual(638676324052177500, fetched[0].GetValue("long"));
152+
}
153+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"id": "1",
4+
"partitionKey": "1",
5+
"string": "Hello world!",
6+
"long": 638676324052177500
7+
}
8+
]

0 commit comments

Comments
 (0)