Skip to content

Commit 7ca3cd5

Browse files
committed
test: SqlServerDataSourceExtension
1 parent 8d0d62f commit 7ca3cd5

File tree

4 files changed

+137
-6
lines changed

4 files changed

+137
-6
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ bld/
6868
# Visual Studio 2017 auto generated files
6969
Generated\ Files/
7070

71+
# MSTest .runsettings
72+
*.runsettings
73+
7174
# MSTest test Results
7275
[Tt]est[Rr]esult*/
7376
[Bb]uild[Ll]og.*
@@ -434,3 +437,4 @@ FodyWeavers.xsd
434437
# macOS .DS_Store
435438
**/.DS_Store
436439
/Core/Cosmos.DataTransfer.Core/migrationsettings.json
440+

Extensions/SqlServer/Cosmos.DataTransfer.SqlServerExtension.UnitTests/Cosmos.DataTransfer.SqlServerExtension.UnitTests.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
</PropertyGroup>
1313

1414
<ItemGroup>
15-
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
15+
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.1" />
1616
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
1717
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
1818
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
1919
<PackageReference Include="coverlet.collector" Version="3.1.2" />
20-
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
20+
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
2121
<PackageReference Include="coverlet.msbuild" Version="2.8.0">
2222
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2323
<PrivateAssets>all</PrivateAssets>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using Microsoft.Data.Sqlite;
2+
using Microsoft.Extensions.Logging.Abstractions;
3+
using Cosmos.DataTransfer.Interfaces;
4+
using Cosmos.DataTransfer.Common;
5+
using Cosmos.DataTransfer.Common.UnitTests;
6+
7+
namespace Cosmos.DataTransfer.SqlServerExtension.UnitTests;
8+
9+
[TestClass]
10+
public class SqlServerDataSourceExtensionTests
11+
{
12+
13+
private static async Task<Func<string,ValueTask<System.Data.Common.DbConnection>>> connectionFactory(CancellationToken cancellationToken = default(CancellationToken)) {
14+
var connection = new SqliteConnection("");
15+
await connection.OpenAsync(cancellationToken);
16+
17+
var cmd = connection.CreateCommand();
18+
cmd.CommandText = @"CREATE TABLE foobar (
19+
id INTEGER NOT NULL,
20+
name TEXT
21+
);";
22+
await cmd.ExecuteNonQueryAsync(cancellationToken);
23+
cmd.CommandText = @"INSERT INTO foobar (id, name)
24+
VALUES (1, 'zoo');";
25+
await cmd.ExecuteNonQueryAsync(cancellationToken);
26+
cmd.CommandText = @"INSERT INTO foobar (id, name)
27+
VALUES (2, NULL);";
28+
await cmd.ExecuteNonQueryAsync(cancellationToken);
29+
30+
var func = (string connectionString) => {
31+
return new ValueTask<System.Data.Common.DbConnection>(connection);
32+
};
33+
34+
return func;
35+
}
36+
37+
[TestMethod]
38+
public async Task TestReadAsync_QueryText() {
39+
var extension = new SqlServerDataSourceExtension();
40+
var config = TestHelpers.CreateConfig(new Dictionary<string, string> {
41+
{ "ConnectionString", "Sqlite" },
42+
{ "QueryText", "SELECT * FROM foobar" }
43+
});
44+
Assert.AreEqual("SqlServer", extension.DisplayName);
45+
46+
var cancellationToken = new CancellationTokenSource(500);
47+
48+
var result = await extension.ReadAsync(config, NullLogger.Instance, await connectionFactory(cancellationToken.Token), cancellationToken.Token).ToListAsync();
49+
var expected = new List<DictionaryDataItem> {
50+
new DictionaryDataItem(new Dictionary<string, object?> { { "id", (long)1 }, { "name", "zoo" } }),
51+
new DictionaryDataItem(new Dictionary<string, object?> { { "id", (long)2 }, { "name", null } })
52+
};
53+
CollectionAssert.That.AreEqual(expected, result, new DataItemComparer());
54+
}
55+
56+
[TestMethod]
57+
public async Task TestReadAsync_FromFile() {
58+
var outputFile = Path.GetTempFileName();
59+
await File.WriteAllTextAsync(outputFile, "SELECT * FROM foobar;");
60+
var extension = new SqlServerDataSourceExtension();
61+
var config = TestHelpers.CreateConfig(new Dictionary<string, string> {
62+
{ "ConnectionString", "Sqlite" },
63+
{ "FilePath", outputFile }
64+
});
65+
66+
67+
var cancellationToken = new CancellationTokenSource(500);
68+
69+
var result = await extension.ReadAsync(config, NullLogger.Instance, await connectionFactory(cancellationToken.Token), cancellationToken.Token).ToListAsync();
70+
var expected = new List<DictionaryDataItem> {
71+
new DictionaryDataItem(new Dictionary<string, object?> { { "id", (long)1 }, { "name", "zoo" } }),
72+
new DictionaryDataItem(new Dictionary<string, object?> { { "id", (long)2 }, { "name", null } })
73+
};
74+
CollectionAssert.That.AreEqual(expected, result, new DataItemComparer());
75+
}
76+
77+
// Allows for testing against an actual SQL Server by specifying a
78+
// connectionstring in either a .runsettings-file or environment variable.
79+
// Example: Using sql.runsettings
80+
// <?xml version="1.0" encoding="utf-8"?>
81+
// <RunSettings>
82+
// <TestRunParameters>
83+
// <Parameter name="TestReadAsync_LiveSqlServer_ConnectionString" value="<Your connection string>" />
84+
// </TestRunParameters>
85+
// </RunSettings>
86+
// run test with dotnet test --settings sql.runsettings
87+
#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.
88+
public TestContext TestContext { get; set; }
89+
#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.
90+
[TestMethod]
91+
[Timeout(1000)]
92+
public async Task TestReadAsync_LiveSqlServer() {
93+
var connectionString = (string?)TestContext.Properties["TestReadAsync_LiveSqlServer_ConnectionString"];
94+
connectionString ??= Environment.GetEnvironmentVariable("TestReadAsync_LiveSqlServer_ConnectionString");
95+
if (connectionString is null) {
96+
Assert.Inconclusive("Could not run, as no connection string to live SQL Server was provided.");
97+
}
98+
99+
var extension = new SqlServerDataSourceExtension();
100+
var config = TestHelpers.CreateConfig(new Dictionary<string, string> {
101+
{ "ConnectionString", connectionString! },
102+
{ "QueryText", "SELECT 1, 'foo' as bar, NULL as zoo;" }
103+
});
104+
105+
var result = await extension.ReadAsync(config, NullLogger.Instance).FirstAsync();
106+
107+
Assert.IsTrue(new DataItemComparer().Equals(result,
108+
new DictionaryDataItem(new Dictionary<string, object?> {
109+
{ "", 1 },
110+
{ "bar", "foo" },
111+
{ "zoo", null }
112+
})));
113+
}
114+
}

