Skip to content

Commit

Permalink
dev: added connection specification tests (#256)
Browse files Browse the repository at this point in the history
* Invoke OnStateChange in YdbConnection (Open -> Close, Close -> Open)
* Impl DbProviderFactory
* Dev: added DbConnection specification tests from MySql Connector
  • Loading branch information
KirillKurdyukov authored Jan 23, 2025
1 parent bc0e095 commit 23e4818
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 34 deletions.
7 changes: 2 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ jobs:
- 8765:8765
env:
YDB_LOCAL_SURVIVE_RESTART: true
YDB_USE_IN_MEMORY_PDISKS: true
options: '--name ydb-local -h localhost'
env:
OS: ubuntu-22.04
Expand All @@ -72,7 +71,7 @@ jobs:
run: |
docker cp ydb-local:/ydb_certs/ca.pem ~/
cd src
dotnet test --filter "(FullyQualifiedName~Ado) | (FullyQualifiedName~Dapper)" -l "console;verbosity=normal"
dotnet test --filter "(FullyQualifiedName~Ado) | (FullyQualifiedName~Dapper)" -f ${{ matrix.dotnet-target-framework }} -l "console;verbosity=normal"
topic-tests:
runs-on: ubuntu-22.04
strategy:
Expand All @@ -94,7 +93,6 @@ jobs:
- 8765:8765
env:
YDB_LOCAL_SURVIVE_RESTART: true
YDB_USE_IN_MEMORY_PDISKS: true
options: '--name ydb-local -h localhost'
env:
OS: ubuntu-22.04
Expand All @@ -112,7 +110,7 @@ jobs:
run: |
docker cp ydb-local:/ydb_certs/ca.pem ~/
cd src
dotnet test --filter "FullyQualifiedName~Topic" -l "console;verbosity=normal"
dotnet test --filter "FullyQualifiedName~Topic" -f ${{ matrix.dotnet-target-framework }} -l "console;verbosity=normal"
integration-tests:
runs-on: ubuntu-22.04
strategy:
Expand All @@ -134,7 +132,6 @@ jobs:
- 8765:8765
env:
YDB_LOCAL_SURVIVE_RESTART: true
YDB_USE_IN_MEMORY_PDISKS: true
options: '--name ydb-local -h localhost'
env:
OS: ubuntu-22.04
Expand Down
7 changes: 5 additions & 2 deletions src/Ydb.Sdk/src/Ado/PoolManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ internal static class PoolManager
private static readonly SemaphoreSlim SemaphoreSlim = new(1); // async mutex
private static readonly ConcurrentDictionary<string, SessionPool> Pools = new();

internal static async Task<Session> GetSession(YdbConnectionStringBuilder connectionString)
internal static async Task<Session> GetSession(
YdbConnectionStringBuilder connectionString,
CancellationToken cancellationToken
)
{
if (Pools.TryGetValue(connectionString.ConnectionString, out var sessionPool))
{
Expand All @@ -17,7 +20,7 @@ internal static async Task<Session> GetSession(YdbConnectionStringBuilder connec

try
{
await SemaphoreSlim.WaitAsync();
await SemaphoreSlim.WaitAsync(cancellationToken);

if (Pools.TryGetValue(connectionString.ConnectionString, out var pool))
{
Expand Down
26 changes: 14 additions & 12 deletions src/Ydb.Sdk/src/Ado/YdbCommand.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Data;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Ydb.Sdk.Ado.Internal;
using Ydb.Sdk.Services.Query;
Expand All @@ -8,9 +9,13 @@ namespace Ydb.Sdk.Ado;

public sealed class YdbCommand : DbCommand
{
private YdbConnection YdbConnection { get; set; }
private YdbConnection? YdbConnection { get; set; }

private string _commandText = string.Empty;
private string? _commandText = string.Empty;

public YdbCommand()
{
}

public YdbCommand(YdbConnection ydbConnection)
{
Expand Down Expand Up @@ -65,14 +70,10 @@ public override void Prepare()

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

public override int CommandTimeout
Expand All @@ -99,14 +100,14 @@ protected override DbConnection? DbConnection
get => YdbConnection;
set
{
if (value is YdbConnection ydbConnection)
if (value is null or Ado.YdbConnection)
{
YdbConnection = ydbConnection;
YdbConnection = (YdbConnection?)value;
}
else
{
throw new ArgumentException(
$"Unsupported DbTransaction type: {value?.GetType()}, expected: {typeof(YdbConnection)}");
$"Unsupported DbTransaction type: {value.GetType()}, expected: {typeof(YdbConnection)}");
}
}
}
Expand Down Expand Up @@ -154,7 +155,8 @@ protected override YdbDataReader ExecuteDbDataReader(CommandBehavior behavior)
protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior,
CancellationToken cancellationToken)
{
if (YdbConnection.IsBusy)
if (YdbConnection?.IsBusy
?? throw new InvalidOperationException("Connection property has not been initialized."))
{
throw new YdbOperationInProgressException(YdbConnection);
}
Expand Down
34 changes: 30 additions & 4 deletions src/Ydb.Sdk/src/Ado/YdbConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ public sealed class YdbConnection : DbConnection
{
private static readonly YdbConnectionStringBuilder DefaultSettings = new();

private static readonly StateChangeEventArgs ClosedToOpenEventArgs =
new(ConnectionState.Closed, ConnectionState.Open);

private static readonly StateChangeEventArgs OpenToClosedEventArgs =
new(ConnectionState.Open, ConnectionState.Closed);

private bool _disposed;

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

try
{
Session = await PoolManager.GetSession(ConnectionStringBuilder);
Session = await PoolManager.GetSession(ConnectionStringBuilder, cancellationToken);
}
catch (Exception e)
{
throw e switch
{
Driver.TransportException transportException => new YdbException(transportException.Status),
StatusUnsuccessfulException unsuccessfulException => new YdbException(unsuccessfulException.Status),
_ => new YdbException("Cannot get session", e)
_ => e
};
}

OnStateChange(ClosedToOpenEventArgs);

ConnectionState = ConnectionState.Open;
}

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

OnStateChange(OpenToClosedEventArgs);

ConnectionState = ConnectionState.Closed;
}
finally
Expand All @@ -140,7 +150,9 @@ public override string ConnectionString
}
}

public override string Database => ConnectionStringBuilder.Database;
public override string Database => State == ConnectionState.Closed
? string.Empty
: ConnectionStringBuilder.Database;

public override ConnectionState State => ConnectionState;

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

public override string DataSource => string.Empty; // TODO
public override string ServerVersion => string.Empty; // TODO

public override string ServerVersion
{
get
{
EnsureConnectionOpen();

return string.Empty; // TODO ServerVersion
}
}

protected override YdbCommand CreateDbCommand()
{
Expand Down Expand Up @@ -239,6 +260,11 @@ public override async ValueTask DisposeAsync()
_disposed = true;
}

/// <summary>
/// DB provider factory.
/// </summary>
protected override DbProviderFactory DbProviderFactory => YdbProviderFactory.Instance;

/// <summary>
/// Clears the connection pool. All idle physical connections in the pool of the given connection are
/// immediately closed, and any busy connections which were opened before <see cref="ClearPool"/> was called
Expand Down
35 changes: 35 additions & 0 deletions src/Ydb.Sdk/src/Ado/YdbProviderFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Data.Common;

namespace Ydb.Sdk.Ado;

public class YdbProviderFactory : DbProviderFactory
{
public static readonly YdbProviderFactory Instance = new();

public override YdbCommand CreateCommand()
{
return new YdbCommand();
}

public override YdbConnection CreateConnection()
{
return new YdbConnection();
}

public override YdbConnectionStringBuilder CreateConnectionStringBuilder()
{
return new YdbConnectionStringBuilder();
}

public override DbParameter CreateParameter()
{
return new YdbParameter();
}

#if NET7_0_OR_GREATER
public override YdbDataSource CreateDataSource(string connectionString)
{
return new YdbDataSource();
}
#endif
}
51 changes: 51 additions & 0 deletions src/Ydb.Sdk/tests/Ado/Specification/YdbConnectionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using AdoNet.Specification.Tests;
using Xunit;

namespace Ydb.Sdk.Tests.Ado.Specification;

public class YdbConnectionTests : ConnectionTestBase<YdbFactoryFixture>
{
public YdbConnectionTests(YdbFactoryFixture fixture) : base(fixture)
{
}

#pragma warning disable xUnit1004
[Fact(Skip = "IComponent legacy.")]
#pragma warning restore xUnit1004
public override void Dispose_raises_Disposed()
{
base.Dispose_raises_Disposed();
}

#pragma warning disable xUnit1004
[Fact(Skip = "IComponent legacy.")]
#pragma warning restore xUnit1004
public override Task DisposeAsync_raises_Disposed()
{
return base.DisposeAsync_raises_Disposed();
}

#pragma warning disable xUnit1004
[Fact(Skip = "Connect to default settings 'grpc://localhost:2136/local'.")]
#pragma warning restore xUnit1004
public override void Open_throws_when_no_connection_string()
{
base.Open_throws_when_no_connection_string();
}

#pragma warning disable xUnit1004
[Fact(Skip = "TODO Supported this field.")]
#pragma warning restore xUnit1004
public override void ServerVersion_returns_value()
{
base.ServerVersion_returns_value();
}

#pragma warning disable xUnit1004
[Fact(Skip = "TODO Supported cancel OpenAsync.")]
#pragma warning restore xUnit1004
public override Task OpenAsync_is_canceled()
{
return base.OpenAsync_is_canceled();
}
}
12 changes: 12 additions & 0 deletions src/Ydb.Sdk/tests/Ado/Specification/YdbFactoryFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Data.Common;
using AdoNet.Specification.Tests;
using Ydb.Sdk.Ado;

namespace Ydb.Sdk.Tests.Ado.Specification;

public class YdbFactoryFixture : IDbFactoryFixture
{
public DbProviderFactory Factory => YdbProviderFactory.Instance;

public string ConnectionString => "Host=localhost;Port=2136;Database=local";
}
6 changes: 3 additions & 3 deletions src/Ydb.Sdk/tests/Ado/YdbDataReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ public async Task BasedIteration_WhenNotCallMethodRead_ThrowException()
}

[Fact]
public void CreateYdbDataReader_WhenAbortedStatus_ThrowException()
public async Task CreateYdbDataReader_WhenAbortedStatus_ThrowException()
{
var statuses = new List<Status>();
Assert.Equal("Status: Aborted", Assert.Throws<YdbException>(
() => YdbDataReader.CreateYdbDataReader(SingleEnumeratorFailed, statuses.Add).GetAwaiter().GetResult())
Assert.Equal("Status: Aborted", (await Assert.ThrowsAsync<YdbException>(
() => YdbDataReader.CreateYdbDataReader(SingleEnumeratorFailed, statuses.Add)))
.Message);
Assert.Single(statuses);
Assert.Equal(StatusCode.Aborted, statuses[0].StatusCode);
Expand Down
3 changes: 1 addition & 2 deletions src/Ydb.Sdk/tests/Pool/ChannelPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ public async Task GetChannel_WhenRaceCondition_ChannelIsCreatedOneTime(bool useA
.Select(endpoint => Task.Run(() => _channelPool.GetChannel(endpoint)))
.ToArray();

// ReSharper disable once CoVariantArrayConversion
Task.WaitAll(tasks);
await Task.WhenAll(tasks);

_mockChannelFactory.Verify(
channelPool => channelPool.CreateChannel(It.IsAny<string>()), Times.Exactly(endpointCount)
Expand Down
5 changes: 3 additions & 2 deletions src/Ydb.Sdk/tests/Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0-rc.1.23419.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
8 changes: 4 additions & 4 deletions src/Ydb.Sdk/tests/Value/BasicUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ public void ListType()
var elements = value.GetTuple();
Assert.Equal(2, elements.Count);

Assert.Equal(0, elements[0].GetList().Count);
Assert.Empty(elements[0].GetList());
Assert.Equal(new[] { "one", "two" }, elements[1].GetList().Select(v => (string)v!));
}

Expand All @@ -340,9 +340,9 @@ public void TupleType()
});

var elements = value.GetTuple();
Assert.Equal(1, elements.Count);
Assert.Single(elements);

Assert.Equal(0, elements[0].GetTuple().Count);
Assert.Empty(elements[0].GetTuple());
}

[Fact]
Expand All @@ -361,7 +361,7 @@ public void StructType()
var elements = value.GetTuple();
Assert.Equal(2, elements.Count);

Assert.Equal(0, elements[0].GetStruct().Count);
Assert.Empty(elements[0].GetStruct());

var s = elements[1].GetStruct();
Assert.Equal(2, s.Count);
Expand Down

0 comments on commit 23e4818

Please sign in to comment.