Skip to content

Commit 88ec7c1

Browse files
committed
test: add more tests
1 parent d6b8f7b commit 88ec7c1

File tree

9 files changed

+693
-156
lines changed

9 files changed

+693
-156
lines changed

drivers/spanner-ado-net/spanner-ado-net-tests/AbstractMockServerTests.cs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,53 @@ public void Reset()
5353
{
5454
Fixture.SpannerMock.Reset();
5555
}
56-
}
56+
57+
protected async Task<SpannerConnection> OpenConnectionAsync()
58+
{
59+
var connection = new SpannerConnection(ConnectionString);
60+
await connection.OpenAsync();
61+
return connection;
62+
}
63+
64+
protected SpannerDataSource CreateDataSource()
65+
{
66+
return CreateDataSource(_ => { });
67+
}
68+
69+
protected SpannerDataSource CreateDataSource(Action<SpannerConnectionStringBuilder> connectionStringBuilderAction)
70+
{
71+
var connectionStringBuilder = new SpannerConnectionStringBuilder(ConnectionString);
72+
connectionStringBuilderAction(connectionStringBuilder);
73+
return SpannerDataSource.Create(connectionStringBuilder);
74+
}
75+
76+
}
77+
78+
public static class SpannerConnectionExtensions
79+
{
80+
public static int ExecuteNonQuery(this SpannerConnection conn, string sql, SpannerTransaction? tx = null)
81+
{
82+
using var command = tx == null ? new SpannerCommand(sql, conn) : new SpannerCommand(sql, conn, tx);
83+
return command.ExecuteNonQuery();
84+
}
85+
86+
public static object? ExecuteScalar(this SpannerConnection conn, string sql, SpannerTransaction? tx = null)
87+
{
88+
using var command = tx == null ? new SpannerCommand(sql, conn) : new SpannerCommand(sql, conn, tx);
89+
return command.ExecuteScalar();
90+
}
91+
92+
public static async Task<int> ExecuteNonQueryAsync(
93+
this SpannerConnection conn, string sql, SpannerTransaction? tx = null, CancellationToken cancellationToken = default)
94+
{
95+
await using var command = tx == null ? new SpannerCommand(sql, conn) : new SpannerCommand(sql, conn, tx);
96+
return await command.ExecuteNonQueryAsync(cancellationToken);
97+
}
98+
99+
public static async Task<object?> ExecuteScalarAsync(
100+
this SpannerConnection conn, string sql, SpannerTransaction? tx = null, CancellationToken cancellationToken = default)
101+
{
102+
await using var command = tx == null ? new SpannerCommand(sql, conn) : new SpannerCommand(sql, conn, tx);
103+
return await command.ExecuteScalarAsync(cancellationToken);
104+
}
105+
}

drivers/spanner-ado-net/spanner-ado-net-tests/CommandTests.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
// limitations under the License.
1414

