Skip to content

Commit 1532b9e

Browse files
authored
[#552][DotNet] Add CosmosDB Azure tests to the test solution (#563)
* Add CosmosDb integration tests * Use Directory.Build.props global BotBuilder variables * Remove unused TestCategory traits * Update to Azure.Cosmos namespace and configuration
1 parent e8b7486 commit 1532b9e

16 files changed

+698
-11
lines changed

Bots/DotNet/Directory.Build.props

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
<Project>
2-
<PropertyGroup>
3-
<BotBuilderVersion Condition="'$(BotBuilderVersion)' == ''">4.15.0</BotBuilderVersion>
4-
<BotBuilderVersion Condition="'$(BotBuilderVersion)' != ''">$(BotBuilderVersion)</BotBuilderVersion>
5-
<RestoreAdditionalProjectSources>$(BotBuilderRegistry)</RestoreAdditionalProjectSources>
6-
</PropertyGroup>
7-
82
<PropertyGroup>
93
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
104
</PropertyGroup>

Directory.Build.props

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
<Project>
2-
2+
33
<PropertyGroup>
44
<LangVersion>latest</LangVersion>
55
</PropertyGroup>
6-
6+
77
<PropertyGroup>
88
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)BotFramework-FunctionalTests.ruleset</CodeAnalysisRuleSet>
99
</PropertyGroup>
1010

1111
<!-- This applies for SDK-style projects only-->
1212
<Import Project="PackageReferences.props" Condition="'$(UsingMicrosoftNETSdk)' == 'true'"/>
13-
13+
1414
<PropertyGroup>
1515
<Company>Microsoft</Company>
1616

@@ -30,5 +30,10 @@
3030
<NeutralLanguage>en-US</NeutralLanguage>
3131
<PublishRepositoryUrl>true</PublishRepositoryUrl>
3232
</PropertyGroup>
33-
33+
34+
<PropertyGroup>
35+
<BotBuilderVersion Condition="'$(BotBuilderVersion)' == ''">4.15.0</BotBuilderVersion>
36+
<BotBuilderVersion Condition="'$(BotBuilderVersion)' != ''">$(BotBuilderVersion)</BotBuilderVersion>
37+
<RestoreAdditionalProjectSources>$(BotBuilderRegistry)</RestoreAdditionalProjectSources>
38+
</PropertyGroup>
3439
</Project>