Extensions/SqlServer/Cosmos.DataTransfer.SqlServerExtension/SqlServerDataSourceExtension.cs

+17-4
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,32 @@ public class SqlServerDataSourceExtension : IDataSourceExtensionWithSettings
1414
public string DisplayName => "SqlServer";
1515

1616
public async IAsyncEnumerable<IDataItem> ReadAsync(IConfiguration config, ILogger logger, [EnumeratorCancellation] CancellationToken cancellationToken = default)
17+
{
18+
await foreach (var item in this.ReadAsync(config, logger, (string connectionString) => new ValueTask<System.Data.Common.DbConnection>(new SqlConnection(connectionString)), cancellationToken)) {
19+
yield return item;
20+
}
21+
}
22+
23+
public async IAsyncEnumerable<IDataItem> ReadAsync(
24+
IConfiguration config,
25+
ILogger logger,
26+
Func<string,ValueTask<System.Data.Common.DbConnection>> connectionFactory,
27+
[EnumeratorCancellation] CancellationToken cancellationToken = default)
1728
{
1829
var settings = config.Get<SqlServerSourceSettings>();
1930
settings.Validate();
2031

2132
string queryText = settings.QueryText!;
2233
if (settings.FilePath != null) {
23-
queryText = File.ReadAllText(queryText);
34+
queryText = File.ReadAllText(settings.FilePath);
2435
}
2536

26-
await using var connection = new SqlConnection(settings.ConnectionString);
37+
await using var connection = connectionFactory(settings.ConnectionString!).Result;
2738
await connection.OpenAsync(cancellationToken);
28-
await using SqlCommand command = new SqlCommand(queryText, connection);
29-
await using SqlDataReader reader = await command.ExecuteReaderAsync(cancellationToken);
39+
var command = connection.CreateCommand();
40+
command.CommandText = queryText;
41+
//await using SqlCommand command = new SqlCommand(queryText, connection);
42+
await using var reader = await command.ExecuteReaderAsync(cancellationToken);
3043
while (await reader.ReadAsync(cancellationToken))
3144
{
3245
var columns = await reader.GetColumnSchemaAsync(cancellationToken);

0 commit comments

Comments
 (0)