1515
using System;
16+
using System.Data;
1617
using System.Data.Common;
1718
using System.Linq;
19+
using System.Text;
1820
using System.Text.Json;
1921
using System.Threading.Tasks;
2022
using Google.Cloud.Spanner.V1;
@@ -167,7 +169,106 @@ public async Task TestAllParameterTypes()
167169
Assert.That(fields["p23"].ListValue.Values[0].NumberValue, Is.EqualTo(3.14f));
168170
Assert.That(fields["p23"].ListValue.Values[1].HasNullValue, Is.True);
169171
}
172+
173+
[Test]
174+
[TestCase(new[] { true }, TestName = "SingleQuery")]
175+
[TestCase(new[] { false }, TestName = "SingleNonQuery")]
176+
[TestCase(new[] { true, true }, TestName = "TwoQueries")]
177+
[TestCase(new[] { false, false }, TestName = "TwoNonQueries")]
178+
[TestCase(new[] { false, true }, TestName = "NonQueryQuery")]
179+
[TestCase(new[] { true, false }, TestName = "QueryNonQuery")]
180+
[Ignore("Requires support for multi-statements strings in the shared library")]
181+
public async Task MultipleStatements(bool[] queries)
182+
{
183+
const string update = "UPDATE my_table SET name='yo' WHERE 1=0;";
184+
const string select = "SELECT 1;";
185+
Fixture.SpannerMock.AddOrUpdateStatementResult(update, StatementResult.CreateUpdateCount(0));
186+
187+
await using var conn = await OpenConnectionAsync();
188+
var sb = new StringBuilder();
189+
foreach (var query in queries)
190+
{
191+
sb.Append(query ? select : update);
192+
}
193+
var sql = sb.ToString();
194+
foreach (var prepare in new[] { false, true })
195+
{
196+
await using var cmd = conn.CreateCommand();
197+
cmd.CommandText = sql;
198+
if (prepare)
199+
{
200+
await cmd.PrepareAsync();
201+
}
202+
await using var reader = await cmd.ExecuteReaderAsync();
203+
var numResultSets = queries.Count(q => q);
204+
for (var i = 0; i < numResultSets; i++)
205+
{
206+
Assert.That(await reader.ReadAsync(), Is.True);
207+
Assert.That(reader[0], Is.EqualTo(1));
208+
Assert.That(await reader.NextResultAsync(), Is.EqualTo(i != numResultSets - 1));
209+
}
210+
}
211+
}
170212

