Skip to content

Commit 462eb7f

Browse files
committed
initialization of project for specification tests
1 parent 25ead1f commit 462eb7f

File tree

8 files changed

+308
-0
lines changed

8 files changed

+308
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational.Specification.Tests" Version="9.0.1"/>
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<ProjectReference Include="..\EfCore.Ydb\src\EfCore.Ydb.csproj"/>
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<None Update="Northwind.sql" CopyToOutputDirectory="PreserveNewest"/>
20+
</ItemGroup>
21+
22+
</Project>

src/EfCore.Ydb.FunctionalTests/Northwind.sql

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using EfCore.Ydb.FunctionalTests.TestUtilities;
2+
using Microsoft.EntityFrameworkCore.Infrastructure;
3+
using Microsoft.EntityFrameworkCore.Query;
4+
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
5+
using Microsoft.EntityFrameworkCore.TestUtilities;
6+
7+
namespace EfCore.Ydb.FunctionalTests.Query;
8+
9+
public class NorthwindQueryYdbFixture<TModelCustomizer> : NorthwindQueryRelationalFixture<TModelCustomizer>
10+
where TModelCustomizer : ITestModelCustomizer, new()
11+
{
12+
protected override ITestStoreFactory TestStoreFactory
13+
=> YdbNorthwindTestStoreFactory.Instance;
14+
15+
protected override Type ContextType
16+
=> typeof(NorthwindContext);
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.EntityFrameworkCore.TestUtilities;
2+
3+
namespace EfCore.Ydb.FunctionalTests.TestUtilities;
4+
5+
public class YdbNorthwindTestStoreFactory : YdbTestStoreFactory
6+
{
7+
public const string DatabaseName = "Northwind";
8+
9+
public new static YdbNorthwindTestStoreFactory Instance { get; } = new();
10+
11+
public override TestStore GetOrCreate(string storeName)
12+
=> YdbTestStore.GetOrCreate(DatabaseName, scriptPath: $"{DatabaseName}.sql");
13+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
using System.Data;
2+
using System.Data.Common;
3+
using System.Text.RegularExpressions;
4+
using EfCore.Ydb.Extensions;
5+
using Microsoft.EntityFrameworkCore;
6+
using Microsoft.EntityFrameworkCore.TestUtilities;
7+
using Microsoft.IdentityModel.Tokens;
8+
using Ydb.Sdk.Ado;
9+
10+
namespace EfCore.Ydb.FunctionalTests.TestUtilities;
11+
12+
public class YdbTestStore : RelationalTestStore
13+
{
14+
public const int CommandTimeout = 690;
15+
private readonly string? _scriptPath;
16+
private readonly string? _additionalSql;
17+
18+
public YdbTestStore(
19+
string name,
20+
string? scriptPath = null,
21+
string? additionalSql = null,
22+
string? connectionStringOptions = null,
23+
bool shared = true,
24+
bool useConnectionString = false
25+
) : base(name, shared, CreateConnection(name, shared))
26+
{
27+
_scriptPath = scriptPath;
28+
_additionalSql = additionalSql;
29+
}
30+
31+
public static YdbTestStore GetOrCreate(
32+
string name,
33+
string? scriptPath = null
34+
) => new(name: name, scriptPath: scriptPath);
35+
36+
public override DbContextOptionsBuilder AddProviderOptions(DbContextOptionsBuilder builder)
37+
{
38+
return UseConnectionString
39+
? builder.UseEfYdb(Connection.ConnectionString)
40+
: builder.UseEfYdb(Connection);
41+
}
42+
43+
protected override async Task InitializeAsync(
44+
Func<DbContext> createContext, Func<DbContext, Task>? seed, Func<DbContext, Task>? clean
45+
)
46+
{
47+
if (_scriptPath is not null)
48+
{
49+
ExecuteScript(_scriptPath);
50+
51+
if (_additionalSql is not null)
52+
{
53+
Execute(Connection, command => command.ExecuteNonQuery(), _additionalSql);
54+
}
55+
}
56+
else
57+
{
58+
await using var context = createContext();
59+
await context.Database.EnsureCreatedResilientlyAsync();
60+
61+
if (_additionalSql is not null)
62+
{
63+
Execute(Connection, command => command.ExecuteNonQuery(), _additionalSql);
64+
}
65+
66+
if (seed is not null)
67+
{
68+
await seed(context);
69+
}
70+
}
71+
}
72+
73+
74+
public void ExecuteScript(string scriptPath)
75+
{
76+
var script = File.ReadAllText(scriptPath);
77+
Execute(
78+
Connection, command =>
79+
{
80+
var commandsToExecute =
81+
new Regex("^GO",
82+
RegexOptions.IgnoreCase | RegexOptions.Multiline,
83+
TimeSpan.FromMilliseconds(4269.0)
84+
)
85+
.Split(script)
86+
.Where(b => !string.IsNullOrEmpty(b));
87+
88+
var commandToExecutes = commandsToExecute.ToList();
89+
foreach (var commandToExecute in commandToExecutes)
90+
{
91+
try
92+
{
93+
var pureCommand = new Regex("\n", RegexOptions.IgnoreCase | RegexOptions.Multiline,
94+
TimeSpan.FromMilliseconds(1_000))
95+
.Split(commandToExecute)
96+
.Where(b => !b.StartsWith("--") && !b.IsNullOrEmpty())
97+
.ToList();
98+
99+
var commandJoined = string.Join("\n", pureCommand);
100+
101+
command.CommandTimeout = 100_000;
102+
command.CommandText = commandJoined;
103+
command.ExecuteNonQuery();
104+
}
105+
catch (Exception e)
106+
{
107+
throw new AggregateException(
108+
new Exception($"Command:\n{commandToExecute}\n"), e
109+
);
110+
}
111+
}
112+
113+
return 0;
114+
}, "");
115+
}
116+
117+
private static T Execute<T>(
118+
DbConnection connection,
119+
Func<DbCommand, T> execute,
120+
string sql,
121+
object[]? parameters = null
122+
) => ExecuteCommand(connection, execute, sql, parameters);
123+
124+
private static T ExecuteCommand<T>(
125+
DbConnection connection,
126+
Func<DbCommand, T> execute,
127+
string sql,
128+
object[]? parameters
129+
)
130+
{
131+
if (connection.State != ConnectionState.Closed)
132+
{
133+
connection.Close();
134+
}
135+
136+
connection.Open();
137+
try
138+
{
139+
using var command = CreateCommand(connection, sql, parameters);
140+
return execute(command);
141+
}
142+
finally
143+
{
144+
if (connection.State != ConnectionState.Closed)
145+
{
146+
connection.Close();
147+
}
148+
}
149+
}
150+
151+
private static YdbCommand CreateCommand(
152+
DbConnection connection,
153+
string commandText,
154+
IReadOnlyList<object>? parameters = null
155+
)
156+
{
157+
var command = (YdbCommand)connection.CreateCommand();
158+
159+
command.CommandText = commandText;
160+
command.CommandTimeout = CommandTimeout;
161+
162+
if (parameters is not null)
163+
{
164+
for (var i = 0; i < parameters.Count; i++)
165+
{
166+
command.Parameters.AddWithValue("p" + i, parameters[i]);
167+
}
168+
}
169+
170+
return command;
171+
}
172+
173+
174+
private static YdbConnection CreateConnection(string name, bool sharedCache)
175+
{
176+
var connectionString = new YdbConnectionStringBuilder(
177+
$"Host=localhost;" +
178+
$"Port=2135;" +
179+
$"Database = \"/local\";" +
180+
$"MaxSessionPool=10;" +
181+
$"RootCertificate=" + Path.Combine(
182+
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
183+
"dev/ydb_ca/ca.pem"
184+
)
185+
);
186+
187+
return new YdbConnection(connectionString);
188+
}
189+
190+
public override Task CleanAsync(DbContext context)
191+
{
192+
return Task.CompletedTask;
193+
}
194+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using EfCore.Ydb.Extensions;
2+
using Microsoft.EntityFrameworkCore.TestUtilities;
3+
using Microsoft.Extensions.DependencyInjection;
4+
5+
namespace EfCore.Ydb.FunctionalTests.TestUtilities;
6+
7+
public class YdbTestStoreFactory : RelationalTestStoreFactory
8+
{
9+
public static YdbTestStoreFactory Instance { get; } = new();
10+
11+
private string? _scriptPath = null;
12+
private string? _additionalSql = null;
13+
private string? _connectionStringOptions = null;
14+
private readonly string? _connectionString;
15+
private bool _useConnectionString = false;
16+
17+
public YdbTestStoreFactory(
18+
string? connectionString = null,
19+
string? additionalSql = null,
20+
string? connectionStringOptions = null,
21+
bool useConnectionString = false
22+
)
23+
{
24+
_connectionString = connectionString;
25+
_additionalSql = additionalSql;
26+
_connectionStringOptions = connectionStringOptions;
27+
_useConnectionString = useConnectionString;
28+
}
29+
30+
public override TestStore Create(string storeName)
31+
=> new YdbTestStore(storeName, _scriptPath, _additionalSql, _connectionStringOptions, shared: false, _useConnectionString);
32+
33+
public override TestStore GetOrCreate(string storeName)
34+
=> new YdbTestStore(storeName, _scriptPath, _additionalSql, _connectionStringOptions, shared: true, _useConnectionString);
35+
36+
public override IServiceCollection AddProviderServices(IServiceCollection serviceCollection)
37+
=> serviceCollection.AddEntityFrameworkYdb();
38+
}

src/EfCore.Ydb/src/Extensions/YdbContextOptionsBuilderExtensions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Data.Common;
23
using EfCore.Ydb.Infrastructure;
34
using EfCore.Ydb.Infrastructure.Internal;
45
using Microsoft.EntityFrameworkCore;
@@ -23,6 +24,22 @@ public static DbContextOptionsBuilder UseEfYdb(
2324
return optionsBuilder;
2425
}
2526

27+
public static DbContextOptionsBuilder UseEfYdb(
28+
this DbContextOptionsBuilder optionsBuilder,
29+
DbConnection connection,
30+
Action<YdbDbContextOptionsBuilder>? efYdbOptionsAction = null
31+
)
32+
{
33+
var extension = GetOrCreateExtension(optionsBuilder).WithConnection(connection);
34+
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
35+
36+
ConfigureWarnings(optionsBuilder);
37+
38+
efYdbOptionsAction?.Invoke(new YdbDbContextOptionsBuilder(optionsBuilder));
39+
return optionsBuilder;
40+
41+
}
42+
2643
// TODO: Right now there are no arguments for constructor, so probably it's ok
2744
private static YdbOptionsExtension GetOrCreateExtension(DbContextOptionsBuilder options)
2845
=> options.Options.FindExtension<YdbOptionsExtension>()

src/YdbSdk.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{39DE35EE-6
2323
EndProject
2424
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EfCore.Ydb.Tests", "EfCore.Ydb\test\EfCore.Ydb.Tests.csproj", "{C340085B-AB9E-484A-B4A5-32131AE5029F}"
2525
EndProject
26+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EfCore.Ydb.FunctionalTests", "EfCore.Ydb.FunctionalTests\EfCore.Ydb.FunctionalTests.csproj", "{28550E34-B56D-4536-B455-031983D9E561}"
27+
EndProject
2628
Global
2729
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2830
Debug|Any CPU = Debug|Any CPU
@@ -61,6 +63,10 @@ Global
6163
{C340085B-AB9E-484A-B4A5-32131AE5029F}.Debug|Any CPU.Build.0 = Debug|Any CPU
6264
{C340085B-AB9E-484A-B4A5-32131AE5029F}.Release|Any CPU.ActiveCfg = Release|Any CPU
6365
{C340085B-AB9E-484A-B4A5-32131AE5029F}.Release|Any CPU.Build.0 = Release|Any CPU
66+
{28550E34-B56D-4536-B455-031983D9E561}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
67+
{28550E34-B56D-4536-B455-031983D9E561}.Debug|Any CPU.Build.0 = Debug|Any CPU
68+
{28550E34-B56D-4536-B455-031983D9E561}.Release|Any CPU.ActiveCfg = Release|Any CPU
69+
{28550E34-B56D-4536-B455-031983D9E561}.Release|Any CPU.Build.0 = Release|Any CPU
6470
EndGlobalSection
6571
GlobalSection(SolutionProperties) = preSolution
6672
HideSolutionNode = FALSE
@@ -77,6 +83,7 @@ Global
7783
{D3C0891F-85FA-4FBA-A8BB-02A58C1D5202} = {369AA6CC-3CD3-40A9-82E4-61B7D7D42CE3}
7884
{39DE35EE-621B-4DEC-BFA2-5337ACBAEEF4} = {5E7B167B-5FC7-41BD-8819-16B02ED9B961}
7985
{C340085B-AB9E-484A-B4A5-32131AE5029F} = {39DE35EE-621B-4DEC-BFA2-5337ACBAEEF4}
86+
{28550E34-B56D-4536-B455-031983D9E561} = {39DE35EE-621B-4DEC-BFA2-5337ACBAEEF4}
8087
EndGlobalSection
8188
GlobalSection(ExtensibilityGlobals) = postSolution
8289
SolutionGuid = {0AB27123-0C66-4E43-A75F-D9EAB9ED0849}

0 commit comments

Comments
 (0)