From 8e68758fb70cc1e30311973d00f8a4345b9a3d35 Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Tue, 1 Oct 2024 12:17:36 +0200 Subject: [PATCH 01/11] Add the badges in the README. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index af3e966..a63a094 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # PosInformatique.Testing.Databases +[![NuGet Version](https://img.shields.io/nuget/v/PosInformatique.Testing.Databases.SqlServer?label=PosInformatique.Testing.Databases.SqlServer)](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer) +[![NuGet Version](https://img.shields.io/nuget/v/PosInformatique.Testing.Databases.SqlServer.Dac?label=PosInformatique.Testing.Databases.SqlServer.Dac)](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.Dac) +[![NuGet Version](https://img.shields.io/nuget/v/PosInformatique.Testing.Databases.SqlServer.EntityFramework?label=PosInformatique.Testing.Databases.SqlServer.EntityFramework)](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.EntityFramework) + **PosInformatique.Testing.Databases** is a set of tools for testing databases. It simplifies writing and executing tests, helping ensure your database and data access code are reliable and bug-free. It is ideal for developers who want to validate data access based on SQL Server code during their development. From f6a24549e722fb990092254be76f95d2ba927d10 Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Tue, 1 Oct 2024 12:23:59 +0200 Subject: [PATCH 02/11] Add smileys for the titles. --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a63a094..c761dd4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PosInformatique.Testing.Databases +# PosInformatique.Testing.Databases [![NuGet Version](https://img.shields.io/nuget/v/PosInformatique.Testing.Databases.SqlServer?label=PosInformatique.Testing.Databases.SqlServer)](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer) [![NuGet Version](https://img.shields.io/nuget/v/PosInformatique.Testing.Databases.SqlServer.Dac?label=PosInformatique.Testing.Databases.SqlServer.Dac)](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.Dac) @@ -21,7 +21,7 @@ approach. Since the version 2.0.0 this tools provide a comparer to compare the schema of two SQL databases. -## The approach of these tools +## 💡 The approach of these tools The main approach of these tools is to perform tests without using mocking or in-memory alternatives for ADO .NET code or Entity Framework `DbContext`, instead using a real SQL Server database. @@ -38,7 +38,7 @@ You also miss technical behaviors like transactions, connection management, trig - You perform test cases, meaning you write simple tests to validate small features instead of writing complex integration tests. - When changing the schema of the database, it is important to test and have a *safeguard* to check that the migration script (or Entity Framework migration actions) will update the database to the expected schema. -## How to test a persistence layer +## 🧪 How to test a persistence layer To perform tests of a persistence layer, the approach is straightforward using the Arrange/Act/Assert pattern: @@ -61,7 +61,7 @@ Before each test (`TestMethod` or `Fact` methods): To write a test using this approach with the [PosInformatique.Testing.Databases](https://github.com/PosInformatique/PosInformatique.Testing.Databases) tools, see the [ Write tests to test the persistence layer](./docs/WriteTest.md) page. -## How to test database migration +## 🧪 How to test database migration To perform tests of a database migration, the approach is straightforward and required only a test which perform the following actions: @@ -91,7 +91,7 @@ migration code. It's better than nothing and very useful for detecting issues du To write a test using this approach with the [PosInformatique.Testing.Databases](https://github.com/PosInformatique/PosInformatique.Testing.Databases) tools, see the [Write tests to test database migration](./docs/WriteDatabaseMigrationTest.md) page. -## What do the PosInformatique.Testing.Databases tools provide? +## 📤 What do the PosInformatique.Testing.Databases tools provide? Using the previous approach, the [PosInformatique.Testing.Databases](https://github.com/PosInformatique/PosInformatique.Testing.Databases) libraries allow you to: @@ -104,7 +104,7 @@ Using the previous approach, the [PosInformatique.Testing.Databases](https://git - Contain a comparer tool to check schema differences between two databases. -## NuGet packages +## 📦 NuGet packages The [PosInformatique.Testing.Databases](https://github.com/PosInformatique/PosInformatique.Testing.Databases) tools are provided in two NuGet packages: @@ -119,7 +119,7 @@ The [PosInformatique.Testing.Databases](https://github.com/PosInformatique/PosIn - [PosInformatique.Testing.Databases.SqlServer.EntityFramework](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.EntityFramework) NuGet package which contains: - Tools to deploy a SQL Server database using a DbContext. -## Samples / Demo +## 🚀 Samples / Demo A complete sample solution is available in this repository inside the [samples](./samples) folder. @@ -129,7 +129,7 @@ The solution contains the following sample projects: - [DemoApp.DataAccessLayer.Tests](./samples/DemoApp.DataAccessLayer.Tests/DemoApp.DataAccessLayer.Tests.csproj): Test project to test the [DemoApp.DataAccessLayer](./samples/DemoApp.DataAccessLayer/DemoApp.DataAccessLayer.csproj) project using the [PosInformatique.Testing.Databases.SqlServer.EntityFramework](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.EntityFramework) package. -## Writing tests for a persistence layer +## 📝 Writing tests for a persistence layer To write tests for a persistence layer, follow the [Write tests to test the persistence layer](./docs/WriteTest.md) documentation page, which explains the different steps to perform using the [PosInformatique.Testing.Databases.SqlServer.EntityFramework](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.EntityFramework) library: @@ -147,7 +147,7 @@ using the [PosInformatique.Testing.Databases.SqlServer.EntityFramework](https:// - [Execute the tests](./docs/WriteTest.md#execute-the-tests) - [Check the database state after a test has failed](./docs/WriteTest.md#check-the-database-state-after-an-test-has-been-failed) -## Writing test to check database migration +## 📝 Writing test to check database migration To write an test to check the migration of database, follow the [Write tests to test database migration](./docs/WriteDatabaseMigrationTest.md) documentation page. From c74db721804a90ae8d27a929aeb59faa547bfcaa Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Tue, 1 Oct 2024 12:24:53 +0200 Subject: [PATCH 03/11] Fix documentation --- docs/WriteDatabaseMigrationTest.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/WriteDatabaseMigrationTest.md b/docs/WriteDatabaseMigrationTest.md index 2fe85ba..7e17d6a 100644 --- a/docs/WriteDatabaseMigrationTest.md +++ b/docs/WriteDatabaseMigrationTest.md @@ -87,7 +87,7 @@ public class DatabaseMigrationTest var targetDatabase = Task.Run(() => server.CreateDatabaseAsync(TargetDatabaseName, dbContext)); - // Wait both task + // Wait both tasks await Task.WhenAll(initialDatabase, targetDatabase); // Call the console application to perform migration of the "DemoApp_InitialDatabase" From 54c01f6faaaadd34c2f13b1b50a7b47b249ac940 Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Wed, 2 Oct 2024 10:29:57 +0200 Subject: [PATCH 04/11] Upgrade the NuGet packages --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 6bcddc1..db8ce6b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,9 +10,9 @@ - + - + \ No newline at end of file From 251bd62cdae338800f33402cef4815207c1cfb23 Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Wed, 2 Oct 2024 11:07:51 +0200 Subject: [PATCH 05/11] Migrate Testing.Databases.SqlServer to .NET Standard 2.0 --- .../Comparer/SqlDatabaseComparisonResults.cs | 18 ++- ...lDatabaseComparisonResultsTextGenerator.cs | 2 +- .../SqlObjectDifferences{TSqlObject}.cs | 2 +- .../Comparer/SqlServerDatabaseComparer.cs | 8 +- .../Comparer/SqlTableDifferences.cs | 2 +- .../Comparer/TsqlCodeHelper.cs | 2 +- .../ObjectModel/SqlCheckConstraint.cs | 8 +- .../ObjectModel/SqlColumn.cs | 36 ++++-- .../ObjectModel/SqlForeignKey.cs | 20 +++- .../ObjectModel/SqlForeignKeyColumn.cs | 11 +- .../ObjectModel/SqlIndex.cs | 12 +- .../ObjectModel/SqlIndexColumn.cs | 8 +- .../ObjectModel/SqlPrimaryKey.cs | 9 +- .../ObjectModel/SqlPrimaryKeyColumn.cs | 8 +- .../ObjectModel/SqlStoredProcedure.cs | 11 +- .../ObjectModel/SqlTable.cs | 19 +++- .../ObjectModel/SqlTrigger.cs | 10 +- .../ObjectModel/SqlUniqueConstraint.cs | 9 +- .../ObjectModel/SqlUserType.cs | 12 +- .../ObjectModel/SqlView.cs | 11 +- .../SqlServerDatabaseExtensions.cs | 4 +- .../SqlServerDatabaseObjectExtensions.cs | 105 +++++------------- .../Testing.Databases.SqlServer.csproj | 2 +- .../SqlDatabaseComparisonResultsTest.cs | 42 +------ .../Comparer/SqlObjectDifferencesTest.cs | 17 +-- .../ObjectModel/SqlCheckConstraintTest.cs | 6 +- .../ObjectModel/SqlColumnTest.cs | 15 +-- .../ObjectModel/SqlForeignKeyColumnTest.cs | 7 +- .../ObjectModel/SqlForeignKeyTest.cs | 8 +- .../ObjectModel/SqlIndexColumnTest.cs | 6 +- .../ObjectModel/SqlIndexTest.cs | 8 +- .../ObjectModel/SqlPrimaryKeyColumnTest.cs | 6 +- .../ObjectModel/SqlPrimaryKeyTest.cs | 6 +- .../ObjectModel/SqlStoredProcedureTest.cs | 7 +- .../ObjectModel/SqlTableTest.cs | 7 +- .../ObjectModel/SqlTriggerTest.cs | 6 +- .../ObjectModel/SqlUniqueConstraintTest.cs | 6 +- .../ObjectModel/SqlUserTypeTest.cs | 8 +- .../ObjectModel/SqlViewTest.cs | 7 +- 39 files changed, 193 insertions(+), 298 deletions(-) diff --git a/src/Testing.Databases.SqlServer/Comparer/SqlDatabaseComparisonResults.cs b/src/Testing.Databases.SqlServer/Comparer/SqlDatabaseComparisonResults.cs index 1b4b7b6..7ee796c 100644 --- a/src/Testing.Databases.SqlServer/Comparer/SqlDatabaseComparisonResults.cs +++ b/src/Testing.Databases.SqlServer/Comparer/SqlDatabaseComparisonResults.cs @@ -13,29 +13,37 @@ namespace PosInformatique.Testing.Databases /// public class SqlDatabaseComparisonResults { - internal SqlDatabaseComparisonResults() + internal SqlDatabaseComparisonResults( + IList> storedProcedures, + IList tables, + IList> userTypes, + IList> views) { + this.StoredProcedures = new ReadOnlyCollection>(storedProcedures); + this.Tables = new ReadOnlyCollection(tables); + this.UserTypes = new ReadOnlyCollection>(userTypes); + this.Views = new ReadOnlyCollection>(views); } /// /// Gets the stored procedures which are different between two databases. /// - public required ReadOnlyCollection> StoredProcedures { get; init; } + public ReadOnlyCollection> StoredProcedures { get; } /// /// Gets the tables which are different between two databases. /// - public required ReadOnlyCollection Tables { get; init; } + public ReadOnlyCollection Tables { get; } /// /// Gets the user types which are different between two databases. /// - public required ReadOnlyCollection> UserTypes { get; init; } + public ReadOnlyCollection> UserTypes { get; } /// /// Gets the views which are different between two databases. /// - public required ReadOnlyCollection> Views { get; init; } + public ReadOnlyCollection> Views { get; } /// /// Gets a value indicating whether if the two database compared have the same schema. diff --git a/src/Testing.Databases.SqlServer/Comparer/SqlDatabaseComparisonResultsTextGenerator.cs b/src/Testing.Databases.SqlServer/Comparer/SqlDatabaseComparisonResultsTextGenerator.cs index 638d892..b41b17c 100644 --- a/src/Testing.Databases.SqlServer/Comparer/SqlDatabaseComparisonResultsTextGenerator.cs +++ b/src/Testing.Databases.SqlServer/Comparer/SqlDatabaseComparisonResultsTextGenerator.cs @@ -219,7 +219,7 @@ private void WriteLine() private void WriteLine(string value) { - var lines = value.Split(Environment.NewLine); + var lines = value.Split(new[] { Environment.NewLine }, StringSplitOptions.None); if (lines.Length > 1) { diff --git a/src/Testing.Databases.SqlServer/Comparer/SqlObjectDifferences{TSqlObject}.cs b/src/Testing.Databases.SqlServer/Comparer/SqlObjectDifferences{TSqlObject}.cs index 656f7e2..2e748ad 100644 --- a/src/Testing.Databases.SqlServer/Comparer/SqlObjectDifferences{TSqlObject}.cs +++ b/src/Testing.Databases.SqlServer/Comparer/SqlObjectDifferences{TSqlObject}.cs @@ -49,7 +49,7 @@ internal SqlObjectDifferences(TSqlObject? source, TSqlObject? target, SqlObjectD /// /// Gets the property changes between and . /// - public ReadOnlyCollection Properties { get; init; } + public ReadOnlyCollection Properties { get; } /// /// Returns a textual representation of the result comparison. diff --git a/src/Testing.Databases.SqlServer/Comparer/SqlServerDatabaseComparer.cs b/src/Testing.Databases.SqlServer/Comparer/SqlServerDatabaseComparer.cs index 15011ac..c854c70 100644 --- a/src/Testing.Databases.SqlServer/Comparer/SqlServerDatabaseComparer.cs +++ b/src/Testing.Databases.SqlServer/Comparer/SqlServerDatabaseComparer.cs @@ -62,13 +62,7 @@ await Task.WhenAll( var userTypesDifferences = SqlObjectComparer.Compare(sourceUserTypes.Result, targetUserTypes.Result, ut => ut.Name); var viewsDifferences = SqlObjectComparer.Compare(sourceViews.Result, targetViews.Result, v => v.Schema + "." + v.Name); - return new SqlDatabaseComparisonResults() - { - StoredProcedures = new ReadOnlyCollection>(storedProceduresDifferences), - Tables = new ReadOnlyCollection(tablesDifferences), - UserTypes = new ReadOnlyCollection>(userTypesDifferences), - Views = new ReadOnlyCollection>(viewsDifferences), - }; + return new SqlDatabaseComparisonResults(storedProceduresDifferences, tablesDifferences, userTypesDifferences, viewsDifferences); } } } diff --git a/src/Testing.Databases.SqlServer/Comparer/SqlTableDifferences.cs b/src/Testing.Databases.SqlServer/Comparer/SqlTableDifferences.cs index 1aefe42..02dad79 100644 --- a/src/Testing.Databases.SqlServer/Comparer/SqlTableDifferences.cs +++ b/src/Testing.Databases.SqlServer/Comparer/SqlTableDifferences.cs @@ -58,7 +58,7 @@ internal SqlTableDifferences( /// /// Gets the primary key differences between the two SQL tables. /// - public required SqlPrimaryKeyDifferences? PrimaryKey { get; init; } + public SqlPrimaryKeyDifferences? PrimaryKey { get; internal set; } /// /// Gets the foreign keys differences between the two SQL tables. diff --git a/src/Testing.Databases.SqlServer/Comparer/TsqlCodeHelper.cs b/src/Testing.Databases.SqlServer/Comparer/TsqlCodeHelper.cs index a5b0ff2..d0cbed0 100644 --- a/src/Testing.Databases.SqlServer/Comparer/TsqlCodeHelper.cs +++ b/src/Testing.Databases.SqlServer/Comparer/TsqlCodeHelper.cs @@ -16,7 +16,7 @@ internal static class TsqlCodeHelper } return code - .ReplaceLineEndings(string.Empty) + .Replace(Environment.NewLine, string.Empty) .Replace(" ", string.Empty) .Replace("\t", string.Empty); } diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlCheckConstraint.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlCheckConstraint.cs index bb76e8f..1c0217c 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlCheckConstraint.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlCheckConstraint.cs @@ -11,19 +11,21 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlCheckConstraint : SqlObject { - internal SqlCheckConstraint() + internal SqlCheckConstraint(string name, string code) { + this.Name = name; + this.Code = code; } /// /// Gets the name of the check constraint type. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the code of the check constraint. /// - public required string Code { get; init; } + public string Code { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlColumn.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlColumn.cs index 450b2d2..5577349 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlColumn.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlColumn.cs @@ -11,64 +11,76 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlColumn : SqlObject { - internal SqlColumn() + internal SqlColumn( + string name, + int position, + string typeName, + short maxLength, + byte precision, + byte scale) { + this.Name = name; + this.Position = position; + this.TypeName = typeName; + this.MaxLength = maxLength; + this.Precision = precision; + this.Scale = scale; } /// /// Gets the name of the column. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the position of the column in the database. /// - public required int Position { get; init; } + public int Position { get; } /// /// Gets the type name of the column. /// - public required string TypeName { get; init; } + public string TypeName { get; } /// /// Gets the max length of the column. /// - public required short MaxLength { get; init; } + public short MaxLength { get; } /// /// Gets the precision of the column. /// - public required byte Precision { get; init; } + public byte Precision { get; } /// /// Gets the scale of the column. /// - public required byte Scale { get; init; } + public byte Scale { get; } /// /// Gets the collation name of the column. /// - public required string? CollationName { get; init; } + public string? CollationName { get; internal set; } /// /// Gets a value indicating whether if the column is nullable. /// - public required bool IsNullable { get; init; } + public bool IsNullable { get; internal set; } /// /// Gets a value indicating whether if the column is identity. /// - public required bool IsIdentity { get; init; } + public bool IsIdentity { get; internal set; } /// /// Gets a value indicating whether if the column is computed. /// - public required bool IsComputed { get; init; } + public bool IsComputed { get; internal set; } /// /// Gets the computed expression of the column. /// - public required string? ComputedExpression { get; init; } + public string? ComputedExpression { get; internal set; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlForeignKey.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlForeignKey.cs index 0319a0e..aa65f23 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlForeignKey.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlForeignKey.cs @@ -13,15 +13,25 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlForeignKey : SqlObject { - internal SqlForeignKey(IList columns) + internal SqlForeignKey( + string name, + string referencedTable, + string updateAction, + string deleteAction, + IList columns) { + this.Name = name; + this.ReferencedTable = referencedTable; + this.UpdateAction = updateAction; + this.DeleteAction = deleteAction; + this.Columns = new ReadOnlyCollection(columns); } /// /// Gets the name of the foreign key. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the columns of the foreign key. @@ -31,17 +41,17 @@ internal SqlForeignKey(IList columns) /// /// Gets the name of the referenced table. /// - public required string ReferencedTable { get; init; } + public string ReferencedTable { get; } /// /// Gets the referential update action. /// - public required string UpdateAction { get; init; } + public string UpdateAction { get; } /// /// Gets the referential delete action. /// - public required string DeleteAction { get; init; } + public string DeleteAction { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlForeignKeyColumn.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlForeignKeyColumn.cs index 7af9443..11c3a99 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlForeignKeyColumn.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlForeignKeyColumn.cs @@ -11,24 +11,27 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlForeignKeyColumn : SqlObject { - internal SqlForeignKeyColumn() + internal SqlForeignKeyColumn(string name, int position, string referenced) { + this.Name = name; + this.Position = position; + this.Referenced = referenced; } /// /// Gets the name of the column of the foreign key. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the name of the column referenced in the referenced table of the foreign key. /// - public required string Referenced { get; init; } + public string Referenced { get; } /// /// Gets the position of the column of the foreign key. /// - public required int Position { get; init; } + public int Position { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlIndex.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlIndex.cs index 31e6077..8ab32bf 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlIndex.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlIndex.cs @@ -13,8 +13,10 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlIndex : SqlObject { - internal SqlIndex(IList columns, IList includedColumns) + internal SqlIndex(string name, string type, IList columns, IList includedColumns) { + this.Name = name; + this.Type = type; this.Columns = new ReadOnlyCollection(columns); this.IncludedColumns = new ReadOnlyCollection(includedColumns); } @@ -22,7 +24,7 @@ internal SqlIndex(IList columns, IList includedC /// /// Gets the name of the index. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the columns of the index. @@ -37,17 +39,17 @@ internal SqlIndex(IList columns, IList includedC /// /// Gets the filter of the index. /// - public required string? Filter { get; init; } + public string? Filter { get; internal set; } /// /// Gets a value indicating whether if the index has unique values. /// - public required bool IsUnique { get; init; } + public bool IsUnique { get; internal set; } /// /// Gets the type of the index. /// - public required string Type { get; init; } + public string Type { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlIndexColumn.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlIndexColumn.cs index e143b4d..392625a 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlIndexColumn.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlIndexColumn.cs @@ -11,19 +11,21 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlIndexColumn : SqlObject { - internal SqlIndexColumn() + internal SqlIndexColumn(string name, int position) { + this.Name = name; + this.Position = position; } /// /// Gets the name of the column of the index. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the position of the column of the index. /// - public required int Position { get; init; } + public int Position { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlPrimaryKey.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlPrimaryKey.cs index 03b7998..ba8e159 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlPrimaryKey.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlPrimaryKey.cs @@ -13,20 +13,23 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlPrimaryKey : SqlObject { - internal SqlPrimaryKey(IList columns) + internal SqlPrimaryKey(string name, string type, IList columns) { + this.Name = name; + this.Type = type; + this.Columns = new ReadOnlyCollection(columns); } /// /// Gets the name of the primary key. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the type of the primary key. /// - public required string Type { get; init; } + public string Type { get; } /// /// Gets the columns which belong to the primary key. diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlPrimaryKeyColumn.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlPrimaryKeyColumn.cs index 79431f2..b81a484 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlPrimaryKeyColumn.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlPrimaryKeyColumn.cs @@ -11,19 +11,21 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlPrimaryKeyColumn : SqlObject { - internal SqlPrimaryKeyColumn() + internal SqlPrimaryKeyColumn(string name, byte position) { + this.Name = name; + this.Position = position; } /// /// Gets the name of the column of the primary key. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the position of the column of the primary key. /// - public required byte Position { get; init; } + public byte Position { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlStoredProcedure.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlStoredProcedure.cs index f8911cc..b64227f 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlStoredProcedure.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlStoredProcedure.cs @@ -11,24 +11,27 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlStoredProcedure : SqlObject { - internal SqlStoredProcedure() + internal SqlStoredProcedure(string schema, string name, string code) { + this.Schema = schema; + this.Name = name; + this.Code = code; } /// /// Gets the schema which the stored procedure belong to. /// - public required string Schema { get; init; } + public string Schema { get; } /// /// Gets the name of the stored procedure. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the SQL code of the stored procedure. /// - public required string Code { get; init; } + public string Code { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlTable.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlTable.cs index b31174b..9b628b8 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlTable.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlTable.cs @@ -13,8 +13,19 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlTable : SqlObject { - internal SqlTable(IList columns, IList triggers, IList checkConstraints, IList indexes, IList foreignKeys, IList uniqueConstraints) + internal SqlTable( + string schema, + string name, + IList columns, + IList triggers, + IList checkConstraints, + IList indexes, + IList foreignKeys, + IList uniqueConstraints) { + this.Schema = schema; + this.Name = name; + this.CheckConstraints = new ReadOnlyCollection(checkConstraints); this.Columns = new ReadOnlyCollection(columns); this.Indexes = new ReadOnlyCollection(indexes); @@ -26,7 +37,7 @@ internal SqlTable(IList columns, IList triggers, IList /// Gets the name of the table. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the check constraints of the table. @@ -51,12 +62,12 @@ internal SqlTable(IList columns, IList triggers, IList /// Gets the primary key of the table. /// - public required SqlPrimaryKey? PrimaryKey { get; init; } + public SqlPrimaryKey? PrimaryKey { get; internal set; } /// /// Gets the schema which the table belong to. /// - public required string Schema { get; init; } + public string Schema { get; } /// /// Gets the triggers of the table. diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlTrigger.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlTrigger.cs index 4733baa..1e325fe 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlTrigger.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlTrigger.cs @@ -11,24 +11,26 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlTrigger : SqlObject { - internal SqlTrigger() + internal SqlTrigger(string name, string code) { + this.Name = name; + this.Code = code; } /// /// Gets the name of the trigger. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets a value indicating whether if the trigger is an INSTEAD OF trigger. /// - public bool IsInsteadOfTrigger { get; init; } + public bool IsInsteadOfTrigger { get; internal set; } /// /// Gets the SQL code of the trigger. /// - public required string Code { get; init; } + public string Code { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlUniqueConstraint.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlUniqueConstraint.cs index c4ba4bd..cfff24b 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlUniqueConstraint.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlUniqueConstraint.cs @@ -13,15 +13,18 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlUniqueConstraint : SqlObject { - internal SqlUniqueConstraint(IList columns) + internal SqlUniqueConstraint(string name, string type, IList columns) { + this.Name = name; + this.Type = type; + this.Columns = new ReadOnlyCollection(columns); } /// /// Gets the name of the unique constraint. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the columns of the unique constraint. @@ -31,7 +34,7 @@ internal SqlUniqueConstraint(IList columns) /// /// Gets the type of the unique constraint. /// - public required string Type { get; init; } + public string Type { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlUserType.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlUserType.cs index 180958a..2386735 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlUserType.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlUserType.cs @@ -11,29 +11,31 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlUserType : SqlObject { - internal SqlUserType() + internal SqlUserType(string name, short maxLength) { + this.Name = name; + this.MaxLength = maxLength; } /// /// Gets the name of the user type. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the max length of the type. /// - public required short MaxLength { get; init; } + public short MaxLength { get; } /// /// Gets a value indicating whether is the type is nullable. /// - public required bool IsNullable { get; init; } + public bool IsNullable { get; internal set; } /// /// Gets a value indicating whether is the type is a table. /// - public required bool IsTableType { get; init; } + public bool IsTableType { get; internal set; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/ObjectModel/SqlView.cs b/src/Testing.Databases.SqlServer/ObjectModel/SqlView.cs index 04f563b..24632a3 100644 --- a/src/Testing.Databases.SqlServer/ObjectModel/SqlView.cs +++ b/src/Testing.Databases.SqlServer/ObjectModel/SqlView.cs @@ -11,24 +11,27 @@ namespace PosInformatique.Testing.Databases /// public sealed class SqlView : SqlObject { - internal SqlView() + internal SqlView(string schema, string name, string code) { + this.Schema = schema; + this.Name = name; + this.Code = code; } /// /// Gets the schema which the view belong to. /// - public required string Schema { get; init; } + public string Schema { get; } /// /// Gets the name of the view. /// - public required string Name { get; init; } + public string Name { get; } /// /// Gets the SQL code of the view. /// - public required string Code { get; init; } + public string Code { get; } /// public override TResult Accept(ISqlObjectVisitor visitor) => visitor.Visit(this); diff --git a/src/Testing.Databases.SqlServer/SqlServerDatabaseExtensions.cs b/src/Testing.Databases.SqlServer/SqlServerDatabaseExtensions.cs index c13471f..dd978f6 100644 --- a/src/Testing.Databases.SqlServer/SqlServerDatabaseExtensions.cs +++ b/src/Testing.Databases.SqlServer/SqlServerDatabaseExtensions.cs @@ -78,7 +78,7 @@ Type t when Array.Exists(AuthorizedNonStringTypes, at => at == t) => builder.Add }; } - if (!@object!.Equals(objects[^1])) + if (!@object!.Equals(objects[objects.Length - 1])) { builder.NewRecord(); } @@ -156,7 +156,7 @@ public SqlInsertStatementBuilder NewRecord() public SqlInsertStatementBuilder AddValue(byte[] value) { - var hexValue = BitConverter.ToString(value).Replace("-", string.Empty, StringComparison.InvariantCultureIgnoreCase); + var hexValue = BitConverter.ToString(value).Replace("-", string.Empty); this.currentRecord.Add($"0x{hexValue}"); diff --git a/src/Testing.Databases.SqlServer/SqlServerDatabaseObjectExtensions.cs b/src/Testing.Databases.SqlServer/SqlServerDatabaseObjectExtensions.cs index 10adaae..68d234e 100644 --- a/src/Testing.Databases.SqlServer/SqlServerDatabaseObjectExtensions.cs +++ b/src/Testing.Databases.SqlServer/SqlServerDatabaseObjectExtensions.cs @@ -93,7 +93,7 @@ ORDER BY await Task.WhenAll(allColumns, allCheckConstraints, allForeignKeys, allIndexes, allPrimaryKeys, allTriggers, allUniqueConstraints); // Builds the SqlTable object - foreach (var table in result.AsEnumerable()) + foreach (var table in result.Rows.Cast()) { // Check constraints var checkConstraintsTable = allCheckConstraints.Result[(int)table["Id"]]; @@ -192,11 +192,9 @@ ORDER BY } // Build and add the table object - tables.Add(new SqlTable(columns, triggers, checkConstraints, indexes, foreignKeys, uniqueConstraints) + tables.Add(new SqlTable((string)table["Schema"], (string)table["Name"], columns, triggers, checkConstraints, indexes, foreignKeys, uniqueConstraints) { - Name = (string)table["Name"], PrimaryKey = primaryKey, - Schema = (string)table["Schema"], }); } @@ -275,7 +273,7 @@ ORDER BY var result = await database.ExecuteQueryAsync(sql, cancellationToken); - return result.AsEnumerable().ToLookup(c => (int)c["TableId"]); + return result.Rows.Cast().ToLookup(c => (int)c["TableId"]); } private static async Task> GetColumnsAsync(SqlServerDatabase database, CancellationToken cancellationToken) @@ -307,7 +305,7 @@ [sys].[types] AS [ty] var result = await database.ExecuteQueryAsync(sql, cancellationToken); - return result.AsEnumerable().ToLookup(c => (int)c["TableId"]); + return result.Rows.Cast().ToLookup(c => (int)c["TableId"]); } private static async Task> GetForeignKeysAsync(SqlServerDatabase database, CancellationToken cancellationToken) @@ -342,7 +340,7 @@ ORDER BY var result = await database.ExecuteQueryAsync(sql, cancellationToken); - return result.AsEnumerable().ToLookup(c => (int)c["TableId"]); + return result.Rows.Cast().ToLookup(c => (int)c["TableId"]); } private static async Task> GetIndexesAsync(SqlServerDatabase database, CancellationToken cancellationToken) @@ -382,7 +380,7 @@ [sys].[columns] AS [c] var result = await database.ExecuteQueryAsync(sql, cancellationToken); - return result.AsEnumerable().ToLookup(c => (int)c["TableId"]); + return result.Rows.Cast().ToLookup(c => (int)c["TableId"]); } private static async Task> GetPrimaryKeysAsync(SqlServerDatabase database, CancellationToken cancellationToken) @@ -411,7 +409,7 @@ ORDER BY var result = await database.ExecuteQueryAsync(sql, cancellationToken); - return result.AsEnumerable().ToLookup(c => (int)c["TableId"]); + return result.Rows.Cast().ToLookup(c => (int)c["TableId"]); } private static async Task> GetUniqueConstraintsAsync(SqlServerDatabase database, CancellationToken cancellationToken) @@ -440,7 +438,7 @@ ORDER BY var result = await database.ExecuteQueryAsync(sql, cancellationToken); - return result.AsEnumerable().ToLookup(c => (int)c["TableId"]); + return result.Rows.Cast().ToLookup(c => (int)c["TableId"]); } private static async Task> GetTriggersAsync(SqlServerDatabase database, CancellationToken cancellationToken) @@ -462,75 +460,54 @@ [sys].[syscomments] AS [c] var result = await database.ExecuteQueryAsync(sql, cancellationToken); - return result.AsEnumerable().ToLookup(c => (int)c["TableId"]); + return result.Rows.Cast().ToLookup(c => (int)c["TableId"]); } private static SqlCheckConstraint ToCheckConstraint(DataRow row) { - return new SqlCheckConstraint() - { - Code = (string)row["Code"], - Name = (string)row["Name"], - }; + return new SqlCheckConstraint((string)row["Name"], (string)row["Code"]); } private static SqlColumn ToColumn(DataRow row) { - return new SqlColumn() + return new SqlColumn( + (string)row["Name"], + Convert.ToInt32(row["Position"], CultureInfo.InvariantCulture), + (string)row["TypeName"], + (short)row["MaxLength"], + (byte)row["Precision"], + (byte)row["Scale"]) { CollationName = NullIfDbNull(row["CollationName"]), ComputedExpression = NullIfDbNull(row["ComputedExpression"]), IsComputed = (bool)row["IsComputed"], IsIdentity = (bool)row["IsIdentity"], IsNullable = (bool)row["IsNullable"], - MaxLength = (short)row["MaxLength"], - Name = (string)row["Name"], - Precision = (byte)row["Precision"], - Position = Convert.ToInt32(row["Position"], CultureInfo.InvariantCulture), - Scale = (byte)row["Scale"], - TypeName = (string)row["TypeName"], }; } private static SqlForeignKey ToForeignKey(DataRow row, IList columns) { - return new SqlForeignKey(columns) - { - DeleteAction = (string)row["DeleteAction"], - Name = (string)row["ForeignKeyName"], - ReferencedTable = (string)row["ReferencedTableName"], - UpdateAction = (string)row["UpdateAction"], - }; + return new SqlForeignKey((string)row["ForeignKeyName"], (string)row["ReferencedTableName"], (string)row["UpdateAction"], (string)row["DeleteAction"], columns); } private static SqlForeignKeyColumn ToForeignKeyColumn(DataRow row) { - return new SqlForeignKeyColumn() - { - Name = (string)row["ColumnName"], - Referenced = (string)row["ReferencedColumnName"], - Position = (int)row["Position"], - }; + return new SqlForeignKeyColumn((string)row["ColumnName"], (int)row["Position"], (string)row["ReferencedColumnName"]); } private static SqlIndex ToIndex(DataRow row, IList columns, IList includedColumn) { - return new SqlIndex(columns, includedColumn) + return new SqlIndex((string)row["IndexName"], (string)row["Type"], columns, includedColumn) { - Name = (string)row["IndexName"], Filter = NullIfDbNull(row["Filter"]), IsUnique = (bool)row["IsUnique"], - Type = (string)row["Type"], }; } private static SqlIndexColumn ToIndexColumn(DataRow row) { - return new SqlIndexColumn() - { - Name = (string)row["ColumnName"], - Position = Convert.ToInt32(row["Position"], CultureInfo.InvariantCulture), - }; + return new SqlIndexColumn((string)row["ColumnName"], Convert.ToInt32(row["Position"], CultureInfo.InvariantCulture)); } private static SqlPrimaryKey? ToPrimaryKey(DataRow? row, IList columns) @@ -540,70 +517,44 @@ private static SqlIndexColumn ToIndexColumn(DataRow row) return null; } - return new SqlPrimaryKey(columns) - { - Name = (string)row["Name"], - Type = (string)row["Type"], - }; + return new SqlPrimaryKey((string)row["Name"], (string)row["Type"], columns); } private static SqlPrimaryKeyColumn ToPrimaryKeyColumn(DataRow column) { - return new SqlPrimaryKeyColumn() - { - Name = (string)column["ColumnName"], - Position = (byte)column["Position"], - }; + return new SqlPrimaryKeyColumn((string)column["ColumnName"], (byte)column["Position"]); } private static SqlStoredProcedure ToStoredProcedure(DataRow row) { - return new SqlStoredProcedure() - { - Code = (string)row["Code"], - Name = (string)row["Name"], - Schema = (string)row["Schema"], - }; + return new SqlStoredProcedure((string)row["Schema"], (string)row["Name"], (string)row["Code"]); } private static SqlTrigger ToTrigger(DataRow row) { - return new SqlTrigger() + return new SqlTrigger((string)row["Name"], (string)row["Code"]) { - Code = (string)row["Code"], IsInsteadOfTrigger = (bool)row["IsInsteadOfTrigger"], - Name = (string)row["Name"], }; } private static SqlUniqueConstraint ToUniqueConstraint(DataRow row, IList columns) { - return new SqlUniqueConstraint(columns) - { - Name = (string)row["ConstraintName"], - Type = (string)row["Type"], - }; + return new SqlUniqueConstraint((string)row["ConstraintName"], (string)row["Type"], columns); } private static SqlUserType ToUserType(DataRow row) { - return new SqlUserType() + return new SqlUserType((string)row["Name"], (short)row["MaxLength"]) { IsNullable = (bool)row["IsNullable"], IsTableType = (bool)row["IsTableType"], - Name = (string)row["Name"], - MaxLength = (short)row["MaxLength"], }; } private static SqlView ToView(DataRow row) { - return new SqlView() - { - Code = (string)row["Code"], - Name = (string)row["Name"], - Schema = (string)row["Schema"], - }; + return new SqlView((string)row["Schema"], (string)row["Name"], (string)row["Code"]); } private static TValue? NullIfDbNull(object value) diff --git a/src/Testing.Databases.SqlServer/Testing.Databases.SqlServer.csproj b/src/Testing.Databases.SqlServer/Testing.Databases.SqlServer.csproj index 6ef4241..cbd5830 100644 --- a/src/Testing.Databases.SqlServer/Testing.Databases.SqlServer.csproj +++ b/src/Testing.Databases.SqlServer/Testing.Databases.SqlServer.csproj @@ -1,7 +1,7 @@  - net8.0 + netstandard2.0 True Testing.Databases.SqlServer is a library that contains a set of tools for testing Data Access Layer (repositories) based on SQL Server. diff --git a/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlDatabaseComparisonResultsTest.cs b/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlDatabaseComparisonResultsTest.cs index 70e396f..8a33f82 100644 --- a/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlDatabaseComparisonResultsTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlDatabaseComparisonResultsTest.cs @@ -13,27 +13,15 @@ public class SqlDatabaseComparisonResultsTest [Fact] public void IsIdentical() { - var results = new SqlDatabaseComparisonResults() - { - StoredProcedures = new ReadOnlyCollection>([null]), - Tables = new ReadOnlyCollection([null]), - UserTypes = new ReadOnlyCollection>([null]), - Views = new ReadOnlyCollection>([null]), - }; + var results = new SqlDatabaseComparisonResults([], [], [], []); - results.IsIdentical.Should().BeFalse(); + results.IsIdentical.Should().BeTrue(); } [Fact] public void IsIdentical_StoredProcedure() { - var results = new SqlDatabaseComparisonResults() - { - StoredProcedures = new ReadOnlyCollection>([null]), - Tables = new ReadOnlyCollection([]), - UserTypes = new ReadOnlyCollection>([]), - Views = new ReadOnlyCollection>([]), - }; + var results = new SqlDatabaseComparisonResults([null], [], [], []); results.IsIdentical.Should().BeFalse(); } @@ -41,13 +29,7 @@ public void IsIdentical_StoredProcedure() [Fact] public void IsIdentical_Tables() { - var results = new SqlDatabaseComparisonResults() - { - StoredProcedures = new ReadOnlyCollection>([]), - Tables = new ReadOnlyCollection([null]), - UserTypes = new ReadOnlyCollection>([]), - Views = new ReadOnlyCollection>([]), - }; + var results = new SqlDatabaseComparisonResults([], [null], [], []); results.IsIdentical.Should().BeFalse(); } @@ -55,13 +37,7 @@ public void IsIdentical_Tables() [Fact] public void IsIdentical_UserTypes() { - var results = new SqlDatabaseComparisonResults() - { - StoredProcedures = new ReadOnlyCollection>([]), - Tables = new ReadOnlyCollection([]), - UserTypes = new ReadOnlyCollection>([null]), - Views = new ReadOnlyCollection>([]), - }; + var results = new SqlDatabaseComparisonResults([], [], [null], []); results.IsIdentical.Should().BeFalse(); } @@ -69,13 +45,7 @@ public void IsIdentical_UserTypes() [Fact] public void IsIdentical_Views() { - var results = new SqlDatabaseComparisonResults() - { - StoredProcedures = new ReadOnlyCollection>([]), - Tables = new ReadOnlyCollection([]), - UserTypes = new ReadOnlyCollection>([]), - Views = new ReadOnlyCollection>([null]), - }; + var results = new SqlDatabaseComparisonResults([], [], [], [null]); results.IsIdentical.Should().BeFalse(); } diff --git a/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectDifferencesTest.cs b/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectDifferencesTest.cs index 6de2880..bf57600 100644 --- a/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectDifferencesTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectDifferencesTest.cs @@ -11,21 +11,8 @@ public class SqlObjectDifferencesTest [Fact] public void ToStringTest() { - var source = new SqlUserType() - { - IsNullable = default, - IsTableType = default, - MaxLength = default, - Name = "The source", - }; - - var target = new SqlUserType() - { - IsNullable = default, - IsTableType = default, - MaxLength = default, - Name = "The source", - }; + var source = new SqlUserType("The source", default); + var target = new SqlUserType("The source", default); var properties = new[] { diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlCheckConstraintTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlCheckConstraintTest.cs index 02fff84..2f52b69 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlCheckConstraintTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlCheckConstraintTest.cs @@ -11,11 +11,7 @@ public class SqlCheckConstraintTest [Fact] public void ToStringTest() { - var checkConstraint = new SqlCheckConstraint() - { - Name = "The name", - Code = default, - }; + var checkConstraint = new SqlCheckConstraint("The name", default); checkConstraint.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlColumnTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlColumnTest.cs index bca7032..4902460 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlColumnTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlColumnTest.cs @@ -11,20 +11,7 @@ public class SqlColumnTest [Fact] public void ToStringTest() { - var column = new SqlColumn() - { - Name = "The name", - CollationName = default, - ComputedExpression = default, - IsComputed = default, - IsIdentity = default, - IsNullable = default, - MaxLength = default, - Position = default, - Precision = default, - Scale = default, - TypeName = default, - }; + var column = new SqlColumn("The name", default, default, default, default, default); column.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlForeignKeyColumnTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlForeignKeyColumnTest.cs index 1283ba8..198d28d 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlForeignKeyColumnTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlForeignKeyColumnTest.cs @@ -11,12 +11,7 @@ public class SqlForeignKeyColumnTest [Fact] public void ToStringTest() { - var column = new SqlForeignKeyColumn() - { - Name = "The name", - Referenced = "The referenced", - Position = default, - }; + var column = new SqlForeignKeyColumn("The name", default, "The referenced"); column.ToString().Should().Be("The name => The referenced"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlForeignKeyTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlForeignKeyTest.cs index 615c15f..535c492 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlForeignKeyTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlForeignKeyTest.cs @@ -11,13 +11,7 @@ public class SqlForeignKeyTest [Fact] public void ToStringTest() { - var foreignKey = new SqlForeignKey([]) - { - DeleteAction = default, - ReferencedTable = default, - UpdateAction = default, - Name = "The name", - }; + var foreignKey = new SqlForeignKey("The name", default, default, default, []); foreignKey.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlIndexColumnTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlIndexColumnTest.cs index ff92abc..80c2a53 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlIndexColumnTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlIndexColumnTest.cs @@ -11,11 +11,7 @@ public class SqlIndexColumnTest [Fact] public void ToStringTest() { - var column = new SqlIndexColumn() - { - Name = "The name", - Position = default, - }; + var column = new SqlIndexColumn("The name", default); column.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlIndexTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlIndexTest.cs index 3fd7b0d..9214047 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlIndexTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlIndexTest.cs @@ -11,13 +11,7 @@ public class SqlIndexTest [Fact] public void ToStringTest() { - var index = new SqlIndex(Array.Empty(), Array.Empty()) - { - Filter = null, - IsUnique = default, - Name = "The name", - Type = default, - }; + var index = new SqlIndex("The name", default, Array.Empty(), Array.Empty()); index.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlPrimaryKeyColumnTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlPrimaryKeyColumnTest.cs index 42861da..57ca793 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlPrimaryKeyColumnTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlPrimaryKeyColumnTest.cs @@ -11,11 +11,7 @@ public class SqlPrimaryKeyColumnTest [Fact] public void ToStringTest() { - var column = new SqlPrimaryKeyColumn() - { - Name = "The name", - Position = default, - }; + var column = new SqlPrimaryKeyColumn("The name", default); column.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlPrimaryKeyTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlPrimaryKeyTest.cs index 5f4fe81..0a7c164 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlPrimaryKeyTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlPrimaryKeyTest.cs @@ -11,11 +11,7 @@ public class SqlPrimaryKeyTest [Fact] public void ToStringTest() { - var primaryKey = new SqlPrimaryKey([]) - { - Name = "The name", - Type = default, - }; + var primaryKey = new SqlPrimaryKey("The name", default, []); primaryKey.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlStoredProcedureTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlStoredProcedureTest.cs index b4374da..33eb395 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlStoredProcedureTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlStoredProcedureTest.cs @@ -11,12 +11,7 @@ public class SqlStoredProcedureTest [Fact] public void ToStringTest() { - var storedProcedure = new SqlStoredProcedure() - { - Name = "The name", - Code = default, - Schema = "The schema", - }; + var storedProcedure = new SqlStoredProcedure("The schema", "The name", default); storedProcedure.ToString().Should().Be("The schema.The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlTableTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlTableTest.cs index 57c57b7..e854dbd 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlTableTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlTableTest.cs @@ -11,12 +11,7 @@ public class SqlTableTest [Fact] public void ToStringTest() { - var table = new SqlTable([], [], [], [], [], []) - { - Name = "The name", - PrimaryKey = default, - Schema = "The schema", - }; + var table = new SqlTable("The schema", "The name", [], [], [], [], [], []); table.ToString().Should().Be("The schema.The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlTriggerTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlTriggerTest.cs index c8d2eab..3c35de4 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlTriggerTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlTriggerTest.cs @@ -11,11 +11,7 @@ public class SqlTriggerTest [Fact] public void ToStringTest() { - var trigger = new SqlTrigger() - { - Name = "The name", - Code = default, - }; + var trigger = new SqlTrigger("The name", default); trigger.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlUniqueConstraintTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlUniqueConstraintTest.cs index 06e9afa..23ba690 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlUniqueConstraintTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlUniqueConstraintTest.cs @@ -11,11 +11,7 @@ public class SqlUniqueConstraintTest [Fact] public void ToStringTest() { - var uniqueConstraint = new SqlUniqueConstraint([]) - { - Name = "The name", - Type = default, - }; + var uniqueConstraint = new SqlUniqueConstraint("The name", default, []); uniqueConstraint.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlUserTypeTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlUserTypeTest.cs index ad5e925..ee34a2b 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlUserTypeTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlUserTypeTest.cs @@ -11,13 +11,7 @@ public class SqlUserTypeTest [Fact] public void ToStringTest() { - var userType = new SqlUserType() - { - Name = "The name", - IsNullable = default, - IsTableType = default, - MaxLength = default, - }; + var userType = new SqlUserType("The name", default); userType.ToString().Should().Be("The name"); } diff --git a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlViewTest.cs b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlViewTest.cs index af9cdbb..bddc526 100644 --- a/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlViewTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/ObjectModel/SqlViewTest.cs @@ -11,12 +11,7 @@ public class SqlViewTest [Fact] public void ToStringTest() { - var view = new SqlView() - { - Name = "The name", - Code = default, - Schema = "The schema", - }; + var view = new SqlView("The schema", "The name", default); view.ToString().Should().Be("The schema.The name"); } From 8b55b58467d7bbc4de9b4368404d7d9ef90c140f Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Wed, 2 Oct 2024 11:28:59 +0200 Subject: [PATCH 06/11] Reduce the dependencies to .NET 6.0 for Entity Framework and .NET 6 + .NET Framework 4.6.2 for DAC. --- Directory.Packages.props | 8 ++++---- .../Testing.Databases.SqlServer.Dac.csproj | 2 +- .../Testing.Databases.SqlServer.EntityFramework.csproj | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index db8ce6b..0921fcd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,11 +6,11 @@ - - - + + + - + diff --git a/src/Testing.Databases.SqlServer.Dac/Testing.Databases.SqlServer.Dac.csproj b/src/Testing.Databases.SqlServer.Dac/Testing.Databases.SqlServer.Dac.csproj index 63d2390..205776f 100644 --- a/src/Testing.Databases.SqlServer.Dac/Testing.Databases.SqlServer.Dac.csproj +++ b/src/Testing.Databases.SqlServer.Dac/Testing.Databases.SqlServer.Dac.csproj @@ -1,7 +1,7 @@  - net8.0 + net6.0;net462 Testing.Databases.SqlServer.Dac is a library that contains a set of tools for testing to deploy DAC (Data-tier Applications) packages (.dacpac files). testing unittest sqlserver repository tdd dataaccesslayer dacpac dac diff --git a/src/Testing.Databases.SqlServer.EntityFramework/Testing.Databases.SqlServer.EntityFramework.csproj b/src/Testing.Databases.SqlServer.EntityFramework/Testing.Databases.SqlServer.EntityFramework.csproj index 403ab7e..5c8d46d 100644 --- a/src/Testing.Databases.SqlServer.EntityFramework/Testing.Databases.SqlServer.EntityFramework.csproj +++ b/src/Testing.Databases.SqlServer.EntityFramework/Testing.Databases.SqlServer.EntityFramework.csproj @@ -1,7 +1,7 @@  - net8.0 + net6.0 True Testing.Databases.SqlServer.EntityFramework is a library that contains a set of tools for testing Data Access Layer (repositories) based on Entity Framework and SQL Server. From 7622a25033c2895582571dbd20f89f7906e00c46 Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Wed, 2 Oct 2024 11:30:45 +0200 Subject: [PATCH 07/11] Update the version to 2.1.0 --- .github/workflows/github-actions-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-release.yml b/.github/workflows/github-actions-release.yml index 11e893c..9a2fb15 100644 --- a/.github/workflows/github-actions-release.yml +++ b/.github/workflows/github-actions-release.yml @@ -7,7 +7,7 @@ on: type: string description: The version of the library required: true - default: 2.0.0 + default: 2.1.0 VersionSuffix: type: string description: The version suffix of the library (for example rc.1) From 758f901ee94f164719a70ccd8371b2183e47f7a4 Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Wed, 2 Oct 2024 14:54:04 +0200 Subject: [PATCH 08/11] Add an extension method ExecuteScript() to execute a SQL script. --- src/Directory.Build.props | 24 ++++-- .../SqlServerDatabaseExtensions.cs | 34 +++++++++ .../SqlServerScriptBlock.cs | 21 ++++++ .../SqlServerScriptParser.cs | 73 +++++++++++++++++++ .../SqlServerDatabaseExtensionsTest.cs | 66 +++++++++++++++++ 5 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 src/Testing.Databases.SqlServer/SqlServerScriptBlock.cs create mode 100644 src/Testing.Databases.SqlServer/SqlServerScriptParser.cs diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 688bf32..0d8078c 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -11,11 +11,25 @@ README.md MIT - 1.0.1 - - Fix the documentation - - 1.0.0 - - Initial version + 2.1.0 + - PosInformatique.Testing.Databases.SqlServer target the .NET Standard 2.0 platform. + - PosInformatique.Testing.Databases.SqlServer.Dac target the .NET Core 6.0 and .NET Framework 4.6.2 + - PosInformatique.Testing.Databases.SqlServer.EntityFramework target the .NET Core 6.0 + - Reduce the dependencies to Entity Framework 6.0 + - Reduce the dependencies of DACfx to a more earlier version. + - Add new method SqlServerDatabase.ExecuteScript() to execute T-SQL script. + + 2.0.0 + - Add SqlServerDatabaseComparer class to perform comparison between two databases. + - Add new PosInformatique.Testing.Databases.SqlServer.Dac NuGet package which contains DAC package tools. + - Add new SqlServer.CreateDatabaseAsync() extension method to create a database from a DbContext. + - Reduce dependencies version of the Entity Framework Core and SQL Server Client NuGet packages. + + 1.0.1 + - Fix the documentation + + 1.0.0 + - Initial version diff --git a/src/Testing.Databases.SqlServer/SqlServerDatabaseExtensions.cs b/src/Testing.Databases.SqlServer/SqlServerDatabaseExtensions.cs index dd978f6..2026d2a 100644 --- a/src/Testing.Databases.SqlServer/SqlServerDatabaseExtensions.cs +++ b/src/Testing.Databases.SqlServer/SqlServerDatabaseExtensions.cs @@ -130,6 +130,40 @@ [sys].[identity_columns] AS [ic] database.ExecuteNonQuery("EXEC sp_msforeachtable 'ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all'"); } + /// + /// Execute an T-SQL script on the . + /// + /// where the will be executed. + /// T-SQL script to execute. + public static void ExecuteScript(this SqlServerDatabase database, string script) + { + using var stringReader = new StringReader(script); + + ExecuteScript(database, stringReader); + } + + /// + /// Execute an T-SQL script on the . + /// + /// where the will be executed. + /// which contains the T-SQL script to execute. + public static void ExecuteScript(this SqlServerDatabase database, StringReader script) + { + var parser = new SqlServerScriptParser(script); + + var block = parser.ReadNextBlock(); + + while (block is not null) + { + for (var i = 0; i < block.Count; i++) + { + database.ExecuteNonQuery(block.Code); + } + + block = parser.ReadNextBlock(); + } + } + private sealed class SqlInsertStatementBuilder { private readonly string tableName; diff --git a/src/Testing.Databases.SqlServer/SqlServerScriptBlock.cs b/src/Testing.Databases.SqlServer/SqlServerScriptBlock.cs new file mode 100644 index 0000000..b0aeec9 --- /dev/null +++ b/src/Testing.Databases.SqlServer/SqlServerScriptBlock.cs @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) P.O.S Informatique. All rights reserved. +// +//----------------------------------------------------------------------- + +namespace PosInformatique.Testing.Databases.SqlServer +{ + internal class SqlServerScriptBlock + { + public SqlServerScriptBlock(string code, int count) + { + this.Code = code; + this.Count = count; + } + + public string Code { get; } + + public int Count { get; } + } +} diff --git a/src/Testing.Databases.SqlServer/SqlServerScriptParser.cs b/src/Testing.Databases.SqlServer/SqlServerScriptParser.cs new file mode 100644 index 0000000..0ee91d0 --- /dev/null +++ b/src/Testing.Databases.SqlServer/SqlServerScriptParser.cs @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) P.O.S Informatique. All rights reserved. +// +//----------------------------------------------------------------------- + +namespace PosInformatique.Testing.Databases.SqlServer +{ + using System.Globalization; + using System.Text; + + internal sealed class SqlServerScriptParser + { + private readonly TextReader script; + + private bool isEndOfScript; + + public SqlServerScriptParser(TextReader script) + { + this.script = script; + } + + public SqlServerScriptBlock? ReadNextBlock() + { + if (this.isEndOfScript) + { + return null; + } + + var codeBuilder = new StringBuilder(); + + var count = 1; + + while (true) + { + var line = this.script.ReadLine(); + + if (line is null) + { + // End of the script reach. + this.isEndOfScript = true; + break; + } + + line = line.Trim(); + + if (line.StartsWith("GO")) + { + // Parse the number after the "GO". + var textAfterGo = line.Substring(2).Trim(); + + if (textAfterGo != string.Empty) + { + count = Convert.ToInt32(textAfterGo, CultureInfo.InvariantCulture); + } + + // If no code parsed, we continue to parse the block. + if (codeBuilder.Length == 0) + { + continue; + } + + // Else, we stop the read of the script. + break; + } + + codeBuilder.AppendLine(line); + } + + return new SqlServerScriptBlock(codeBuilder.ToString(), count); + } + } +} diff --git a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs index d4379a4..538041a 100644 --- a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs @@ -90,5 +90,71 @@ Binary VARBINARY(MAX) NULL, table.Rows[1]["Boolean"].As().Should().Be(false); table.Rows[1]["BooleanNull"].Should().Be(DBNull.Value); } + + [Fact] + public void ExecuteScript_String() + { + var server = new SqlServer(ConnectionString); + + var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest"); + + database.ExecuteScript(@" + CREATE TABLE TableTest + ( + Id INT NOT NULL + ) + + GO + GO + + INSERT INTO [TableTest] ([Id]) VALUES (0) + + GO + UPDATE [TableTest] + SET [Id] = [Id] + 1 + + GO 10 + + "); + + var table = database.ExecuteQuery("SELECT * FROM [TableTest]"); + + table.Rows.Should().HaveCount(1); + + table.Rows[0]["Id"].Should().Be(10); + } + + [Fact] + public void ExecuteScript_StringReader() + { + var server = new SqlServer(ConnectionString); + + var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest"); + + database.ExecuteScript(new StringReader(@" + CREATE TABLE TableTest + ( + Id INT NOT NULL + ) + + GO + GO + + INSERT INTO [TableTest] ([Id]) VALUES (0) + + GO + UPDATE [TableTest] + SET [Id] = [Id] + 1 + + GO 10 + + ")); + + var table = database.ExecuteQuery("SELECT * FROM [TableTest]"); + + table.Rows.Should().HaveCount(1); + + table.Rows[0]["Id"].Should().Be(10); + } } } \ No newline at end of file From 01f99627158676137cedfb576d87700e4f005ca1 Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Thu, 17 Oct 2024 08:52:06 +0200 Subject: [PATCH 09/11] Fix a bug when parsing the GO instruction. --- .../SqlServerScriptParser.cs | 20 ++++-- .../SqlServerDatabaseExtensionsTest.cs | 63 +++++++++++++++++++ 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/Testing.Databases.SqlServer/SqlServerScriptParser.cs b/src/Testing.Databases.SqlServer/SqlServerScriptParser.cs index 0ee91d0..7375344 100644 --- a/src/Testing.Databases.SqlServer/SqlServerScriptParser.cs +++ b/src/Testing.Databases.SqlServer/SqlServerScriptParser.cs @@ -8,9 +8,12 @@ namespace PosInformatique.Testing.Databases.SqlServer { using System.Globalization; using System.Text; + using System.Text.RegularExpressions; internal sealed class SqlServerScriptParser { + private static readonly Regex GoInstruction = new Regex("^GO\\s*(?\\d+)?\\b", RegexOptions.Compiled); + private readonly TextReader script; private bool isEndOfScript; @@ -44,14 +47,14 @@ public SqlServerScriptParser(TextReader script) line = line.Trim(); - if (line.StartsWith("GO")) - { - // Parse the number after the "GO". - var textAfterGo = line.Substring(2).Trim(); + var goInstructionMatch = GoInstruction.Match(line); - if (textAfterGo != string.Empty) + if (goInstructionMatch.Success) + { + // Retrieve the number after the "GO". + if (goInstructionMatch.Groups["count"].Success) { - count = Convert.ToInt32(textAfterGo, CultureInfo.InvariantCulture); + count = Convert.ToInt32(goInstructionMatch.Groups["count"].Value, CultureInfo.InvariantCulture); } // If no code parsed, we continue to parse the block. @@ -67,6 +70,11 @@ public SqlServerScriptParser(TextReader script) codeBuilder.AppendLine(line); } + if (codeBuilder.Length == 0) + { + return null; + } + return new SqlServerScriptBlock(codeBuilder.ToString(), count); } } diff --git a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs index 538041a..ffdfdde 100644 --- a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs +++ b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs @@ -98,6 +98,37 @@ public void ExecuteScript_String() var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest"); + database.ExecuteScript(@" + CREATE TABLE TableTest + ( + Id INT NOT NULL + ) + + GO + GO + + INSERT INTO [TableTest] ([Id]) VALUES (0) + + GO + UPDATE [TableTest] + SET [Id] = [Id] + 1 + + GO 10"); + + var table = database.ExecuteQuery("SELECT * FROM [TableTest]"); + + table.Rows.Should().HaveCount(1); + + table.Rows[0]["Id"].Should().Be(10); + } + + [Fact] + public void ExecuteScript_String_WithEmptyLinesAtTheEnd() + { + var server = new SqlServer(ConnectionString); + + var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest"); + database.ExecuteScript(@" CREATE TABLE TableTest ( @@ -131,6 +162,37 @@ public void ExecuteScript_StringReader() var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest"); + database.ExecuteScript(new StringReader(@" + CREATE TABLE TableTest + ( + Id INT NOT NULL + ) + + GO + GO + + INSERT INTO [TableTest] ([Id]) VALUES (0) + + GO + UPDATE [TableTest] + SET [Id] = [Id] + 1 + + GO 10")); + + var table = database.ExecuteQuery("SELECT * FROM [TableTest]"); + + table.Rows.Should().HaveCount(1); + + table.Rows[0]["Id"].Should().Be(10); + } + + [Fact] + public void ExecuteScript_StringReader_WithEmptyLinesAtTheEnd() + { + var server = new SqlServer(ConnectionString); + + var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest"); + database.ExecuteScript(new StringReader(@" CREATE TABLE TableTest ( @@ -148,6 +210,7 @@ UPDATE [TableTest] GO 10 + ")); var table = database.ExecuteQuery("SELECT * FROM [TableTest]"); From 9ff762d874bd4250d4bb5df41733f9b06c5da312 Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Thu, 17 Oct 2024 09:10:20 +0200 Subject: [PATCH 10/11] Add the documentation and samples for the ExecuteScript() method. --- docs/WriteTest.md | 35 +++++++++++++++++++ .../CustomerRepositoryTest.cs | 4 +++ .../DemoApp.DataAccessLayer.Tests.csproj | 12 ++++++- .../InsertData.sql | 4 +++ samples/Directory.Packages.props | 10 +++--- src/Directory.Build.props | 2 +- 6 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 samples/DemoApp.DataAccessLayer.Tests/InsertData.sql diff --git a/docs/WriteTest.md b/docs/WriteTest.md index 60cc4f3..1be28db 100644 --- a/docs/WriteTest.md +++ b/docs/WriteTest.md @@ -298,6 +298,41 @@ public CustomerRepositoryTest(SqlServerDatabaseInitializer initializer) Now, every time we will execute an test in the `CustomerRepositoryTest` class, a database will be deployed with these 3 `Customer` before the execution of the test. +### Initializes the data from a T-SQL script + +Since the version 2.1.0, it is possible to execute a T-SQL script by using the `SqlServerDatabase.ExecuteScript()` method. + +For example, if we want to initialize the previous customers using a T-SQL, add a `.sql` file in the project with the following code: + +```sql +INSERT INTO [Customer] ([FirstName], [LastName], [Revenue]) +VALUES + ('John', 'DOE', 110.50), + ('Marcel', 'DUPONT', 4852.45), + ('Andres', 'GARCIA', 0) +``` + +In Visual Studio, set the following project properties for the `InsertData.sql` file: +- `Build Action`: `Content`. This option will recompile the test project every time the T-SQL script has been changed. +- `Copy to Output Directory`: `Copy if newer`. This option allows the compiler to copy the file in the output directory and +make it available for the tests in the current directory when execution the tests. + +In the constructor of the `CustomerRepositoryTest` call the script using the `SqlServerDatabase.ExecuteScript()` method. + +```csharp +public CustomerRepositoryTest(SqlServerDatabaseInitializer initializer) +{ + using var dbContext = new DemoAppDbContext(DatabaseTestsConnectionStrings.CreateDbContextOptions(DatabaseName)); + + this.database = initializer.Initialize(dbContext); + + this.database.ExecuteScript(File.ReadAllText("InsertData.sql")); +} +``` + +Now, every time we will execute an test in the `CustomerRepositoryTest` class, +a database will be deployed and the `InsertData.sql` will be executed. + ## Write the tests for methods that retrieve data This section describes how to write an test for methods that retrieve data (SELECT queries). diff --git a/samples/DemoApp.DataAccessLayer.Tests/CustomerRepositoryTest.cs b/samples/DemoApp.DataAccessLayer.Tests/CustomerRepositoryTest.cs index 4124519..7838f51 100644 --- a/samples/DemoApp.DataAccessLayer.Tests/CustomerRepositoryTest.cs +++ b/samples/DemoApp.DataAccessLayer.Tests/CustomerRepositoryTest.cs @@ -35,6 +35,10 @@ public CustomerRepositoryTest(SqlServerDatabaseInitializer initializer) // Also, we recommand to force to set the IDENTITY column values explicit to avoid // to update lot of code if you delete some rows later... this.database.InsertCustomer(id: 20, firstName: "Andres", lastName: "GARCIA"); + + // - Here we use a T-SQL script to insert data in the Customer table. + // The script can contains GO instructions. + this.database.ExecuteScript(File.ReadAllText("InsertData.sql")); } [Fact] diff --git a/samples/DemoApp.DataAccessLayer.Tests/DemoApp.DataAccessLayer.Tests.csproj b/samples/DemoApp.DataAccessLayer.Tests/DemoApp.DataAccessLayer.Tests.csproj index a4d5b7e..2ebda52 100644 --- a/samples/DemoApp.DataAccessLayer.Tests/DemoApp.DataAccessLayer.Tests.csproj +++ b/samples/DemoApp.DataAccessLayer.Tests/DemoApp.DataAccessLayer.Tests.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -9,6 +9,16 @@ true + + + + + + + PreserveNewest + + + diff --git a/samples/DemoApp.DataAccessLayer.Tests/InsertData.sql b/samples/DemoApp.DataAccessLayer.Tests/InsertData.sql new file mode 100644 index 0000000..831a121 --- /dev/null +++ b/samples/DemoApp.DataAccessLayer.Tests/InsertData.sql @@ -0,0 +1,4 @@ +INSERT INTO [Customer] ([FirstName], [LastName], [Revenue]) + VALUES ('From script', 'Peter', 100) + +GO 10 \ No newline at end of file diff --git a/samples/Directory.Packages.props b/samples/Directory.Packages.props index 6728226..17c9a48 100644 --- a/samples/Directory.Packages.props +++ b/samples/Directory.Packages.props @@ -6,14 +6,14 @@ - - - + + + - + - + \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 0d8078c..2689e88 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -17,7 +17,7 @@ - PosInformatique.Testing.Databases.SqlServer.EntityFramework target the .NET Core 6.0 - Reduce the dependencies to Entity Framework 6.0 - Reduce the dependencies of DACfx to a more earlier version. - - Add new method SqlServerDatabase.ExecuteScript() to execute T-SQL script. + - Add new method SqlServerDatabase.ExecuteScript() to execute T-SQL scripts. 2.0.0 - Add SqlServerDatabaseComparer class to perform comparison between two databases. From 45894b8b448bfda71e904a0f99499679488e5104 Mon Sep 17 00:00:00 2001 From: Gilles TOURREAU Date: Thu, 17 Oct 2024 10:45:30 +0200 Subject: [PATCH 11/11] Improve the samples code. --- samples/DemoApp.DataAccessLayer.Tests/InsertData.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/DemoApp.DataAccessLayer.Tests/InsertData.sql b/samples/DemoApp.DataAccessLayer.Tests/InsertData.sql index 831a121..35234ff 100644 --- a/samples/DemoApp.DataAccessLayer.Tests/InsertData.sql +++ b/samples/DemoApp.DataAccessLayer.Tests/InsertData.sql @@ -1,4 +1,4 @@ INSERT INTO [Customer] ([FirstName], [LastName], [Revenue]) VALUES ('From script', 'Peter', 100) -GO 10 \ No newline at end of file +GO 10 -- Execute the previous insert 10x times. \ No newline at end of file