213+
[Test]
214+
[Ignore("Requires support for multi-statements strings in the shared library")]
215+
public async Task MultipleStatementsWithParameters([Values(PrepareOrNot.NotPrepared, PrepareOrNot.Prepared)] PrepareOrNot prepare)
216+
{
217+
await using var conn = await OpenConnectionAsync();
218+
await using var cmd = conn.CreateCommand();
219+
cmd.CommandText = "SELECT @p1; SELECT @p2";
220+
var p1 = new SpannerParameter{ParameterName = "p1"};
221+
var p2 = new SpannerParameter{ParameterName = "p2"};
222+
cmd.Parameters.Add(p1);
223+
cmd.Parameters.Add(p2);
224+
if (prepare == PrepareOrNot.Prepared)
225+
{
226+
await cmd.PrepareAsync();
227+
}
228+
p1.Value = 8;
229+
p2.Value = "foo";
230+
await using var reader = await cmd.ExecuteReaderAsync();
231+
Assert.That(await reader.ReadAsync(), Is.True);
232+
Assert.That(reader.GetInt32(0), Is.EqualTo(8));
233+
Assert.That(await reader.NextResultAsync(), Is.True);
234+
Assert.That(await reader.ReadAsync(), Is.True);
235+
Assert.That(reader.GetString(0), Is.EqualTo("foo"));
236+
Assert.That(await reader.NextResultAsync(), Is.False);
237+
}
238+
239+
[Test]
240+
[Ignore("Requires support for multi-statements strings in the shared library")]
241+
public async Task SingleRowMultipleStatements([Values(PrepareOrNot.NotPrepared, PrepareOrNot.Prepared)] PrepareOrNot prepare)
242+
{
243+
await using var conn = await OpenConnectionAsync();
244+
await using var cmd = new SpannerCommand("SELECT 1; SELECT 2", conn);
245+
if (prepare == PrepareOrNot.Prepared)
246+
{
247+
await cmd.PrepareAsync();
248+
}
249+
await using var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow);
250+
Assert.That(await reader.ReadAsync(), Is.True);
251+
Assert.That(reader.GetInt32(0), Is.EqualTo(1));
252+
Assert.That(await reader.ReadAsync(), Is.False);
253+
Assert.That(await reader.NextResultAsync(), Is.False);
254+
}
255+
256+
[Test]
257+
[Ignore("Requires support for statement_timeout in the shared library")]
258+
public async Task Timeout()
259+
{
260+
Fixture.SpannerMock.AddOrUpdateExecutionTime(nameof(Fixture.SpannerMock.ExecuteStreamingSql), ExecutionTime.FromMillis(10, 0));
261+
262+
await using var dataSource = CreateDataSource(csb => csb.CommandTimeout = 1);
263+
await using var conn = await dataSource.OpenConnectionAsync() as SpannerConnection;
264+
await using var cmd = new SpannerCommand("SELECT 1", conn!);
265+
Assert.That(() => cmd.ExecuteScalar(), Throws.Exception
266+
.TypeOf<SpannerDbException>()
267+
.With.InnerException.TypeOf<TimeoutException>()
268+
);
269+
Assert.That(conn!.State, Is.EqualTo(ConnectionState.Open));
270+
}
271+
171272
private void AddParameter(DbCommand command, string name, object? value)
172273
{
173274
var parameter = command.CreateParameter();

drivers/spanner-ado-net/spanner-ado-net-tests/ConnectionStringBuilderTests.cs

Lines changed: 1 addition & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,10 @@
1313
// limitations under the License.
1414

1515
using System.Data;
16-
using Google.Cloud.SpannerLib;
17-
using Google.Cloud.SpannerLib.MockServer;
18-
using Google.Rpc;
19-
using Grpc.Core;
20-
using Status = Grpc.Core.Status;
2116

2217
namespace Google.Cloud.Spanner.DataProvider.Tests;
2318

24-
public class ConnectionStringBuilderTests : AbstractMockServerTests
19+
public class ConnectionStringBuilderTests
2520
{
2621
[Test]
2722
public void Basic()
@@ -172,126 +167,5 @@ public void PropertiesToConnectionString()
172167
Assert.That(builder.ConnectionString, Is.EqualTo("Data Source=projects/project1/instances/instance1/databases/database1;Host=localhost;Port=80;UsePlainText=True;DefaultIsolationLevel=RepeatableRead"));
173168
Assert.That(builder.SpannerLibConnectionString, Is.EqualTo("localhost:80/projects/project1/instances/instance1/databases/database1;UsePlainText=True;DefaultIsolationLevel=RepeatableRead"));
174169
}
175-
176-
[Test]
177-
public void RequiredConnectionStringProperties()
178-
{
179-
using var connection = new SpannerConnection();
180-
Assert.Throws<ArgumentException>(() => connection.ConnectionString = "Host=localhost;Port=80");
181-
}
182-
183-
[Test]
184-
public void FailedConnectThenSucceed()
185-
{
186-
// Close all current pools to ensure that we get a fresh pool.
187-
SpannerPool.CloseSpannerLib();
188-
// TODO: Make this a public property in the mock server.
189-
const string detectDialectQuery =
190-
"select option_value from information_schema.database_options where option_name='database_dialect'";
191-
Fixture.SpannerMock.AddOrUpdateStatementResult(detectDialectQuery, StatementResult.CreateException(new RpcException(new Status(StatusCode.NotFound, "Database not found"))));
192-
using var conn = new SpannerConnection();
193-
conn.ConnectionString = ConnectionString;
194-
var exception = Assert.Throws<SpannerException>(() => conn.Open());
195-
Assert.That(exception.Code, Is.EqualTo(Code.NotFound));
196-
Assert.That(conn.State, Is.EqualTo(ConnectionState.Closed));
197-
198-
// Remove the error and retry.
199-
Fixture.SpannerMock.AddOrUpdateStatementResult(detectDialectQuery, StatementResult.CreateResultSet(new List<Tuple<Google.Cloud.Spanner.V1.TypeCode, string>>
200-
{
201-
Tuple.Create<Google.Cloud.Spanner.V1.TypeCode, string>(V1.TypeCode.String, "option_value")
202-
}, new List<object[]>
203-
{
204-
new object[] { "GOOGLE_STANDARD_SQL" }
205-
}));
206-
conn.Open();
207-
Assert.That(conn.State, Is.EqualTo(ConnectionState.Open));
208-
}
209-
210-
[Test]
211-
[Ignore("Needs connect_timeout property")]
212-
public void OpenTimeout()
213-
{
214-
// TODO: Add connect_timeout property.
215-
var builder = new SpannerConnectionStringBuilder
216-
{
217-
Host = Fixture.Host,
218-
Port = (uint) Fixture.Port,
219-
UsePlainText = true,
220-
DataSource = "projects/project1/instances/instance1/databases/database1",
221-
//ConnectTimeout = TimeSpan.FromMicroseconds(1),
222-
};
223-
using var connection = new SpannerConnection();
224-
connection.ConnectionString = builder.ConnectionString;
225-
var exception = Assert.Throws<SpannerDbException>(() => connection.Open());
226-
Assert.That(exception.ErrorCode, Is.EqualTo((int) Code.DeadlineExceeded));
227-
}
228-
229-
[Test]
230-
[Ignore("OpenAsync must be implemented")]
231-
public async Task OpenCancel()
232-
{
233-
// Close all current pools to ensure that we get a fresh pool.
234-
SpannerPool.CloseSpannerLib();
235-
Fixture.SpannerMock.AddOrUpdateExecutionTime(nameof(Fixture.SpannerMock.CreateSession), ExecutionTime.FromMillis(20, 0));
236-
var builder = new SpannerConnectionStringBuilder
237-
{
238-
Host = Fixture.Host,
239-
Port = (uint) Fixture.Port,
240-
UsePlainText = true,
241-
DataSource = "projects/project1/instances/instance1/databases/database1",
242-
};
243-
await using var connection = new SpannerConnection();
244-
connection.ConnectionString = builder.ConnectionString;
245-
var tokenSource = new CancellationTokenSource(5);
246-
// TODO: Implement actual async opening of connections
247-
Assert.ThrowsAsync<OperationCanceledException>(async () => await connection.OpenAsync(tokenSource.Token));
248-
Assert.That(connection.State, Is.EqualTo(ConnectionState.Closed));
249-
}
250-
251-
[Test]
252-
public void DataSourceProperty()
253-
{
254-
using var conn = new SpannerConnection();
255-
Assert.That(conn.DataSource, Is.EqualTo(string.Empty));
256-
257-
var builder = new SpannerConnectionStringBuilder(ConnectionString);
258-
259-
conn.ConnectionString = builder.ConnectionString;
260-
Assert.That(conn.DataSource, Is.EqualTo("projects/p1/instances/i1/databases/d1"));
261-
}
262-
263-
[Test]
264-
public void SettingConnectionStringWhileOpenThrows()
265-
{
266-
using var conn = new SpannerConnection();
267-
conn.ConnectionString = ConnectionString;
268-
conn.Open();
269-
Assert.That(() => conn.ConnectionString = "", Throws.Exception.TypeOf<InvalidOperationException>());
270-
}
271-
272-
[Test]
273-
public void EmptyConstructor()
274-
{
275-
var conn = new SpannerConnection();
276-
Assert.That(conn.ConnectionTimeout, Is.EqualTo(15));
277-
Assert.That(conn.ConnectionString, Is.SameAs(string.Empty));
278-
Assert.That(() => conn.Open(), Throws.Exception.TypeOf<InvalidOperationException>());
279-
}
280-
281-
[Test]
282-
public void Constructor_with_null_connection_string()
283-
{
284-
var conn = new SpannerConnection(null);
285-
Assert.That(conn.ConnectionString, Is.SameAs(string.Empty));
286-
Assert.That(() => conn.Open(), Throws.Exception.TypeOf<InvalidOperationException>());
287-
}
288-
289-
[Test]
290-
public void Constructor_with_empty_connection_string()
291-
{
292-
var conn = new NpgsqlConnection("");
293-
Assert.That(conn.ConnectionString, Is.SameAs(string.Empty));
294-
Assert.That(() => conn.Open(), Throws.Exception.TypeOf<InvalidOperationException>());
295-
}
296170

297171
}

0 commit comments

Comments
 (0)