Skip to content

Commit ea26d29

Browse files
[Internal] Thin Client Integration: Adds runner tests for thinclient test scenarios. (#5195)
# Pull Request Template ## Description Adds pipeline to allow integration tests to run for thinclient test scenarios. Creating a separate pipeline which does not impact the main PR pipeline as the thinclient tests are still unstable. Adding new endpoint variable dotnet-v3-CI pipeline $(COSMOSDB_THINCLIENT). Test Results: - When Thinclient category tests are failing, it does not impact the entire pipeline(optional) and allows PR merge. https://cosmos-db-sdk-public.visualstudio.com/cosmos-db-sdk-public/_build/results?buildId=55136&view=results <img width="578" alt="image" src="https://github.com/user-attachments/assets/1545978c-f666-4dee-b198-cff8d55c1d4e" /> - Pipeline failing when tests other than thinclient tests fail. <img width="598" alt="image" src="https://github.com/user-attachments/assets/2049bc2e-c2ef-4fb8-be0f-303ac45b7ec5" /> ## Type of change Please delete options that are not relevant. - [X] New feature (non-breaking change which adds functionality) ## Closing issues To automatically close an issue: closes #5144 --------- Co-authored-by: Debdatta Kunda <87335885+kundadebdatta@users.noreply.github.com>
1 parent d5d16df commit ea26d29

4 files changed

Lines changed: 423 additions & 2 deletions

File tree

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
//------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
//------------------------------------------------------------
4+
5+
namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
6+
{
7+
using System;
8+
using System.Collections.Generic;
9+
using System.IO;
10+
using System.Linq;
11+
using System.Net;
12+
using System.Text.Json;
13+
using System.Text.Json.Serialization;
14+
using System.Threading.Tasks;
15+
using Microsoft.VisualStudio.TestTools.UnitTesting;
16+
using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.MultiRegionSetupHelpers;
17+
using TestObject = MultiRegionSetupHelpers.CosmosIntegrationTestObject;
18+
19+
[TestClass]
20+
public class CosmosItemThinClientTests
21+
{
22+
private string connectionString;
23+
private CosmosClient client;
24+
private Database database;
25+
private Container container;
26+
private CosmosSystemTextJsonSerializer cosmosSystemTextJsonSerializer;
27+
28+
private const int ItemCount = 1000;
29+
30+
[TestInitialize]
31+
public async Task TestInitAsync()
32+
{
33+
this.connectionString = Environment.GetEnvironmentVariable("COSMOSDB_THINCLIENT");
34+
Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True");
35+
36+
if (string.IsNullOrEmpty(this.connectionString))
37+
{
38+
Assert.Fail("Set environment variable COSMOSDB_THINCLIENT to run the tests");
39+
}
40+
41+
JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
42+
{
43+
PropertyNamingPolicy = null,
44+
PropertyNameCaseInsensitive = true,
45+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
46+
};
47+
this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions);
48+
49+
this.client = new CosmosClient(
50+
this.connectionString,
51+
new CosmosClientOptions()
52+
{
53+
ConnectionMode = ConnectionMode.Gateway,
54+
Serializer = this.cosmosSystemTextJsonSerializer,
55+
});
56+
57+
string uniqueDbName = "TestDb_" + Guid.NewGuid().ToString();
58+
this.database = await this.client.CreateDatabaseIfNotExistsAsync(uniqueDbName);
59+
string uniqueContainerName = "TestContainer_" + Guid.NewGuid().ToString();
60+
this.container = await this.database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk");
61+
}
62+
63+
64+
[TestCleanup]
65+
public async Task TestCleanupAsync()
66+
{
67+
Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "False");
68+
69+
if (this.database != null)
70+
{
71+
await this.database.DeleteAsync();
72+
}
73+
74+
this.client?.Dispose();
75+
}
76+
77+
private IEnumerable<TestObject> GenerateItems(string partitionKey)
78+
{
79+
List<TestObject> items = new List<TestObject>();
80+
for (int i = 0; i < ItemCount; i++)
81+
{
82+
items.Add(new TestObject
83+
{
84+
Id = Guid.NewGuid().ToString(),
85+
Pk = partitionKey,
86+
Other = "Test Item " + i
87+
});
88+
}
89+
90+
return items;
91+
}
92+
93+
[TestMethod]
94+
[TestCategory("ThinClient")]
95+
public async Task CreateItemsTest()
96+
{
97+
string pk = "pk_create";
98+
IEnumerable<TestObject> items = this.GenerateItems(pk);
99+
100+
foreach (TestObject item in items)
101+
{
102+
ItemResponse<TestObject> response = await this.container.CreateItemAsync(item, new PartitionKey(item.Pk));
103+
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
104+
}
105+
}
106+
107+
[TestMethod]
108+
[TestCategory("ThinClient")]
109+
public async Task ReadItemsTest()
110+
{
111+
string pk = "pk_read";
112+
List<TestObject> items = this.GenerateItems(pk).ToList();
113+
114+
foreach (TestObject item in items)
115+
{
116+
await this.container.CreateItemAsync(item, new PartitionKey(item.Pk));
117+
}
118+
119+
foreach (TestObject item in items)
120+
{
121+
ItemResponse<TestObject> response = await this.container.ReadItemAsync<TestObject>(item.Id, new PartitionKey(item.Pk));
122+
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
123+
Assert.AreEqual(item.Id, response.Resource.Id);
124+
}
125+
}
126+
127+
[TestMethod]
128+
[TestCategory("ThinClient")]
129+
public async Task ReplaceItemsTest()
130+
{
131+
string pk = "pk_replace";
132+
List<TestObject> items = this.GenerateItems(pk).ToList();
133+
134+
foreach (TestObject item in items)
135+
{
136+
await this.container.CreateItemAsync(item, new PartitionKey(item.Pk));
137+
}
138+
139+
foreach (TestObject item in items)
140+
{
141+
TestObject updatedItem = new TestObject
142+
{
143+
Id = item.Id,
144+
Pk = item.Pk,
145+
Other = "Updated " + item.Other
146+
};
147+
148+
ItemResponse<TestObject> response = await this.container.ReplaceItemAsync(updatedItem, updatedItem.Id, new PartitionKey(updatedItem.Pk));
149+
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
150+
Assert.AreEqual("Updated " + item.Other, response.Resource.Other);
151+
}
152+
}
153+
154+
[TestMethod]
155+
[TestCategory("ThinClient")]
156+
public async Task UpsertItemsTest()
157+
{
158+
string pk = "pk_upsert";
159+
IEnumerable<TestObject> items = this.GenerateItems(pk);
160+
161+
foreach (TestObject item in items)
162+
{
163+
ItemResponse<TestObject> response = await this.container.UpsertItemAsync(item, new PartitionKey(item.Pk));
164+
Assert.IsTrue(response.StatusCode == HttpStatusCode.Created || response.StatusCode == HttpStatusCode.OK);
165+
}
166+
}
167+
168+
[TestMethod]
169+
[TestCategory("ThinClient")]
170+
public async Task DeleteItemsTest()
171+
{
172+
string pk = "pk_delete";
173+
List<TestObject> items = this.GenerateItems(pk).ToList();
174+
175+
foreach (TestObject item in items)
176+
{
177+
await this.container.CreateItemAsync(item, new PartitionKey(item.Pk));
178+
}
179+
180+
foreach (TestObject item in items)
181+
{
182+
ItemResponse<TestObject> response = await this.container.DeleteItemAsync<TestObject>(item.Id, new PartitionKey(item.Pk));
183+
Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode);
184+
}
185+
}
186+
187+
[TestMethod]
188+
[TestCategory("ThinClient")]
189+
public async Task CreateItemStreamTest()
190+
{
191+
string pk = "pk_create_stream";
192+
IEnumerable<TestObject> items = this.GenerateItems(pk);
193+
194+
foreach (TestObject item in items)
195+
{
196+
using (Stream stream = this.cosmosSystemTextJsonSerializer.ToStream(item))
197+
{
198+
using (ResponseMessage response = await this.container.CreateItemStreamAsync(stream, new PartitionKey(item.Pk)))
199+
{
200+
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
201+
}
202+
}
203+
}
204+
}
205+
206+
[TestMethod]
207+
[TestCategory("ThinClient")]
208+
public async Task ReadItemStreamTest()
209+
{
210+
string pk = "pk_read_stream";
211+
List<TestObject> items = this.GenerateItems(pk).ToList();
212+
213+
foreach (TestObject item in items)
214+
{
215+
await this.container.CreateItemAsync(item, new PartitionKey(item.Pk));
216+
}
217+
218+
foreach (TestObject item in items)
219+
{
220+
using (ResponseMessage response = await this.container.ReadItemStreamAsync(item.Id, new PartitionKey(item.Pk)))
221+
{
222+
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
223+
}
224+
}
225+
}
226+
227+
[TestMethod]
228+
[TestCategory("ThinClient")]
229+
public async Task ReplaceItemStreamTest()
230+
{
231+
string pk = "pk_replace_stream";
232+
List<TestObject> items = this.GenerateItems(pk).ToList();
233+
234+
foreach (TestObject item in items)
235+
{
236+
await this.container.CreateItemAsync(item, new PartitionKey(item.Pk));
237+
}
238+
239+
foreach (TestObject item in items)
240+
{
241+
TestObject updatedItem = new TestObject
242+
{
243+
Id = item.Id,
244+
Pk = item.Pk,
245+
Other = "Updated " + item.Other
246+
};
247+
248+
using (Stream stream = this.cosmosSystemTextJsonSerializer.ToStream(updatedItem))
249+
{
250+
using (ResponseMessage response = await this.container.ReplaceItemStreamAsync(stream, updatedItem.Id, new PartitionKey(updatedItem.Pk)))
251+
{
252+
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
253+
}
254+
}
255+
}
256+
}
257+
258+
[TestMethod]
259+
[TestCategory("ThinClient")]
260+
public async Task UpsertItemStreamTest()
261+
{
262+
string pk = "pk_upsert_stream";
263+
IEnumerable<TestObject> items = this.GenerateItems(pk);
264+
265+
foreach (TestObject item in items)
266+
{
267+
using (Stream stream = this.cosmosSystemTextJsonSerializer.ToStream(item))
268+
{
269+
using (ResponseMessage response = await this.container.UpsertItemStreamAsync(stream, new PartitionKey(item.Pk)))
270+
{
271+
Assert.IsTrue(response.StatusCode == HttpStatusCode.Created || response.StatusCode == HttpStatusCode.OK);
272+
}
273+
}
274+
}
275+
}
276+
277+
[TestMethod]
278+
[TestCategory("ThinClient")]
279+
public async Task DeleteItemStreamTest()
280+
{
281+
string pk = "pk_delete_stream";
282+
List<TestObject> items = this.GenerateItems(pk).ToList();
283+
284+
foreach (TestObject item in items)
285+
{
286+
await this.container.CreateItemAsync(item, new PartitionKey(item.Pk));
287+
}
288+
289+
foreach (TestObject item in items)
290+
{
291+
using (ResponseMessage response = await this.container.DeleteItemStreamAsync(item.Id, new PartitionKey(item.Pk)))
292+
{
293+
Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode);
294+
}
295+
}
296+
}
297+
298+
[TestMethod]
299+
[TestCategory("ThinClient")]
300+
public async Task QueryItemsTest()
301+
{
302+
string pk = "pk_query";
303+
List<TestObject> items = this.GenerateItems(pk).ToList();
304+
305+
foreach (TestObject item in items)
306+
{
307+
await this.container.CreateItemAsync(item, new PartitionKey(item.Pk));
308+
}
309+
310+
string query = $"SELECT * FROM c WHERE c.partitionKey = '{pk}'";
311+
FeedIterator<TestObject> iterator = this.container.GetItemQueryIterator<TestObject>(query);
312+
313+
int count = 0;
314+
while (iterator.HasMoreResults)
315+
{
316+
FeedResponse<TestObject> response = await iterator.ReadNextAsync();
317+
count += response.Count;
318+
}
319+
320+
Assert.AreEqual(ItemCount, count);
321+
}
322+
323+
[TestMethod]
324+
[TestCategory("ThinClient")]
325+
public async Task QueryItemsStreamTest()
326+
{
327+
string pk = "pk_query_stream";
328+
List<TestObject> items = this.GenerateItems(pk).ToList();
329+
330+
foreach (dynamic item in items)
331+
{
332+
await this.container.CreateItemAsync(item, new PartitionKey(item.partitionKey));
333+
}
334+
335+
QueryDefinition query = new QueryDefinition("SELECT * FROM c WHERE c.partitionKey = @pk").WithParameter("@pk", pk);
336+
FeedIterator iterator = this.container.GetItemQueryStreamIterator(query);
337+
338+
int count = 0;
339+
while (iterator.HasMoreResults)
340+
{
341+
using (ResponseMessage response = await iterator.ReadNextAsync())
342+
{
343+
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
344+
345+
using (StreamReader reader = new StreamReader(response.Content))
346+
{
347+
string json = await reader.ReadToEndAsync();
348+
using (JsonDocument doc = JsonDocument.Parse(json))
349+
{
350+
count += doc.RootElement.GetProperty("Documents").GetArrayLength();
351+
}
352+
}
353+
}
354+
}
355+
356+
Assert.AreEqual(ItemCount, count);
357+
}
358+
}
359+
}

