Skip to content

Commit 0edd563

Browse files
Updates the CreateEmptyDatabase() to specify the location of the database to create.
1 parent f35abb8 commit 0edd563

4 files changed

Lines changed: 221 additions & 8 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="SqlDatabaseCreationSettings.cs" company="P.O.S Informatique">
3+
// Copyright (c) P.O.S Informatique. All rights reserved.
4+
// </copyright>
5+
//-----------------------------------------------------------------------
6+
7+
namespace PosInformatique.Testing.Databases.SqlServer
8+
{
9+
/// <summary>
10+
/// Settings of the <see cref="SqlServerDatabase"/> to create using the
11+
/// <see cref="SqlServer.CreateEmptyDatabase(string, SqlDatabaseCreationSettings?)"/>
12+
/// or
13+
/// <see cref="SqlServer.CreateEmptyDatabaseAsync(string, SqlDatabaseCreationSettings?, CancellationToken)"/>
14+
/// methods.
15+
/// </summary>
16+
public class SqlDatabaseCreationSettings
17+
{
18+
/// <summary>
19+
/// Gets or sets the data file name (full path) of the database to create.
20+
/// </summary>
21+
public string? DataFileName { get; set; }
22+
}
23+
}

src/Testing.Databases.SqlServer/SqlServer.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace PosInformatique.Testing.Databases.SqlServer
88
{
9+
using System.Text;
910
using Microsoft.Data.SqlClient;
1011

1112
/// <summary>
@@ -42,11 +43,12 @@ public SqlServer(string connectionString)
4243
/// If the database already exists, it will be delete.
4344
/// </summary>
4445
/// <param name="name">Name of the database to create.</param>
46+
/// <param name="settings">Settings of the database to create.</param>
4547
/// <returns>An instance of <see cref="SqlServerDatabase"/> which allows to execute SQL commands/queries.</returns>
46-
public SqlServerDatabase CreateEmptyDatabase(string name)
48+
public SqlServerDatabase CreateEmptyDatabase(string name, SqlDatabaseCreationSettings? settings = null)
4749
{
4850
this.DeleteDatabase(name);
49-
this.Master.ExecuteNonQuery($"CREATE DATABASE [{name}]");
51+
this.Master.ExecuteNonQuery(BuildCreateDatabaseSqlCommand(name, settings));
5052

5153
return this.GetDatabase(name);
5254
}
@@ -56,12 +58,13 @@ public SqlServerDatabase CreateEmptyDatabase(string name)
5658
/// If the database already exists, it will be delete.
5759
/// </summary>
5860
/// <param name="name">Name of the database to create.</param>
61+
/// <param name="settings">Settings of the database to create.</param>
5962
/// <param name="cancellationToken"><see cref="CancellationToken"/> used to cancel the asynchronous operation.</param>
6063
/// <returns>A <see cref="Task"/> which represents the asynchronous operation and contains an instance of <see cref="SqlServerDatabase"/> which allows to execute SQL commands/queries.</returns>
61-
public async Task<SqlServerDatabase> CreateEmptyDatabaseAsync(string name, CancellationToken cancellationToken = default)
64+
public async Task<SqlServerDatabase> CreateEmptyDatabaseAsync(string name, SqlDatabaseCreationSettings? settings = null, CancellationToken cancellationToken = default)
6265
{
6366
await this.DeleteDatabaseAsync(name, cancellationToken);
64-
await this.Master.ExecuteNonQueryAsync($"CREATE DATABASE [{name}]", cancellationToken);
67+
await this.Master.ExecuteNonQueryAsync(BuildCreateDatabaseSqlCommand(name, settings), cancellationToken);
6568

6669
return this.GetDatabase(name);
6770
}
@@ -102,5 +105,20 @@ public SqlServerDatabase GetDatabase(string name)
102105

103106
return new SqlServerDatabase(this, databaseConnectionString.ToString());
104107
}
108+
109+
private static string BuildCreateDatabaseSqlCommand(string name, SqlDatabaseCreationSettings? settings)
110+
{
111+
var sql = new StringBuilder($"CREATE DATABASE [{name}]");
112+
113+
if (settings is not null)
114+
{
115+
if (!string.IsNullOrEmpty(settings.DataFileName))
116+
{
117+
sql.Append($"ON (NAME = '{name}', FILENAME = '{settings.DataFileName}')");
118+
}
119+
}
120+
121+
return sql.ToString();
122+
}
105123
}
106124
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="SqlDatabaseCreationSettingsTest.cs" company="P.O.S Informatique">
3+
// Copyright (c) P.O.S Informatique. All rights reserved.
4+
// </copyright>
5+
//-----------------------------------------------------------------------
6+
7+
namespace PosInformatique.Testing.Databases.SqlServer.Tests
8+
{
9+
public class SqlDatabaseCreationSettingsTest
10+
{
11+
[Fact]
12+
public void Constructor()
13+
{
14+
var settings = new SqlDatabaseCreationSettings();
15+
16+
settings.DataFileName.Should().BeNull();
17+
}
18+
19+
[Fact]
20+
public void DataFileName_ValueChanged()
21+
{
22+
var settings = new SqlDatabaseCreationSettings();
23+
24+
settings.DataFileName = "New value";
25+
26+
settings.DataFileName.Should().Be("New value");
27+
}
28+
}
29+
}

