Skip to content

Commit 23e4818

Browse files
dev: added connection specification tests (#256)
* Invoke OnStateChange in YdbConnection (Open -> Close, Close -> Open) * Impl DbProviderFactory * Dev: added DbConnection specification tests from MySql Connector
1 parent bc0e095 commit 23e4818

File tree

11 files changed

+160
-34
lines changed

11 files changed

+160
-34
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ jobs:
5454
- 8765:8765
5555
env:
5656
YDB_LOCAL_SURVIVE_RESTART: true
57-
YDB_USE_IN_MEMORY_PDISKS: true
5857
options: '--name ydb-local -h localhost'
5958
env:
6059
OS: ubuntu-22.04
@@ -72,7 +71,7 @@ jobs:
7271
run: |
7372
docker cp ydb-local:/ydb_certs/ca.pem ~/
7473
cd src
75-
dotnet test --filter "(FullyQualifiedName~Ado) | (FullyQualifiedName~Dapper)" -l "console;verbosity=normal"
74+
dotnet test --filter "(FullyQualifiedName~Ado) | (FullyQualifiedName~Dapper)" -f ${{ matrix.dotnet-target-framework }} -l "console;verbosity=normal"
7675
topic-tests:
7776
runs-on: ubuntu-22.04
7877
strategy:
@@ -94,7 +93,6 @@ jobs:
9493
- 8765:8765
9594
env:
9695
YDB_LOCAL_SURVIVE_RESTART: true
97-
YDB_USE_IN_MEMORY_PDISKS: true
9896
options: '--name ydb-local -h localhost'
9997
env:
10098
OS: ubuntu-22.04
@@ -112,7 +110,7 @@ jobs:
112110
run: |
113111
docker cp ydb-local:/ydb_certs/ca.pem ~/
114112
cd src
115-
dotnet test --filter "FullyQualifiedName~Topic" -l "console;verbosity=normal"
113+
dotnet test --filter "FullyQualifiedName~Topic" -f ${{ matrix.dotnet-target-framework }} -l "console;verbosity=normal"
116114
integration-tests:
117115
runs-on: ubuntu-22.04
118116
strategy:
@@ -134,7 +132,6 @@ jobs:
134132
- 8765:8765
135133
env:
136134
YDB_LOCAL_SURVIVE_RESTART: true
137-
YDB_USE_IN_MEMORY_PDISKS: true
138135
options: '--name ydb-local -h localhost'
139136
env:
140137
OS: ubuntu-22.04

src/Ydb.Sdk/src/Ado/PoolManager.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ internal static class PoolManager
88
private static readonly SemaphoreSlim SemaphoreSlim = new(1); // async mutex
99
private static readonly ConcurrentDictionary<string, SessionPool> Pools = new();
1010

11-
internal static async Task<Session> GetSession(YdbConnectionStringBuilder connectionString)
11+
internal static async Task<Session> GetSession(
12+
YdbConnectionStringBuilder connectionString,
13+
CancellationToken cancellationToken
14+
)
1215
{
1316
if (Pools.TryGetValue(connectionString.ConnectionString, out var sessionPool))
1417
{
@@ -17,7 +20,7 @@ internal static async Task<Session> GetSession(YdbConnectionStringBuilder connec
1720

1821
try
1922
{
20-
await SemaphoreSlim.WaitAsync();
23+
await SemaphoreSlim.WaitAsync(cancellationToken);
2124

2225
if (Pools.TryGetValue(connectionString.ConnectionString, out var pool))
2326
{

src/Ydb.Sdk/src/Ado/YdbCommand.cs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Data;
22
using System.Data.Common;
3+
using System.Diagnostics.CodeAnalysis;
34
using System.Text;
45
using Ydb.Sdk.Ado.Internal;
56
using Ydb.Sdk.Services.Query;
@@ -8,9 +9,13 @@ namespace Ydb.Sdk.Ado;
89

910
public sealed class YdbCommand : DbCommand
1011
{
11-
private YdbConnection YdbConnection { get; set; }
12+
private YdbConnection? YdbConnection { get; set; }
1213

13-
private string _commandText = string.Empty;
14+
private string? _commandText = string.Empty;
15+
16+
public YdbCommand()
17+
{
18+
}
1419

1520
public YdbCommand(YdbConnection ydbConnection)
1621
{
@@ -65,14 +70,10 @@ public override void Prepare()
6570

6671
public override string CommandText
6772
{
68-
get => _commandText;
73+
get => _commandText ?? throw new InvalidOperationException("CommandText property has not been initialized");
6974
#pragma warning disable CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes).
70-
set
75+
[param: AllowNull] set => _commandText = value;
7176
#pragma warning restore CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes).
72-
{
73-
_commandText = value ?? throw new ArgumentNullException(nameof(value));
74-
DbParameterCollection.Clear();
75-
}
7677
}
7778

7879
public override int CommandTimeout
@@ -99,14 +100,14 @@ protected override DbConnection? DbConnection
99100
get => YdbConnection;
100101
set
101102
{
102-
if (value is YdbConnection ydbConnection)
103+
if (value is null or Ado.YdbConnection)
103104
{
104-
YdbConnection = ydbConnection;
105+
YdbConnection = (YdbConnection?)value;
105106
}
106107
else
107108
{
108109
throw new ArgumentException(
109-
$"Unsupported DbTransaction type: {value?.GetType()}, expected: {typeof(YdbConnection)}");
110+
$"Unsupported DbTransaction type: {value.GetType()}, expected: {typeof(YdbConnection)}");
110111
}
111112
}
112113
}
@@ -154,7 +155,8 @@ protected override YdbDataReader ExecuteDbDataReader(CommandBehavior behavior)
154155
protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior,
155156
CancellationToken cancellationToken)
156157
{
157-
if (YdbConnection.IsBusy)
158+
if (YdbConnection?.IsBusy
159+
?? throw new InvalidOperationException("Connection property has not been initialized."))
158160
{
159161
throw new YdbOperationInProgressException(YdbConnection);
160162
}

src/Ydb.Sdk/src/Ado/YdbConnection.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ public sealed class YdbConnection : DbConnection
99
{
1010
private static readonly YdbConnectionStringBuilder DefaultSettings = new();
1111

12+
private static readonly StateChangeEventArgs ClosedToOpenEventArgs =
13+
new(ConnectionState.Closed, ConnectionState.Open);
14+
15+
private static readonly StateChangeEventArgs OpenToClosedEventArgs =
16+
new(ConnectionState.Open, ConnectionState.Closed);
17+
1218
private bool _disposed;
1319

1420
private YdbConnectionStringBuilder ConnectionStringBuilder { get; set; }
@@ -84,18 +90,20 @@ public override async Task OpenAsync(CancellationToken cancellationToken)
8490

8591
try
8692
{
87-
Session = await PoolManager.GetSession(ConnectionStringBuilder);
93+
Session = await PoolManager.GetSession(ConnectionStringBuilder, cancellationToken);
8894
}
8995
catch (Exception e)
9096
{
9197
throw e switch
9298
{
9399
Driver.TransportException transportException => new YdbException(transportException.Status),
94100
StatusUnsuccessfulException unsuccessfulException => new YdbException(unsuccessfulException.Status),
95-
_ => new YdbException("Cannot get session", e)
101+
_ => e
96102
};
97103
}
98104

105+
OnStateChange(ClosedToOpenEventArgs);
106+
99107
ConnectionState = ConnectionState.Open;
100108
}
101109

@@ -118,6 +126,8 @@ public override async Task CloseAsync()
118126
await LastTransaction.RollbackAsync();
119127
}
120128

129+
OnStateChange(OpenToClosedEventArgs);
130+
121131
ConnectionState = ConnectionState.Closed;
122132
}
123133
finally
@@ -140,7 +150,9 @@ public override string ConnectionString
140150
}
141151
}
142152

143-
public override string Database => ConnectionStringBuilder.Database;
153+
public override string Database => State == ConnectionState.Closed
154+
? string.Empty
155+
: ConnectionStringBuilder.Database;
144156

145157
public override ConnectionState State => ConnectionState;
146158

@@ -152,7 +164,16 @@ public override string ConnectionString
152164
internal bool IsBusy => LastReader is { IsClosed: false };
153165

154166
public override string DataSource => string.Empty; // TODO
155-
public override string ServerVersion => string.Empty; // TODO
167+
168+
public override string ServerVersion
169+
{
170+
get
171+
{
172+
EnsureConnectionOpen();
173+
174+
return string.Empty; // TODO ServerVersion
175+
}
176+
}
156177

157178
protected override YdbCommand CreateDbCommand()
158179
{
@@ -239,6 +260,11 @@ public override async ValueTask DisposeAsync()
239260
_disposed = true;
240261
}
241262

263+
/// <summary>
264+
/// DB provider factory.
265+
/// </summary>
266+
protected override DbProviderFactory DbProviderFactory => YdbProviderFactory.Instance;
267+
242268
/// <summary>
243269
/// Clears the connection pool. All idle physical connections in the pool of the given connection are
244270
/// immediately closed, and any busy connections which were opened before <see cref="ClearPool"/> was called
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Data.Common;
2+
3+
namespace Ydb.Sdk.Ado;
4+
5+
public class YdbProviderFactory : DbProviderFactory
6+
{
7+
public static readonly YdbProviderFactory Instance = new();
8+
9+
public override YdbCommand CreateCommand()
10+
{
11+
return new YdbCommand();
12+
}
13+
14+
public override YdbConnection CreateConnection()
15+
{
16+
return new YdbConnection();
17+
}
18+
19+
public override YdbConnectionStringBuilder CreateConnectionStringBuilder()
20+
{
21+
return new YdbConnectionStringBuilder();
22+
}
23+
24+
public override DbParameter CreateParameter()
25+
{
26+
return new YdbParameter();
27+
}
28+
29+
#if NET7_0_OR_GREATER
30+
public override YdbDataSource CreateDataSource(string connectionString)
31+
{
32+
return new YdbDataSource();
33+
}
34+
#endif
35+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using AdoNet.Specification.Tests;
2+
using Xunit;
3+
4+
namespace Ydb.Sdk.Tests.Ado.Specification;
5+
6+
public class YdbConnectionTests : ConnectionTestBase<YdbFactoryFixture>
7+
{
8+
public YdbConnectionTests(YdbFactoryFixture fixture) : base(fixture)
9+
{
10+
}
11+
12+
#pragma warning disable xUnit1004
13+
[Fact(Skip = "IComponent legacy.")]
14+
#pragma warning restore xUnit1004
15+
public override void Dispose_raises_Disposed()
16+
{
17+
base.Dispose_raises_Disposed();
18+
}
19+
20+
#pragma warning disable xUnit1004
21+
[Fact(Skip = "IComponent legacy.")]
22+
#pragma warning restore xUnit1004
23+
public override Task DisposeAsync_raises_Disposed()
24+
{
25+
return base.DisposeAsync_raises_Disposed();
26+
}
27+
28+
#pragma warning disable xUnit1004
29+
[Fact(Skip = "Connect to default settings 'grpc://localhost:2136/local'.")]
30+
#pragma warning restore xUnit1004
31+
public override void Open_throws_when_no_connection_string()
32+
{
33+
base.Open_throws_when_no_connection_string();
34+
}
35+
36+
#pragma warning disable xUnit1004
37+
[Fact(Skip = "TODO Supported this field.")]
38+
#pragma warning restore xUnit1004
39+
public override void ServerVersion_returns_value()
40+
{
41+
base.ServerVersion_returns_value();
42+
}
43+
44+
#pragma warning disable xUnit1004
45+
[Fact(Skip = "TODO Supported cancel OpenAsync.")]
46+
#pragma warning restore xUnit1004
47+
public override Task OpenAsync_is_canceled()
48+
{
49+
return base.OpenAsync_is_canceled();
50+
}
51+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Data.Common;
2+
using AdoNet.Specification.Tests;
3+
using Ydb.Sdk.Ado;
4+
5+
namespace Ydb.Sdk.Tests.Ado.Specification;
6+
7+
public class YdbFactoryFixture : IDbFactoryFixture
8+
{
9+
public DbProviderFactory Factory => YdbProviderFactory.Instance;
10+
11+
public string ConnectionString => "Host=localhost;Port=2136;Database=local";
12+
}

src/Ydb.Sdk/tests/Ado/YdbDataReaderTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ public async Task BasedIteration_WhenNotCallMethodRead_ThrowException()
4040
}
4141

4242
[Fact]
43-
public void CreateYdbDataReader_WhenAbortedStatus_ThrowException()
43+
public async Task CreateYdbDataReader_WhenAbortedStatus_ThrowException()
4444
{
4545
var statuses = new List<Status>();
46-
Assert.Equal("Status: Aborted", Assert.Throws<YdbException>(
47-
() => YdbDataReader.CreateYdbDataReader(SingleEnumeratorFailed, statuses.Add).GetAwaiter().GetResult())
46+
Assert.Equal("Status: Aborted", (await Assert.ThrowsAsync<YdbException>(
47+
() => YdbDataReader.CreateYdbDataReader(SingleEnumeratorFailed, statuses.Add)))
4848
.Message);
4949
Assert.Single(statuses);
5050
Assert.Equal(StatusCode.Aborted, statuses[0].StatusCode);

src/Ydb.Sdk/tests/Pool/ChannelPoolTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ public async Task GetChannel_WhenRaceCondition_ChannelIsCreatedOneTime(bool useA
9696
.Select(endpoint => Task.Run(() => _channelPool.GetChannel(endpoint)))
9797
.ToArray();
9898

99-
// ReSharper disable once CoVariantArrayConversion
100-
Task.WaitAll(tasks);
99+
await Task.WhenAll(tasks);
101100

102101
_mockChannelFactory.Verify(
103102
channelPool => channelPool.CreateChannel(It.IsAny<string>()), Times.Exactly(endpointCount)

src/Ydb.Sdk/tests/Tests.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
</PropertyGroup>
1717

1818
<ItemGroup>
19+
<PackageReference Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
1920
<PackageReference Include="Dapper" Version="2.1.35" />
2021
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0-rc.1.23419.4" />
2122
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
2223
<PackageReference Include="Moq" Version="4.20.70" />
23-
<PackageReference Include="xunit" Version="2.4.1" />
24-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
24+
<PackageReference Include="xunit" Version="2.5.3" />
25+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
2526
<PrivateAssets>all</PrivateAssets>
2627
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2728
</PackageReference>

0 commit comments

Comments
 (0)