FunctionalTests.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Functional", "Functional",
2626
EndProject
2727
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{9027483A-C2FD-48FF-922A-9050ED490B03}"
2828
EndProject
29+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "Tests\Integration\DotNet\IntegrationTests.csproj", "{BF885B2E-942C-43F3-BECA-64EFBB0598F4}"
30+
EndProject
2931
Global
3032
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3133
Debug|Any CPU = Debug|Any CPU
@@ -48,6 +50,10 @@ Global
4850
{5CAE9F80-9BD3-480C-8504-552B31B265E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
4951
{5CAE9F80-9BD3-480C-8504-552B31B265E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
5052
{5CAE9F80-9BD3-480C-8504-552B31B265E0}.Release|Any CPU.Build.0 = Release|Any CPU
53+
{BF885B2E-942C-43F3-BECA-64EFBB0598F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54+
{BF885B2E-942C-43F3-BECA-64EFBB0598F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
55+
{BF885B2E-942C-43F3-BECA-64EFBB0598F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
56+
{BF885B2E-942C-43F3-BECA-64EFBB0598F4}.Release|Any CPU.Build.0 = Release|Any CPU
5157
EndGlobalSection
5258
GlobalSection(SolutionProperties) = preSolution
5359
HideSolutionNode = FALSE
@@ -59,6 +65,7 @@ Global
5965
{5CAE9F80-9BD3-480C-8504-552B31B265E0} = {B5E35C94-0880-4AEB-8F3C-940FECA7A8C7}
6066
{DDEE53CF-F094-4009-B198-AA656CB7A009} = {5A0C68F1-6CA7-43D7-8F01-70A484D8D412}
6167
{9027483A-C2FD-48FF-922A-9050ED490B03} = {5A0C68F1-6CA7-43D7-8F01-70A484D8D412}
68+
{BF885B2E-942C-43F3-BECA-64EFBB0598F4} = {9027483A-C2FD-48FF-922A-9050ED490B03}
6269
EndGlobalSection
6370
GlobalSection(ExtensibilityGlobals) = postSolution
6471
SolutionGuid = {CF79F565-B676-4679-83A1-DDE5CB729723}

Tests/Directory.Build.props

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,19 @@
77
<PropertyGroup>
88
<!-- SA0001;CS1573,CS1591,CS1712: For tests, we don't generate documentation. Supress related rules. -->
99
<!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
10-
<NoWarn>$(NoWarn);SA0001;CS1573;CS1591;CS1712;SX1309</NoWarn>
10+
<!-- NU1701: Duplicate from Microsoft.Bot.Builder.Azure.csproj
11+
The KeyVault package, picked up as a transitive dependency of the Azure Storage libraries
12+
doesn't yet support NetStandard20. I confirmed with the Azure Storage team that this warning
13+
is fine, and can be supressed.
14+
15+
It does appear the Azure SDK team is "in-process" of supporting NetStandard20 as seen in this
16+
Commit: https://github.com/Azure/azure-sdk-for-net/commit/b0d42d14bfe92a24996826b2487ba592e644f581
17+
18+
We cannot apply the no-warn supression directly to the package links below as
19+
they're not picked up across transitive dependencies. See this GitHub Issue for details:
20+
https://github.com/NuGet/Home/issues/5740
21+
-->
22+
<NoWarn>$(NoWarn);SA0001;CS1573;CS1591;CS1712;SX1309;NU1701</NoWarn>
1123
</PropertyGroup>
1224

1325
<!-- This ensures that Directory.Build.props in parent folders are merged with this one -->

Tests/Integration/DotNet/.gitkeep

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
6+
namespace IntegrationTests.Azure.Cosmos
7+
{
8+
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
9+
public class CosmosDbAttribute : Attribute
10+
{
11+
public CosmosDbAttribute(string databaseId = "CosmosDatabase", string containerId = "CosmosContainer")
12+
{
13+
DatabaseId = databaseId;
14+
ContainerId = containerId;
15+
}
16+
17+
public string DatabaseId { get; private set; }
18+
19+
public string ContainerId { get; private set; }
20+
}
21+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Reflection;
6+
using System.Threading.Tasks;
7+
using Microsoft.Azure.Cosmos;
8+
using Microsoft.Azure.Documents.Client;
9+
using Xunit;
10+
11+
namespace IntegrationTests.Azure.Cosmos
12+
{
13+
public abstract class CosmosDbBaseFixture : ConfigurationFixture, IAsyncLifetime
14+
{
15+
public string AuthKey { get; private set; }
16+
17+
public string ServiceEndpoint { get; private set; }
18+
19+
public string DatabaseId { get; private set; }
20+
21+
public string ContainerId { get; private set; }
22+
23+
public CosmosClient Client { get; private set; }
24+
25+
protected bool IsRunning { get; private set; }
26+
27+
public async Task InitializeAsync()
28+
{
29+
var attr = GetType().GetCustomAttribute(typeof(CosmosDbAttribute)) as CosmosDbAttribute;
30+
DatabaseId = attr?.DatabaseId;
31+
ContainerId = attr?.ContainerId;
32+
33+
ServiceEndpoint = Configuration["Azure:Cosmos:ServiceEndpoint"];
34+
AuthKey = Configuration["Azure:Cosmos:AuthKey"];
35+
36+
Client = new CosmosClient(
37+
ServiceEndpoint,
38+
AuthKey,
39+
new CosmosClientOptions());
40+
41+
IsRunning = await IsServiceRunning();
42+
43+
await Client.CreateDatabaseIfNotExistsAsync(DatabaseId);
44+
}
45+
46+
public async Task DisposeAsync()
47+
{
48+
if (!IsRunning)
49+
{
50+
return;
51+
}
52+
53+
await DeleteDatabase(DatabaseId);
54+
}
55+
56+
protected Task<DatabaseResponse> DeleteDatabase(string name)
57+
{
58+
try
59+
{
60+
return Client.GetDatabase(name).DeleteAsync();
61+
}
62+
catch (Exception ex)
63+
{
64+
const string message = "Cosmos: Error cleaning up resources.";
65+
throw new Exception(message, ex);
66+
}
67+
}
68+
69+
protected async Task<bool> IsServiceRunning()
70+
{
71+
using var client = new DocumentClient(new Uri(ServiceEndpoint), AuthKey);
72+
try
73+
{
74+
await client.OpenAsync();
75+
return true;
76+
}
77+
catch (Exception ex)
78+
{
79+
var message = $"Cosmos: Unable to connect to the '{ServiceEndpoint}' endpoint.";
80+
throw new Exception(message, ex);
81+
}
82+
}
83+
}
84+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using Microsoft.Bot.Builder;
8+
using Xunit;
9+
10+
namespace IntegrationTests.Azure.Cosmos
11+
{
12+
public class CosmosDbBaseTests
13+
{
14+
public CosmosDbBaseTests()
15+
{
16+
Sample = new CosmosDbStorageItem() { MessageList = new string[] { "hi", "how are u" }, City = "Contoso" };
17+
}
18+
19+
public CosmosDbStorageItem Sample { get; private set; }
20+
21+
protected async Task ReadUnknownItemTest(IStorage storage)
22+
{
23+
var result = await storage.ReadAsync(new[] { "unknown" });
24+
25+
Assert.Empty(result);
26+
}
27+
28+
protected async Task DeleteUnknownItemTest(IStorage storage)
29+
{
30+
await storage.DeleteAsync(new[] { "unknown" });
31+
}
32+
33+
protected async Task CreateItemTest(IStorage storage)
34+
{
35+
var dict = new Dictionary<string, object>
36+
{
37+
{ "createItem", Sample },
38+
};
39+
40+
await storage.WriteAsync(dict);
41+
42+
var createdItems = await storage.ReadAsync<CosmosDbStorageItem>(dict.Keys.ToArray());
43+
var created = createdItems.FirstOrDefault().Value;
44+
45+
Assert.Single(createdItems);
46+
Assert.NotNull(created);
47+
Assert.NotNull(created.ETag);
48+
Assert.Equal(Sample.City, created.City);
49+
Assert.Equal(Sample.MessageList, created.MessageList);
50+
}
51+
52+
protected async Task CreateItemWithSpecialCharactersTest(IStorage storage)
53+
{
54+
var key = "!@#$%^&*()~/\\><,.?';\"`~";
55+
var dict = new Dictionary<string, object>
56+
{
57+
{ key, Sample },
58+
};
59+
60+
await storage.WriteAsync(dict);
61+
62+
var createdItems = await storage.ReadAsync<CosmosDbStorageItem>(dict.Keys.ToArray());
63+
var created = createdItems.FirstOrDefault().Value;
64+
65+
Assert.Single(createdItems);
66+
Assert.NotNull(created);
67+
Assert.NotNull(created.ETag);
68+
Assert.Equal(Sample.City, created.City);
69+
Assert.Equal(Sample.MessageList, created.MessageList);
70+
}
71+
72+
protected async Task UpdateItemTest(IStorage storage)
73+
{
74+
var dict = new Dictionary<string, object>
75+
{
76+
{ "updateItem", Sample },
77+
};
78+
79+
await storage.WriteAsync(dict);
80+
81+
var createdItems = await storage.ReadAsync(dict.Keys.ToArray());
82+
var created = createdItems.FirstOrDefault().Value as CosmosDbStorageItem;
83+
84+
// Update store item
85+
created.MessageList = new string[] { "new message" };
86+
87+
await storage.WriteAsync(createdItems);
88+
89+
var updatedItems = await storage.ReadAsync(dict.Keys.ToArray());
90+
var updated = updatedItems.FirstOrDefault().Value as CosmosDbStorageItem;
91+
92+
Assert.NotEqual(created.ETag, updated.ETag);
93+
Assert.Single(updated.MessageList);
94+
Assert.Equal(created.MessageList, updated.MessageList);
95+
}
96+
97+
protected async Task DeleteItemTest(IStorage storage)
98+
{
99+
var dict = new Dictionary<string, object>
100+
{
101+
{ "deleteItem", Sample },
102+
};
103+
104+
await storage.WriteAsync(dict);
105+
106+
var createdItems = await storage.ReadAsync<CosmosDbStorageItem>(dict.Keys.ToArray());
107+
var created = createdItems.FirstOrDefault().Value;
108+
109+
// Delete store item
110+
await storage.DeleteAsync(dict.Keys.ToArray());
111+
112+
var deleted = await storage.ReadAsync(dict.Keys.ToArray());
113+
114+
Assert.NotNull(created);
115+
Assert.Empty(deleted);
116+
}
117+
}
118+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Threading.Tasks;
5+
using Microsoft.Bot.Builder;
6+
using Microsoft.Bot.Builder.Azure;
7+
using Xunit;
8+
9+
namespace IntegrationTests.Azure.Cosmos
10+
{
11+
[CosmosDb(databaseId: "CosmosDbPartitionedStorageTests")]
12+
public class CosmosDbPartitionedStorageFixture : CosmosDbBaseFixture, IAsyncLifetime
13+
{
14+
public IStorage Storage { get; private set; }
15+
16+
public new async Task InitializeAsync()
17+
{
18+
await base.InitializeAsync();
19+
20+
Storage = new CosmosDbPartitionedStorage(
21+
new CosmosDbPartitionedStorageOptions
22+
{
23+
AuthKey = AuthKey,
24+
ContainerId = ContainerId,
25+
CosmosDbEndpoint = ServiceEndpoint,
26+
DatabaseId = DatabaseId,
27+
});
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)