tests/Testing.Databases.SqlServer.Tests/SqlServerTest.cs

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,166 @@ public void Constructor(string connectionString, string expectedMasterConnection
2222
server.Master.Server.Should().BeSameAs(server);
2323
}
2424

25-
[Fact]
26-
public async Task CreateAndDeleteAsync()
25+
[Theory]
26+
[InlineData(false)]
27+
[InlineData(true)]
28+
public async Task CreateAndDelete(bool withCreationSettings)
2729
{
2830
var server = new SqlServer(ConnectionString);
2931

30-
var database = await server.CreateEmptyDatabaseAsync("CreateAndDeleteDB", CancellationToken.None);
32+
SqlDatabaseCreationSettings settings = null;
33+
34+
if (withCreationSettings)
35+
{
36+
settings = new SqlDatabaseCreationSettings();
37+
}
38+
39+
var database = server.CreateEmptyDatabase("CreateAndDeleteDB", settings);
3140

3241
database.ConnectionString.Should().Be("Data Source=(localDB)\\posinfo-tests;Initial Catalog=CreateAndDeleteDB;Integrated Security=True");
3342

3443
var table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB'");
3544
table.Rows.Should().HaveCount(1);
3645

3746
// Delete the database
38-
await server.DeleteDatabaseAsync("CreateAndDeleteDB", CancellationToken.None);
47+
server.DeleteDatabase("CreateAndDeleteDB");
3948

4049
table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB'");
4150
table.Rows.Should().BeEmpty();
4251
}
52+
53+
[Fact]
54+
public async Task CreateAndDelete_WithSpecificName()
55+
{
56+
using var temporaryFolder = TemporaryFolder.Create();
57+
58+
var server = new SqlServer(ConnectionString);
59+
60+
var settings = new SqlDatabaseCreationSettings()
61+
{
62+
DataFileName = Path.Combine(temporaryFolder.Path, "TheSpecificName.mdf"),
63+
};
64+
65+
var database = server.CreateEmptyDatabase("CreateAndDeleteDB_WithSpecificName", settings);
66+
67+
database.ConnectionString.Should().Be("Data Source=(localDB)\\posinfo-tests;Initial Catalog=CreateAndDeleteDB_WithSpecificName;Integrated Security=True");
68+
69+
var table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB_WithSpecificName'");
70+
table.Rows.Should().HaveCount(1);
71+
72+
// Check the location of the database
73+
File.Exists(Path.Combine(temporaryFolder.Path, "TheSpecificName.mdf")).Should().BeTrue();
74+
File.Exists(Path.Combine(temporaryFolder.Path, "TheSpecificName_log.ldf")).Should().BeTrue();
75+
76+
var result = database.ExecuteQuery("SELECT * FROM [sys].[database_files] ORDER BY [physical_name]");
77+
78+
result.Rows.Should().HaveCount(2);
79+
80+
result.Rows[0]["name"].Should().Be("CreateAndDeleteDB_WithSpecificName");
81+
result.Rows[0]["physical_name"].Should().Be(Path.Combine(temporaryFolder.Path, "TheSpecificName.mdf"));
82+
result.Rows[0]["type_desc"].Should().Be("ROWS");
83+
84+
result.Rows[1]["name"].Should().Be("TheSpecificName_log");
85+
result.Rows[1]["physical_name"].Should().Be(Path.Combine(temporaryFolder.Path, "TheSpecificName_log.ldf"));
86+
result.Rows[1]["type_desc"].Should().Be("LOG");
87+
88+
// Delete the database
89+
server.DeleteDatabase("CreateAndDeleteDB_WithSpecificName");
90+
91+
table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB_WithSpecificName'");
92+
table.Rows.Should().BeEmpty();
93+
}
94+
95+
[Fact]
96+
public async Task CreateAndDeleteAsync()
97+
{
98+
var server = new SqlServer(ConnectionString);
99+
100+
var database = await server.CreateEmptyDatabaseAsync("CreateAndDeleteDBAsync", new SqlDatabaseCreationSettings(), CancellationToken.None);
101+
102+
database.ConnectionString.Should().Be("Data Source=(localDB)\\posinfo-tests;Initial Catalog=CreateAndDeleteDBAsync;Integrated Security=True");
103+
104+
var table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDBAsync'");
105+
table.Rows.Should().HaveCount(1);
106+
107+
// Delete the database
108+
await server.DeleteDatabaseAsync("CreateAndDeleteDBAsync", CancellationToken.None);
109+
110+
table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDBAsync'");
111+
table.Rows.Should().BeEmpty();
112+
}
113+
114+
[Fact]
115+
public async Task CreateAndDeleteAsync_WithSpecificName()
116+
{
117+
using var temporaryFolder = TemporaryFolder.Create();
118+
119+
var server = new SqlServer(ConnectionString);
120+
121+
var settings = new SqlDatabaseCreationSettings()
122+
{
123+
DataFileName = Path.Combine(temporaryFolder.Path, "TheSpecificNameAsync.mdf"),
124+
};
125+
126+
var database = await server.CreateEmptyDatabaseAsync("CreateAndDeleteDB_WithSpecificNameAsync", settings);
127+
128+
database.ConnectionString.Should().Be("Data Source=(localDB)\\posinfo-tests;Initial Catalog=CreateAndDeleteDB_WithSpecificNameAsync;Integrated Security=True");
129+
130+
var table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB_WithSpecificNameAsync'");
131+
table.Rows.Should().HaveCount(1);
132+
133+
// Check the location of the database
134+
File.Exists(Path.Combine(temporaryFolder.Path, "TheSpecificNameAsync.mdf")).Should().BeTrue();
135+
File.Exists(Path.Combine(temporaryFolder.Path, "TheSpecificNameAsync_log.ldf")).Should().BeTrue();
136+
137+
var result = database.ExecuteQuery("SELECT * FROM [sys].[database_files] ORDER BY [physical_name]");
138+
139+
result.Rows.Should().HaveCount(2);
140+
141+
result.Rows[0]["name"].Should().Be("CreateAndDeleteDB_WithSpecificNameAsync");
142+
result.Rows[0]["physical_name"].Should().Be(Path.Combine(temporaryFolder.Path, "TheSpecificNameAsync.mdf"));
143+
result.Rows[0]["type_desc"].Should().Be("ROWS");
144+
145+
result.Rows[1]["name"].Should().Be("TheSpecificNameAsync_log");
146+
result.Rows[1]["physical_name"].Should().Be(Path.Combine(temporaryFolder.Path, "TheSpecificNameAsync_log.ldf"));
147+
result.Rows[1]["type_desc"].Should().Be("LOG");
148+
149+
// Delete the database
150+
await server.DeleteDatabaseAsync("CreateAndDeleteDB_WithSpecificNameAsync");
151+
152+
table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB_WithSpecificNameAsync'");
153+
table.Rows.Should().BeEmpty();
154+
}
155+
156+
private sealed class TemporaryFolder : IDisposable
157+
{
158+
private TemporaryFolder(string path)
159+
{
160+
this.Path = path;
161+
}
162+
163+
public string Path { get; }
164+
165+
public static TemporaryFolder Create()
166+
{
167+
var temporaryFolder = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "PosInformatique.Testing.Databases.SqlServer.Tests", Guid.NewGuid().ToString());
168+
169+
Directory.CreateDirectory(temporaryFolder);
170+
171+
return new TemporaryFolder(temporaryFolder);
172+
}
173+
174+
public void Dispose()
175+
{
176+
try
177+
{
178+
Directory.Delete(this.Path, true);
179+
}
180+
catch (IOException)
181+
{
182+
// Ignore the errors.
183+
}
184+
}
185+
}
43186
}
44187
}

0 commit comments

Comments
 (0)