azure-pipelines.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,12 @@ jobs:
7272
BuildConfiguration: Release
7373
Arguments: $(ReleaseArguments)
7474
VmImage: $(VmImage)
75+
76+
- template: templates/build-thinclient.yml
77+
parameters:
78+
BuildConfiguration: Release
79+
Arguments: $(ReleaseArguments)
80+
VmImage: $(VmImage)
81+
ThinClientConnectionString: $(COSMOSDB_THINCLIENT)
82+
IncludePerformance: true
83+
IncludeCoverage: true

templates/build-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ parameters:
55
Arguments: ''
66
VmImage: '' # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops
77
OS: 'Windows'
8-
EmulatorPipeline1Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed)" --verbosity normal '
9-
EmulatorPipeline2Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster" --verbosity normal '
8+
EmulatorPipeline1Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed)" --verbosity normal '
9+
EmulatorPipeline2Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster" --verbosity normal '
1010
EmulatorPipeline3Arguments: ' --filter "TestCategory=MultiRegion" --verbosity normal '
1111
EmulatorPipeline4Arguments: ' --filter "TestCategory=MultiMaster" --verbosity normal '
1212
EmulatorPipeline1CategoryListName: ' Client Telemetry, Query, ChangeFeed, ReadFeed, Batch ' # Divided in 2 categories to run them in parallel and reduce the PR feedback time

0 commit comments

Comments
 (0)