diff --git a/.github/workflows/github-actions-ci.yaml b/.github/workflows/github-actions-ci.yaml
index 7457777..dad5f40 100644
--- a/.github/workflows/github-actions-ci.yaml
+++ b/.github/workflows/github-actions-ci.yaml
@@ -8,6 +8,66 @@ on:
jobs:
build:
+ runs-on: ubuntu-latest
+ permissions:
+ checks: write
+ pull-requests: write
+ services:
+ sqlserver:
+ image: mcr.microsoft.com/mssql/server:2022-latest
+ env:
+ SA_PASSWORD: "P@ssw0rd12345!"
+ ACCEPT_EULA: "Y"
+ ports:
+ - 1433:1433
+ volumes:
+ - /tmp/other_databases_path:/tmp/other_databases_path
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.0.x
+
+ - name: Restore dependencies
+ run: dotnet restore PosInformatique.Testing.Databases.sln
+
+ - name: Build solution
+ run: dotnet build PosInformatique.Testing.Databases.sln --configuration Release --no-restore
+
+ - name: Install SQL Server command-line tools
+ run: |
+ curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
+ curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list
+ sudo apt-get update
+ sudo apt-get install -y mssql-tools
+ echo "/opt/mssql-tools/bin" >> $GITHUB_PATH
+
+ - name: Prepare SQL databases directory
+ run: |
+ # Give the rights to the 'mssql' user to read/write in the /tmp/other_databases_path directory.
+ docker exec --user root $(docker ps -qf "ancestor=mcr.microsoft.com/mssql/server:2022-latest") chown -R mssql:mssql /tmp/other_databases_path
+
+ - name: Run tests
+ run: |
+ dotnet test PosInformatique.Testing.Databases.sln \
+ --configuration Release \
+ --no-build \
+ --logger "trx;LogFileName=test_results.trx" \
+ --results-directory ./TestResults
+ env:
+ SQL_SERVER_UNIT_TESTS_CONNECTION_STRING: "Data Source=localhost,1433;Database=master;User Id=sa;Password=P@ssw0rd12345!;TrustServerCertificate=True;"
+ SQL_SERVER_UNIT_TESTS_OTHER_DATA_PATH: "/tmp/other_databases_path/"
+
+ - name: Publish Test Results
+ uses: EnricoMi/publish-unit-test-result-action@v2
+ if: (!cancelled())
+ with:
+ files: |
+ TestResults/**/*.trx
+
+ build-samples:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
@@ -15,35 +75,11 @@ jobs:
- name: Setup NuGet
uses: nuget/setup-nuget@v2
- - name: Restore NuGet packages
- run: nuget restore PosInformatique.Testing.Databases.sln
-
+ - name: Restore NuGet packages of the samples
+ run: nuget restore samples/PosInformatique.Testing.Databases.Samples.sln
+
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
- - name: Build
- run: msbuild "PosInformatique.Testing.Databases.sln" /p:Configuration=Debug
-
- - name: Restore NuGet packages
- run: nuget restore "samples/PosInformatique.Testing.Databases.Samples.sln"
-
- - name: Build the samples
+ - name: Build samples with Visual Studio
run: msbuild "samples/PosInformatique.Testing.Databases.Samples.sln" /p:Configuration=Debug
-
- - name: Creates the LocalDB for the tests
- shell: cmd
- run: SqlLocalDB create posinfo-tests
-
- - name: Creates the SQL Login service accounts for the tests
- shell: cmd
- run: sqlcmd -S "(localDB)\posinfo-tests" -Q "IF NOT EXISTS (SELECT 1 FROM [sys].[server_principals] WHERE [Name] = 'ServiceAccountLogin') CREATE LOGIN [ServiceAccountLogin] WITH PASSWORD = 'P@ssw0rd'"
-
- # Use this fix https://github.com/microsoft/vstest-action/issues/31#issuecomment-2159463764
- - name: Test with the dotnet CLI
- uses: rusty-bender/vstest-action@main
- with:
- searchFolder: .\
- testAssembly: |
- /tests/**/*tests.dll
- !./**/*TestAdapter.dll
- !./**/obj/**
diff --git a/.github/workflows/github-actions-release.yml b/.github/workflows/github-actions-release.yml
index 3d176f0..a3a1959 100644
--- a/.github/workflows/github-actions-release.yml
+++ b/.github/workflows/github-actions-release.yml
@@ -7,12 +7,12 @@ on:
type: string
description: The version of the library
required: true
- default: 2.3.0
+ default: 3.0.0
VersionSuffix:
type: string
description: The version suffix of the library (for example rc.1)
-run-name: ${{ inputs.VersionPrefix }}-${{ inputs.VersionSuffix }}
+run-name: ${{ inputs.VersionSuffix && format('{0}-{1}', inputs.VersionPrefix, inputs.VersionSuffix) || inputs.VersionPrefix }}
jobs:
build:
@@ -32,6 +32,13 @@ jobs:
--property:VersionSuffix=${{ github.event.inputs.VersionSuffix }}
"src/Testing.Databases.SqlServer/Testing.Databases.SqlServer.csproj"
+ - name: Build Testing.Databases.SqlServer.Dac
+ run: dotnet pack
+ --property:Configuration=Release
+ --property:VersionPrefix=${{ github.event.inputs.VersionPrefix }}
+ --property:VersionSuffix=${{ github.event.inputs.VersionSuffix }}
+ "src/Testing.Databases.SqlServer.Dac/Testing.Databases.SqlServer.Dac.csproj"
+
- name: Build Testing.Databases.SqlServer.EntityFramework
run: dotnet pack
--property:Configuration=Release
@@ -39,12 +46,12 @@ jobs:
--property:VersionSuffix=${{ github.event.inputs.VersionSuffix }}
"src/Testing.Databases.SqlServer.EntityFramework/Testing.Databases.SqlServer.EntityFramework.csproj"
- - name: Build Testing.Databases.SqlServer.Dac
+ - name: Build Testing.Databases.SqlServer.SqlCmd
run: dotnet pack
--property:Configuration=Release
--property:VersionPrefix=${{ github.event.inputs.VersionPrefix }}
--property:VersionSuffix=${{ github.event.inputs.VersionSuffix }}
- "src/Testing.Databases.SqlServer.Dac/Testing.Databases.SqlServer.Dac.csproj"
+ "src/Testing.Databases.SqlServer.SqlCmd/Testing.Databases.SqlServer.SqlCmd.csproj"
- name: Publish the package to nuget.org
run: dotnet nuget push "src/**/bin/Release/*.nupkg" --api-key "${{ secrets.NUGET_APIKEY }}" --source https://api.nuget.org/v3/index.json
diff --git a/Directory.Build.props b/Directory.Build.props
index 47a3a52..012c3e0 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -15,9 +15,6 @@
enable
-
- false
-
$(NoWarn);SA0001;NU1903
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 546b9aa..f3d984f 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -12,7 +12,7 @@
-
+
\ No newline at end of file
diff --git a/PosInformatique.Testing.Databases.sln b/PosInformatique.Testing.Databases.sln
index d1c2105..c19db51 100644
--- a/PosInformatique.Testing.Databases.sln
+++ b/PosInformatique.Testing.Databases.sln
@@ -31,7 +31,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{49103176-7D0
src\Directory.Build.props = src\Directory.Build.props
EndProjectSection
EndProject
-Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Testing.Databases.SqlServer.Tests.DacPac", "tests\Testing.Databases.SqlServer.Tests.DacPac\Testing.Databases.SqlServer.Tests.DacPac.sqlproj", "{5F618225-0E1C-46A7-BBCC-23A6243D5CEE}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Testing.Databases.SqlServer.Tests.DacPac", "tests\Testing.Databases.SqlServer.Tests.DacPac\Testing.Databases.SqlServer.Tests.DacPac.csproj", "{5F618225-0E1C-46A7-BBCC-23A6243D5CEE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{91BFD2B1-6AB6-4B07-9D2E-430C93F150D4}"
EndProject
@@ -41,9 +41,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
.github\workflows\github-actions-release.yml = .github\workflows\github-actions-release.yml
EndProjectSection
EndProject
-Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Testing.Databases.SqlServer.Tests.Source", "tests\Testing.Databases.SqlServer.Tests.Source\Testing.Databases.SqlServer.Tests.Source.sqlproj", "{A261D4FF-9BEA-475C-8671-E9BACFDCE960}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Testing.Databases.SqlServer.Tests.Source", "tests\Testing.Databases.SqlServer.Tests.Source\Testing.Databases.SqlServer.Tests.Source.csproj", "{A261D4FF-9BEA-475C-8671-E9BACFDCE960}"
EndProject
-Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Testing.Databases.SqlServer.Tests.Target", "tests\Testing.Databases.SqlServer.Tests.Target\Testing.Databases.SqlServer.Tests.Target.sqlproj", "{6CD3F177-053F-4816-A37E-5CA6F293D34C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Testing.Databases.SqlServer.Tests.Target", "tests\Testing.Databases.SqlServer.Tests.Target\Testing.Databases.SqlServer.Tests.Target.csproj", "{6CD3F177-053F-4816-A37E-5CA6F293D34C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Testing.Databases.SqlServer.EntityFramework", "src\Testing.Databases.SqlServer.EntityFramework\Testing.Databases.SqlServer.EntityFramework.csproj", "{157DDF0D-9410-4646-94B9-9CEE4C140F5E}"
EndProject
@@ -51,12 +51,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Testing.Databases.SqlServer
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{8500A9B6-CAA0-432C-BABB-DDC86CE08994}"
ProjectSection(SolutionItems) = preProject
- docs\WriteTest.md = docs\WriteTest.md
docs\WriteDatabaseMigrationTest.md = docs\WriteDatabaseMigrationTest.md
+ docs\WriteTest.md = docs\WriteTest.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Testing.Databases.SqlServer.Dac", "src\Testing.Databases.SqlServer.Dac\Testing.Databases.SqlServer.Dac.csproj", "{8BE60460-EBA5-43DE-B85D-C756E2988DC8}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testing.Databases.SqlServer.Dac.Tests", "tests\Testing.Databases.SqlServer.Dac.Tests\Testing.Databases.SqlServer.Dac.Tests.csproj", "{6751A585-1BB0-49A1-BF68-D7FBD3392DF6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testing.Databases.SqlServer.SqlCmd", "src\Testing.Databases.SqlServer.SqlCmd\Testing.Databases.SqlServer.SqlCmd.csproj", "{D3004122-CCDD-4EAD-BD9E-DA6DFF470943}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testing.Databases.SqlServer.SqlCmd.Tests", "tests\Testing.Databases.SqlServer.SqlCmd.Tests\Testing.Databases.SqlServer.SqlCmd.Tests.csproj", "{F8E025D7-4E2F-437A-ABFA-C43A1368E15A}"
+EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Testing.Databases.SqlServer.Shared", "src\Testing.Databases.SqlServer.Shared\Testing.Databases.SqlServer.Shared.shproj", "{B9F8C52D-4652-4FC6-A695-DC2F61BA05C9}"
+EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Testing.Databases.SqlServer.Shared.Tests", "tests\Testing.Databases.SqlServer.Shared.Tests\Testing.Databases.SqlServer.Shared.Tests.shproj", "{1554EA1B-37C8-4F10-9770-BF4DD8A7E80C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -101,6 +111,18 @@ Global
{8BE60460-EBA5-43DE-B85D-C756E2988DC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BE60460-EBA5-43DE-B85D-C756E2988DC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BE60460-EBA5-43DE-B85D-C756E2988DC8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6751A585-1BB0-49A1-BF68-D7FBD3392DF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6751A585-1BB0-49A1-BF68-D7FBD3392DF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6751A585-1BB0-49A1-BF68-D7FBD3392DF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6751A585-1BB0-49A1-BF68-D7FBD3392DF6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D3004122-CCDD-4EAD-BD9E-DA6DFF470943}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D3004122-CCDD-4EAD-BD9E-DA6DFF470943}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D3004122-CCDD-4EAD-BD9E-DA6DFF470943}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D3004122-CCDD-4EAD-BD9E-DA6DFF470943}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8E025D7-4E2F-437A-ABFA-C43A1368E15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8E025D7-4E2F-437A-ABFA-C43A1368E15A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8E025D7-4E2F-437A-ABFA-C43A1368E15A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8E025D7-4E2F-437A-ABFA-C43A1368E15A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -115,4 +137,15 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FAC64573-D665-48A8-AC75-7B82B692EC9E}
EndGlobalSection
+ GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ tests\Testing.Databases.SqlServer.Shared.Tests\Testing.Databases.SqlServer.Shared.Tests.projitems*{04a7ae8f-fe77-435b-9250-600388bb8065}*SharedItemsImports = 5
+ tests\Testing.Databases.SqlServer.Shared.Tests\Testing.Databases.SqlServer.Shared.Tests.projitems*{1554ea1b-37c8-4f10-9770-bf4dd8a7e80c}*SharedItemsImports = 13
+ src\Testing.Databases.SqlServer.Shared\Testing.Databases.SqlServer.Shared.projitems*{157ddf0d-9410-4646-94b9-9cee4c140f5e}*SharedItemsImports = 5
+ tests\Testing.Databases.SqlServer.Shared.Tests\Testing.Databases.SqlServer.Shared.Tests.projitems*{6751a585-1bb0-49a1-bf68-d7fbd3392df6}*SharedItemsImports = 5
+ src\Testing.Databases.SqlServer.Shared\Testing.Databases.SqlServer.Shared.projitems*{8be60460-eba5-43de-b85d-c756e2988dc8}*SharedItemsImports = 5
+ src\Testing.Databases.SqlServer.Shared\Testing.Databases.SqlServer.Shared.projitems*{b9f8c52d-4652-4fc6-a695-dc2f61ba05c9}*SharedItemsImports = 13
+ tests\Testing.Databases.SqlServer.Shared.Tests\Testing.Databases.SqlServer.Shared.Tests.projitems*{c87e8f0d-d96d-4d77-9713-5564dc2e3597}*SharedItemsImports = 5
+ src\Testing.Databases.SqlServer.Shared\Testing.Databases.SqlServer.Shared.projitems*{d3004122-ccdd-4ead-bd9e-da6dff470943}*SharedItemsImports = 5
+ tests\Testing.Databases.SqlServer.Shared.Tests\Testing.Databases.SqlServer.Shared.Tests.projitems*{f8e025d7-4e2f-437a-abfa-c43a1368e15a}*SharedItemsImports = 5
+ EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index c761dd4..4b9ed3a 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,11 @@
# PosInformatique.Testing.Databases
-[](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer)
-[](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.Dac)
-[](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.EntityFramework)
+| Package | NuGet |
+|---------|-------|
+| PosInformatique.Testing.Databases.SqlServer | [](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer) |
+| PosInformatique.Testing.Databases.SqlServer.Dac | [](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.Dac) |
+| PosInformatique.Testing.Databases.SqlServer.EntityFramework | [](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.EntityFramework) |
+| PosInformatique.Testing.Databases.SqlServer.SqlCmd | [](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.SqlCmd) |
**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.
@@ -19,7 +22,10 @@ You can also use this tools to create and run integration tests with the
[Integration tests in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-8.0)
approach.
-Since the version 2.0.0 this tools provide a comparer to compare the schema of two SQL databases.
+### Main release improvements
+- v2.0: This tools provide a comparer to compare the schema of two SQL databases.
+- v3.0: Add new [PosInformatique.Testing.Databases.SqlServer.SqlCmd](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.SqlCmd) which allows
+to deploy database using a T-SQL script with the SQL Server [sqlcmd utility](https://learn.microsoft.com/en-us/sql/tools/sqlcmd/sqlcmd-utility).
## 💡 The approach of these tools
@@ -46,9 +52,10 @@ Before each test (`TestMethod` or `Fact` methods):
1. Create an empty database with the SQL schema of the application.
- There are two ways to do this:
- - Deploy a DACPAC file (built by a SQL Server Database project).
- - Or create a database from a `DbContext` using Entity Framework.
+ There are three ways to do this:
+ - Deploy a DACPAC file (built by a SQL Server Database project) using [PosInformatique.Testing.Databases.SqlServer.Dac](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.Dac) library.
+ - Create a database from a `DbContext` using Entity Framework using [PosInformatique.Testing.Databases.SqlServer.EntityFramework](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.EntityFramework) library.
+ - Or create a database since a T-SQL script file using [PosInformatique.Testing.Databases.SqlServer.SqlCmd](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.SqlCmd) library
2. Fill the tables with the sample data needed.
@@ -69,19 +76,21 @@ To perform tests of a database migration, the approach is straightforward and re
2. Create a secondary database with the targeted schema (*target database*).
- There are two ways to do this:
- - Deploy a DACPAC file (built by a SQL Server Database project).
- - Or create a database from a `DbContext` using Entity Framework.
+ There are three ways to do this:
+ - Deploy a DACPAC file (built by a SQL Server Database project) using [PosInformatique.Testing.Databases.SqlServer.Dac](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.Dac) library.
+ - Create a database from a `DbContext` using Entity Framework using [PosInformatique.Testing.Databases.SqlServer.EntityFramework](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.EntityFramework) library.
+ - Or create a database since a T-SQL script file using [PosInformatique.Testing.Databases.SqlServer.SqlCmd](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.SqlCmd) library
3. Execute your database *migration code* on the *initial database*.
Your database *migration code* can be:
- A simple SQL script file.
- An Entity Framework migration sets executed with the `MigrateAsync()` method.
+ - Or any other way that you usually use to migrate the schema of your database.
4. Compare the two databases schemas (*initial* and *target*).
- If the database *migration code* works, the *initial* and *target* must have the same schema.
+ If the database *migration code* works, the *initial* and *target* must have **EXACTLY** the same schema.
> **NB**: The initial database is not necessarily empty. It can be at a specific schema version X if we want to test the migration from version X to Y.
@@ -119,6 +128,10 @@ 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.
+- [PosInformatique.Testing.Databases.SqlServer.SqlCmd](https://www.nuget.org/packages/PosInformatique.Testing.Databases.SqlServer.SqlCmd) NuGet package which contains:
+ - Tools to execute T-SQL script using the SQL Server [sqlcmd utility](https://learn.microsoft.com/en-us/sql/tools/sqlcmd/sqlcmd-utility). This script can be use to deploy a
+ SQL Server database.
+
## 🚀 Samples / Demo
A complete sample solution is available in this repository inside the [samples](./samples) folder.
@@ -160,3 +173,34 @@ For Entity Framework migration:
- [Add the NuGet packages](./docs/WriteDatabaseMigrationTest.md#add-the-nuget-packages)
- [Write test to check the migration of the database](./docs/WriteDatabaseMigrationTest.md#write-test-to-check-the-migration-of-the-database)
- [Check the report details of the `SqlServerDatabaseComparer` tool](./docs/WriteDatabaseMigrationTest.md#check-the-report-details-of-the-sqlserverdatabasecomparer-tool)
+
+## 📦 NuGet package dependency versions
+
+These tools rely on a minimal set of NuGet dependencies to ensure broad compatibility.
+They are built for **.NET Core 6.0** and **.NET Framework 4.6.2** but also work seamlessly with newer versions of .NET:
+
+- .NET Framework 4.6.2
+- .NET Framework 4.7
+- .NET Framework 4.7.1
+- .NET Framework 4.7.2
+- .NET Framework 4.8
+- .NET Framework 4.8.1
+- .NET Core 6.0
+- .NET Core 7.0
+- .NET Core 8.0
+- .NET Core 9.0
+- .NET Core 10.0
+
+### Dependency versions
+
+All NuGet packages depend on **low baseline versions** of Microsoft libraries to remain compatible with any modern version:
+
+- [Microsoft.Data.SqlClient](https://www.nuget.org/packages/microsoft.data.sqlclient) >= 5.0.1
+- [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/microsoft.entityframeworkcore) >= 6.0.0
+- [Microsoft.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/microsoft.entityframeworkcore.sqlserver) >= 6.0.0
+- [Microsoft.EntityFrameworkCore.Tools](https://www.nuget.org/packages/microsoft.entityframeworkcore.tools) >= 6.0.0
+- [Microsoft.SqlServer.DacFx](https://www.nuget.org/packages/microsoft.sqlserver.dacfx) >= 162.1.172
+
+### Recommendation
+
+We recommend using the **latest versions** of these libraries in your own projects to benefit from the most recent features, performance improvements, and security fixes.
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 26a5ca9..9b643d3 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -11,6 +11,13 @@
README.md
MIT
+ 3.0.0
+ - Add new PosInformatique.Testing.Databases.SqlCmd to initialize database using sqlcmd utility.
+ - For DACPAC deployment or database creation it is now possible to specify the location of the data and log files.
+
+ 2.3.0
+ - Add the support to compare the seed and increment for the IDENTITY columns
+
2.2.0
- Add SqlServerDatabase.ClearDataAsync() method.
- Add SqlServer.CreateDatabase() method to create database from an Entity Framework DbContext.
@@ -42,7 +49,7 @@
-
+
<_Parameter1>$(AssemblyName).Tests
diff --git a/src/Testing.Databases.SqlServer.Dac/Icon.png b/src/Testing.Databases.SqlServer.Dac/Icon.png
index 4d3e4c8..8ed8d93 100644
Binary files a/src/Testing.Databases.SqlServer.Dac/Icon.png and b/src/Testing.Databases.SqlServer.Dac/Icon.png differ
diff --git a/src/Testing.Databases.SqlServer.Dac/SqlServerDacDatabaseInitializer.cs b/src/Testing.Databases.SqlServer.Dac/SqlServerDacDatabaseInitializer.cs
index 1724b3e..9528978 100644
--- a/src/Testing.Databases.SqlServer.Dac/SqlServerDacDatabaseInitializer.cs
+++ b/src/Testing.Databases.SqlServer.Dac/SqlServerDacDatabaseInitializer.cs
@@ -10,10 +10,10 @@ namespace PosInformatique.Testing.Databases.SqlServer
///
/// Initializer used to initialize the database for the tests.
- /// Call the method to initialize a database from
+ /// Call the method to initialize a database from
/// a DACPAC file.
///
- /// The database will be created the call of the method. For the next calls
+ /// The database will be created the call of the method. For the next calls
/// the database is preserved but all the data are deleted.
public static class SqlServerDacDatabaseInitializer
{
@@ -23,9 +23,23 @@ public static class SqlServerDacDatabaseInitializer
/// which the initialization will be perform on.
/// Full path of the DACPAC file.
/// Connection string to the SQL Server with administrator rights.
+ /// Additionnal settings for the DACPAC to deploy.
+ /// If the specified argument is .
+ /// If the specified argument is .
+ /// If the specified argument is .
+ /// If no file exists with the specified argument.
/// An instance of the which allows to perform query to initialize the data.
- public static SqlServerDatabase Initialize(this SqlServerDatabaseInitializer initializer, string packageName, string connectionString)
+ public static SqlServerDatabase Initialize(this SqlServerDatabaseInitializer initializer, string packageName, string connectionString, SqlServerDacDeploymentSettings? settings = null)
{
+ Guard.ThrowIfNull(initializer, nameof(initializer));
+ Guard.ThrowIfNull(packageName, nameof(packageName));
+ Guard.ThrowIfNull(connectionString, nameof(connectionString));
+
+ if (!File.Exists(packageName))
+ {
+ throw new FileNotFoundException($"Could not find file '{packageName}'", packageName);
+ }
+
var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString);
var server = new SqlServer(connectionString);
@@ -34,7 +48,7 @@ public static SqlServerDatabase Initialize(this SqlServerDatabaseInitializer ini
if (!initializer.IsInitialized)
{
- database = server.DeployDacPackage(packageName, connectionStringBuilder.InitialCatalog);
+ database = server.DeployDacPackage(packageName, connectionStringBuilder.InitialCatalog, settings);
initializer.IsInitialized = true;
}
diff --git a/src/Testing.Databases.SqlServer.Dac/SqlServerDacDeploymentSettings.cs b/src/Testing.Databases.SqlServer.Dac/SqlServerDacDeploymentSettings.cs
new file mode 100644
index 0000000..285aa03
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.Dac/SqlServerDacDeploymentSettings.cs
@@ -0,0 +1,20 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer
+{
+ ///
+ /// Contains additional settings when deploying a
+ /// using or .
+ ///
+ public class SqlServerDacDeploymentSettings
+ {
+ ///
+ /// Gets or sets the data file name (full path) of the database to create.
+ ///
+ public string? DataFileName { get; set; }
+ }
+}
diff --git a/src/Testing.Databases.SqlServer.Dac/SqlServerDacExtensions.cs b/src/Testing.Databases.SqlServer.Dac/SqlServerDacExtensions.cs
index e37145f..d72cdb8 100644
--- a/src/Testing.Databases.SqlServer.Dac/SqlServerDacExtensions.cs
+++ b/src/Testing.Databases.SqlServer.Dac/SqlServerDacExtensions.cs
@@ -21,16 +21,31 @@ public static class SqlServerDacExtensions
/// instance where the DACPAC file will be deployed.
/// File name (including the path) for the DACPAC file to deploy.
/// Name of the database which will be created.
+ /// Additional settings of the database to deploy.
+ /// If the specified argument is .
+ /// If the specified argument is .
+ /// If the specified argument is .
+ /// If no file exists with the specified argument.
/// An instance of the which represents the deployed database.
- public static SqlServerDatabase DeployDacPackage(this SqlServer server, string fileName, string databaseName)
+ public static SqlServerDatabase DeployDacPackage(this SqlServer server, string fileName, string databaseName, SqlServerDacDeploymentSettings? settings = null)
{
+ Guard.ThrowIfNull(server, nameof(server));
+ Guard.ThrowIfNull(fileName, nameof(fileName));
+ Guard.ThrowIfNull(databaseName, nameof(databaseName));
+
+ if (!File.Exists(fileName))
+ {
+ throw new FileNotFoundException($"Could not find file '{fileName}'", fileName);
+ }
+
using (var package = DacPackage.Load(fileName))
{
- var options = new DacDeployOptions();
- options.CreateNewDatabase = true;
+ // Currently DacFx does not support to define explicitly the location of the database files.
+ // So, we create an empty database and after we run the deployment without deleting the database.
+ server.CreateEmptyDatabase(databaseName, new SqlDatabaseCreationSettings() { DataFileName = settings?.DataFileName });
var services = new DacServices(server.Master.ConnectionString);
- services.Deploy(package, databaseName, true, options: options);
+ services.Deploy(package, databaseName, true);
}
return server.GetDatabase(databaseName);
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 205776f..4a68d3c 100644
--- a/src/Testing.Databases.SqlServer.Dac/Testing.Databases.SqlServer.Dac.csproj
+++ b/src/Testing.Databases.SqlServer.Dac/Testing.Databases.SqlServer.Dac.csproj
@@ -4,7 +4,8 @@
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
+ testing;unittest;sqlserver;repository;tdd;dataaccesslayer;dacpac;dac
+ True
@@ -23,4 +24,6 @@
+
+
diff --git a/src/Testing.Databases.SqlServer.EntityFramework/EntityFrameworkDatabaseInitializerExtensions.cs b/src/Testing.Databases.SqlServer.EntityFramework/EntityFrameworkDatabaseInitializerExtensions.cs
index 7d9db85..450b276 100644
--- a/src/Testing.Databases.SqlServer.EntityFramework/EntityFrameworkDatabaseInitializerExtensions.cs
+++ b/src/Testing.Databases.SqlServer.EntityFramework/EntityFrameworkDatabaseInitializerExtensions.cs
@@ -21,8 +21,13 @@ public static class EntityFrameworkDatabaseInitializerExtensions
/// which the initialization will be perform on.
/// Instance of the which represents the database schema to initialize.
/// An instance of the which allows to perform query to initialize the data.
+ /// If the specified argument is .
+ /// If the specified argument is .
public static SqlServerDatabase Initialize(this SqlServerDatabaseInitializer initializer, DbContext context)
{
+ Guard.ThrowIfNull(initializer, nameof(initializer));
+ Guard.ThrowIfNull(context, nameof(context));
+
var connectionString = context.Database.GetConnectionString();
var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString);
diff --git a/src/Testing.Databases.SqlServer.EntityFramework/EntityFrameworkSqlServerExtensions.cs b/src/Testing.Databases.SqlServer.EntityFramework/EntityFrameworkSqlServerExtensions.cs
index ace72cc..4525608 100644
--- a/src/Testing.Databases.SqlServer.EntityFramework/EntityFrameworkSqlServerExtensions.cs
+++ b/src/Testing.Databases.SqlServer.EntityFramework/EntityFrameworkSqlServerExtensions.cs
@@ -21,9 +21,16 @@ public static class EntityFrameworkSqlServerExtensions
/// instance where the database will be created.
/// Name of the database to create.
/// which represents the database to create.
+ /// If the specified argument is .
+ /// If the specified argument is .
+ /// If the specified argument is .
/// An instance of the which represents the deployed database.
public static SqlServerDatabase CreateDatabase(this SqlServer server, string name, DbContext context)
{
+ Guard.ThrowIfNull(server, nameof(server));
+ Guard.ThrowIfNull(name, nameof(name));
+ Guard.ThrowIfNull(context, nameof(context));
+
var database = server.GetDatabase(name);
context.Database.SetConnectionString(database.ConnectionString);
@@ -41,9 +48,16 @@ public static SqlServerDatabase CreateDatabase(this SqlServer server, string nam
/// instance where the database will be created.
/// Name of the database to create.
/// which represents the database to create.
+ /// If the specified argument is .
+ /// If the specified argument is .
+ /// If the specified argument is .
/// A which represents the asynchronous operation and contains an instance of the that represents the deployed database.
public static async Task CreateDatabaseAsync(this SqlServer server, string name, DbContext context)
{
+ Guard.ThrowIfNull(server, nameof(server));
+ Guard.ThrowIfNull(name, nameof(name));
+ Guard.ThrowIfNull(context, nameof(context));
+
var database = server.GetDatabase(name);
context.Database.SetConnectionString(database.ConnectionString);
diff --git a/src/Testing.Databases.SqlServer.EntityFramework/Icon.png b/src/Testing.Databases.SqlServer.EntityFramework/Icon.png
index 76744e4..8ed8d93 100644
Binary files a/src/Testing.Databases.SqlServer.EntityFramework/Icon.png and b/src/Testing.Databases.SqlServer.EntityFramework/Icon.png differ
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 5c8d46d..0e5a336 100644
--- a/src/Testing.Databases.SqlServer.EntityFramework/Testing.Databases.SqlServer.EntityFramework.csproj
+++ b/src/Testing.Databases.SqlServer.EntityFramework/Testing.Databases.SqlServer.EntityFramework.csproj
@@ -5,7 +5,7 @@
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.
- testing unittest entityframework sqlserver repository tdd dataaccesslayer
+ testing;unittest;entityframework;sqlserver;repository;tdd;dataaccesslayer
@@ -24,4 +24,6 @@
+
+
diff --git a/src/Testing.Databases.SqlServer.Shared/Guard.cs b/src/Testing.Databases.SqlServer.Shared/Guard.cs
new file mode 100644
index 0000000..cec2e0f
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.Shared/Guard.cs
@@ -0,0 +1,19 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique
+{
+ internal static class Guard
+ {
+ public static void ThrowIfNull(object? value, string paramName)
+ {
+ if (value is null)
+ {
+ throw new ArgumentNullException(paramName);
+ }
+ }
+ }
+}
diff --git a/src/Testing.Databases.SqlServer.Shared/Testing.Databases.SqlServer.Shared.projitems b/src/Testing.Databases.SqlServer.Shared/Testing.Databases.SqlServer.Shared.projitems
new file mode 100644
index 0000000..f3b3b69
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.Shared/Testing.Databases.SqlServer.Shared.projitems
@@ -0,0 +1,14 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ b9f8c52d-4652-4fc6-a695-dc2f61ba05c9
+
+
+ Testing.Databases.SqlServer.Shared
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Testing.Databases.SqlServer.Shared/Testing.Databases.SqlServer.Shared.shproj b/src/Testing.Databases.SqlServer.Shared/Testing.Databases.SqlServer.Shared.shproj
new file mode 100644
index 0000000..572486d
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.Shared/Testing.Databases.SqlServer.Shared.shproj
@@ -0,0 +1,13 @@
+
+
+
+ b9f8c52d-4652-4fc6-a695-dc2f61ba05c9
+ 14.0
+
+
+
+
+
+
+
+
diff --git a/src/Testing.Databases.SqlServer.SqlCmd/Icon.png b/src/Testing.Databases.SqlServer.SqlCmd/Icon.png
new file mode 100644
index 0000000..8ed8d93
Binary files /dev/null and b/src/Testing.Databases.SqlServer.SqlCmd/Icon.png differ
diff --git a/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdCommandLineArgumentsBuilder.cs b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdCommandLineArgumentsBuilder.cs
new file mode 100644
index 0000000..0e67e83
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdCommandLineArgumentsBuilder.cs
@@ -0,0 +1,91 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer
+{
+ using System.Text;
+ using Microsoft.Data.SqlClient;
+
+ internal sealed class SqlCmdCommandLineArgumentsBuilder
+ {
+ private readonly Dictionary variables;
+
+ public SqlCmdCommandLineArgumentsBuilder(SqlConnectionStringBuilder connectionString)
+ {
+ this.Database = connectionString.InitialCatalog;
+ this.LoginId = connectionString.UserID;
+ this.Password = connectionString.Password;
+ this.Server = connectionString.DataSource;
+ this.TrustedConnection = connectionString.IntegratedSecurity;
+
+ this.variables = new Dictionary();
+ }
+
+ public string? Database { get; set; }
+
+ public string? InputFile { get; set; }
+
+ public string? LoginId { get; set; }
+
+ public string? Password { get; set; }
+
+ public string? Server { get; set; }
+
+ public bool TrustedConnection { get; set; }
+
+ public IReadOnlyDictionary Variables => this.variables;
+
+ public void AddVariable(string name, string value)
+ {
+ this.variables.Add(name, value);
+ }
+
+ public override string ToString()
+ {
+ var parameters = new StringBuilder();
+
+ if (!string.IsNullOrEmpty(this.Database))
+ {
+ parameters.Append($"-d \"{this.Database}\" ");
+ }
+
+ if (!string.IsNullOrEmpty(this.InputFile))
+ {
+ parameters.Append($"-i \"{this.InputFile}\" ");
+ }
+
+ if (!string.IsNullOrEmpty(this.LoginId))
+ {
+ parameters.Append($"-U \"{this.LoginId}\" ");
+ }
+
+ if (!string.IsNullOrEmpty(this.Password))
+ {
+ parameters.Append($"-P \"{this.Password}\" ");
+ }
+
+ if (this.TrustedConnection)
+ {
+ parameters.Append($"-E ");
+ }
+
+ if (!string.IsNullOrEmpty(this.Server))
+ {
+ parameters.Append($"-S \"{this.Server}\" ");
+ }
+
+ foreach (var variable in this.variables)
+ {
+ parameters.Append($"-v {variable.Key}=\"{variable.Value}\" ");
+ }
+
+ // To have exit error code when the script contains errors.
+ parameters.Append("-b");
+
+ return parameters.ToString();
+ }
+ }
+}
diff --git a/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdDatabaseInitializer.cs b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdDatabaseInitializer.cs
new file mode 100644
index 0000000..676dbf8
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdDatabaseInitializer.cs
@@ -0,0 +1,64 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer
+{
+ using Microsoft.Data.SqlClient;
+
+ ///
+ /// Initializer used to initialize the database for the tests.
+ /// Call the method to initialize a database from
+ /// a T-SQL script file using sqlcmd.
+ ///
+ /// The database will be created the call of the method. For the next calls
+ /// the database is preserved but all the data are deleted.
+ public static class SqlCmdDatabaseInitializer
+ {
+ ///
+ /// Initialize a SQL Server database by executing the T-SQL script specified in the argument.
+ /// The script will be executed on the master database specified in the , so
+ /// you have to switch the connection if need in your script using the T-SQL USE directive.
+ ///
+ /// which the initialization will be perform on.
+ /// Full path of the T-SQL file to execute.
+ /// Connection string to the SQL Server with administrator rights.
+ /// Additionnal settings to run the sqlcmd tool.
+ /// If the specified argument is .
+ /// If the specified argument is .
+ /// If the specified argument is .
+ /// If no file exists with the specified argument.
+ /// An instance of the which allows to perform query to initialize the data.
+ public static SqlServerDatabase Initialize(this SqlServerDatabaseInitializer initializer, string fileName, string connectionString, SqlCmdRunScriptSettings? settings = null)
+ {
+ Guard.ThrowIfNull(initializer, nameof(initializer));
+ Guard.ThrowIfNull(fileName, nameof(fileName));
+ Guard.ThrowIfNull(connectionString, nameof(connectionString));
+
+ if (!File.Exists(fileName))
+ {
+ throw new FileNotFoundException($"Could not find file '{fileName}'", fileName);
+ }
+
+ var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString);
+
+ var server = new SqlServer(connectionString);
+
+ SqlServerDatabase database;
+
+ if (!initializer.IsInitialized)
+ {
+ server.Master.RunScript(fileName, settings);
+
+ initializer.IsInitialized = true;
+ }
+
+ database = server.GetDatabase(connectionStringBuilder.InitialCatalog);
+ database.ClearAllData();
+
+ return database;
+ }
+ }
+}
diff --git a/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdException.cs b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdException.cs
new file mode 100644
index 0000000..d250ca4
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdException.cs
@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer
+{
+ ///
+ /// Occured when an error has been raised by the SQL Server sqlcmd tool.
+ ///
+ public class SqlCmdException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SqlCmdException()
+ : base()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified .
+ ///
+ /// Exception message.
+ public SqlCmdException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified and
+ /// of the sqlcmd process.
+ ///
+ /// Exception message.
+ /// Output of the sqlcmd process.
+ public SqlCmdException(string message, string output)
+ : base(message)
+ {
+ this.Output = output;
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified raised by
+ /// an other .
+ ///
+ /// Exception message.
+ /// Previous which raised the .
+ public SqlCmdException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Gets the output of the sqlcmd execution.
+ ///
+ public string? Output { get; }
+ }
+}
diff --git a/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdProcess.cs b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdProcess.cs
new file mode 100644
index 0000000..567f1ef
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdProcess.cs
@@ -0,0 +1,94 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer
+{
+ using System.Diagnostics;
+ using Microsoft.Data.SqlClient;
+
+ internal sealed class SqlCmdProcess : IDisposable
+ {
+ private readonly List output;
+
+ private Process? process;
+
+ private SqlCmdProcess(string arguments)
+ {
+ this.output = new List();
+
+ this.process = new Process()
+ {
+ StartInfo =
+ {
+ Arguments = arguments,
+ FileName = "sqlcmd",
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ },
+ };
+
+ this.process.ErrorDataReceived += this.OnOutputDataReceived;
+ this.process.OutputDataReceived += this.OnOutputDataReceived;
+
+ this.process.Start();
+
+ this.process.BeginErrorReadLine();
+ this.process.BeginOutputReadLine();
+ }
+
+ public string Output => string.Join(Environment.NewLine, this.output);
+
+ public static SqlCmdProcess RunScript(SqlConnectionStringBuilder connectionString, string inputFile, SqlCmdRunScriptSettings settings)
+ {
+ var commandLineBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString)
+ {
+ InputFile = inputFile,
+ };
+
+ foreach (var variable in settings.Variables)
+ {
+ commandLineBuilder.AddVariable(variable.Key, variable.Value);
+ }
+
+ var process = new SqlCmdProcess(commandLineBuilder.ToString());
+
+ return process;
+ }
+
+ public void Dispose()
+ {
+ if (this.process is not null)
+ {
+ this.process.Dispose();
+ this.process = null;
+ }
+ }
+
+ public int WaitForExit()
+ {
+ if (this.process is null)
+ {
+ throw new ObjectDisposedException(this.GetType().FullName);
+ }
+
+ this.process.WaitForExit();
+
+ return this.process.ExitCode;
+ }
+
+ private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ if (e.Data is not null)
+ {
+ lock (this.output)
+ {
+ this.output.Add(e.Data);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdRunScriptSettings.cs b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdRunScriptSettings.cs
new file mode 100644
index 0000000..5c24932
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdRunScriptSettings.cs
@@ -0,0 +1,29 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer
+{
+ ///
+ /// Contains additional settings when runing the sqlcmd.
+ ///
+ public class SqlCmdRunScriptSettings
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SqlCmdRunScriptSettings()
+ {
+ this.Variables = new Dictionary();
+ }
+
+ ///
+ /// Gets a collection of variables and associated values which will be applied
+ /// on the script to run. The variables can be referenced in the T-SQL script
+ /// using the $(Variable) syntax.
+ ///
+ public IDictionary Variables { get; }
+ }
+}
diff --git a/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdSqlServerDatabaseExtensions.cs b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdSqlServerDatabaseExtensions.cs
new file mode 100644
index 0000000..032e656
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.SqlCmd/SqlCmdSqlServerDatabaseExtensions.cs
@@ -0,0 +1,55 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer
+{
+ using Microsoft.Data.SqlClient;
+
+ ///
+ /// Contains extensions methods for the to run script using the SQL Server sqlcmd tool.
+ ///
+ public static class SqlCmdSqlServerDatabaseExtensions
+ {
+ ///
+ /// Run the T-SQL script specified in with the sqlcmd tool.
+ ///
+ /// where the script will be executed on.
+ /// T-SQL script to execute on the .
+ /// Additional settings to run the script.
+ /// If the specified argument is .
+ /// If the specified argument is .
+ /// If no file exists with the specified argument.
+ /// If an error has been occured when running the T-SQL script. Check the
+ /// to retrieve the output result of the script execution.
+ public static void RunScript(this SqlServerDatabase database, string fileName, SqlCmdRunScriptSettings? settings = null)
+ {
+ Guard.ThrowIfNull(database, nameof(database));
+ Guard.ThrowIfNull(fileName, nameof(fileName));
+
+ if (!File.Exists(fileName))
+ {
+ throw new FileNotFoundException($"Could not find file '{fileName}'", fileName);
+ }
+
+ if (settings is null)
+ {
+ settings = new SqlCmdRunScriptSettings();
+ }
+
+ var connectionStringBuilder = new SqlConnectionStringBuilder(database.ConnectionString);
+
+ using (var sqlCmdProcess = SqlCmdProcess.RunScript(connectionStringBuilder, fileName, settings))
+ {
+ var exitCode = sqlCmdProcess.WaitForExit();
+
+ if (exitCode != 0)
+ {
+ throw new SqlCmdException($"Some errors has been occurred when executing the '{fileName}'.{Environment.NewLine}{Environment.NewLine}-- Output --{Environment.NewLine}{sqlCmdProcess.Output}", sqlCmdProcess.Output);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Testing.Databases.SqlServer.SqlCmd/Testing.Databases.SqlServer.SqlCmd.csproj b/src/Testing.Databases.SqlServer.SqlCmd/Testing.Databases.SqlServer.SqlCmd.csproj
new file mode 100644
index 0000000..1a4198c
--- /dev/null
+++ b/src/Testing.Databases.SqlServer.SqlCmd/Testing.Databases.SqlServer.SqlCmd.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net6.0;net462
+ True
+
+ Testing.Databases.SqlServer.SqlCmd is a library that contains a set of tools for testing Data Access Layer using SQLCMD tool to initialize the database with a SQL script.
+ testing;unittest;sqlcmd;sqlserver;repository;tdd;dataaccesslayer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Testing.Databases.SqlServer/Icon.png b/src/Testing.Databases.SqlServer/Icon.png
index 4d3e4c8..8ed8d93 100644
Binary files a/src/Testing.Databases.SqlServer/Icon.png and b/src/Testing.Databases.SqlServer/Icon.png differ
diff --git a/src/Testing.Databases.SqlServer/SqlDatabaseCreationSettings.cs b/src/Testing.Databases.SqlServer/SqlDatabaseCreationSettings.cs
new file mode 100644
index 0000000..8d90eef
--- /dev/null
+++ b/src/Testing.Databases.SqlServer/SqlDatabaseCreationSettings.cs
@@ -0,0 +1,23 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer
+{
+ ///
+ /// Settings of the to create using the
+ ///
+ /// or
+ ///
+ /// methods.
+ ///
+ public class SqlDatabaseCreationSettings
+ {
+ ///
+ /// Gets or sets the data file name (full path) of the database to create.
+ ///
+ public string? DataFileName { get; set; }
+ }
+}
diff --git a/src/Testing.Databases.SqlServer/SqlServer.cs b/src/Testing.Databases.SqlServer/SqlServer.cs
index c848b12..de49e6b 100644
--- a/src/Testing.Databases.SqlServer/SqlServer.cs
+++ b/src/Testing.Databases.SqlServer/SqlServer.cs
@@ -6,6 +6,7 @@
namespace PosInformatique.Testing.Databases.SqlServer
{
+ using System.Text;
using Microsoft.Data.SqlClient;
///
@@ -42,11 +43,12 @@ public SqlServer(string connectionString)
/// If the database already exists, it will be delete.
///
/// Name of the database to create.
+ /// Settings of the database to create.
/// An instance of which allows to execute SQL commands/queries.
- public SqlServerDatabase CreateEmptyDatabase(string name)
+ public SqlServerDatabase CreateEmptyDatabase(string name, SqlDatabaseCreationSettings? settings = null)
{
this.DeleteDatabase(name);
- this.Master.ExecuteNonQuery($"CREATE DATABASE [{name}]");
+ this.Master.ExecuteNonQuery(BuildCreateDatabaseSqlCommand(name, settings));
return this.GetDatabase(name);
}
@@ -56,12 +58,13 @@ public SqlServerDatabase CreateEmptyDatabase(string name)
/// If the database already exists, it will be delete.
///
/// Name of the database to create.
+ /// Settings of the database to create.
/// used to cancel the asynchronous operation.
/// A which represents the asynchronous operation and contains an instance of which allows to execute SQL commands/queries.
- public async Task CreateEmptyDatabaseAsync(string name, CancellationToken cancellationToken = default)
+ public async Task CreateEmptyDatabaseAsync(string name, SqlDatabaseCreationSettings? settings = null, CancellationToken cancellationToken = default)
{
await this.DeleteDatabaseAsync(name, cancellationToken);
- await this.Master.ExecuteNonQueryAsync($"CREATE DATABASE [{name}]", cancellationToken);
+ await this.Master.ExecuteNonQueryAsync(BuildCreateDatabaseSqlCommand(name, settings), cancellationToken);
return this.GetDatabase(name);
}
@@ -102,5 +105,25 @@ public SqlServerDatabase GetDatabase(string name)
return new SqlServerDatabase(this, databaseConnectionString.ToString());
}
+
+ private static string BuildCreateDatabaseSqlCommand(string name, SqlDatabaseCreationSettings? settings)
+ {
+ var sql = new StringBuilder($"CREATE DATABASE [{name}]");
+
+ if (settings is not null)
+ {
+ if (!string.IsNullOrEmpty(settings.DataFileName))
+ {
+ var logFileName = Path.Combine(
+ Path.GetDirectoryName(settings.DataFileName),
+ $"{Path.GetFileNameWithoutExtension(settings.DataFileName)}_log.ldf");
+
+ sql.Append($"ON (NAME = '{name}', FILENAME = '{settings.DataFileName}')");
+ sql.Append($"LOG ON (NAME = '{name}_log', FILENAME = '{logFileName}')");
+ }
+ }
+
+ return sql.ToString();
+ }
}
}
diff --git a/src/Testing.Databases.SqlServer/Testing.Databases.SqlServer.csproj b/src/Testing.Databases.SqlServer/Testing.Databases.SqlServer.csproj
index cbd5830..d1c0009 100644
--- a/src/Testing.Databases.SqlServer/Testing.Databases.SqlServer.csproj
+++ b/src/Testing.Databases.SqlServer/Testing.Databases.SqlServer.csproj
@@ -5,7 +5,7 @@
True
Testing.Databases.SqlServer is a library that contains a set of tools for testing Data Access Layer (repositories) based on SQL Server.
- testing unittest sqlserver repository tdd dataaccesslayer
+ testing;unittest;sqlserver;repository;tdd;dataaccesslayer
diff --git a/tests/.editorconfig b/tests/.editorconfig
index eedb7c8..3132794 100644
--- a/tests/.editorconfig
+++ b/tests/.editorconfig
@@ -1,3 +1,6 @@
[*.cs]
### StyleCop
+
+# SA1118: Parameter should not span multiple lines
+dotnet_diagnostic.SA1118.severity = none
\ No newline at end of file
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
index 65d79b0..c02c472 100644
--- a/tests/Directory.Build.props
+++ b/tests/Directory.Build.props
@@ -2,6 +2,18 @@
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
diff --git a/tests/Testing.Databases.SqlServer.Dac.Tests/SqlServerDacDeploymentSettingsTest.cs b/tests/Testing.Databases.SqlServer.Dac.Tests/SqlServerDacDeploymentSettingsTest.cs
new file mode 100644
index 0000000..452982d
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Dac.Tests/SqlServerDacDeploymentSettingsTest.cs
@@ -0,0 +1,29 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ public class SqlServerDacDeploymentSettingsTest
+ {
+ [Fact]
+ public void Constructor()
+ {
+ var settings = new SqlServerDacDeploymentSettings();
+
+ settings.DataFileName.Should().BeNull();
+ }
+
+ [Fact]
+ public void DataFileName_ValueChanged()
+ {
+ var settings = new SqlServerDacDeploymentSettings();
+
+ settings.DataFileName = "The value";
+
+ settings.DataFileName.Should().Be("The value");
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Dac.Tests/SqlServerDacExtensionsTest.cs b/tests/Testing.Databases.SqlServer.Dac.Tests/SqlServerDacExtensionsTest.cs
new file mode 100644
index 0000000..a8bf9e0
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Dac.Tests/SqlServerDacExtensionsTest.cs
@@ -0,0 +1,139 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ [Collection("PosInformatique.Testing.Databases.SqlServer.Tests")]
+ public class SqlServerDacExtensionsTest
+ {
+ private static readonly string ConnectionString = ConnectionStrings.Get();
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void DeployDacPackage(bool withSettings)
+ {
+ // Create existing database to be sure the database is recreated when deploying the database with a DACPAC
+ CreateDatabase("SqlServerDacExtensionsTest_DeployDacPackage");
+
+ var server = new SqlServer(ConnectionString);
+
+ SqlServerDacDeploymentSettings settings = null;
+
+ if (withSettings)
+ {
+ settings = new SqlServerDacDeploymentSettings();
+ }
+
+ var database = server.DeployDacPackage("PosInformatique.Testing.Databases.SqlServer.Tests.DacPac.dacpac", "SqlServerDacExtensionsTest_DeployDacPackage", settings);
+
+ var table = database.ExecuteQuery("SELECT * FROM MyTable");
+
+ table.Rows.Should().BeEmpty();
+
+ // Insert data to check the connection.
+ database.InsertInto("MyTable", new { Id = 1, Name = "Name 1" });
+ database.InsertInto("MyTable", new { Id = 2, Name = "Name 2" });
+ }
+
+ [Fact]
+ public void DeployDacPackage_WithSpecificDataFileName()
+ {
+ // Create existing database to be sure the database is recreated when deploying the database with a DACPAC
+ CreateDatabase("SqlServerDacExtensionsTest_DeployDacPackage_WithSpecificDataFileName");
+
+ using var otherDataPath = OtherDatabasePath.Create();
+
+ var server = new SqlServer(ConnectionString);
+
+ var settings = new SqlServerDacDeploymentSettings()
+ {
+ DataFileName = Path.Combine(otherDataPath.Path, "TheSpecificDataFileName.mdf"),
+ };
+
+ var database = server.DeployDacPackage("PosInformatique.Testing.Databases.SqlServer.Tests.DacPac.dacpac", "SqlServerDacExtensionsTest_DeployDacPackage_WithSpecificDataFileName", settings);
+
+ var table = database.ExecuteQuery("SELECT * FROM MyTable");
+
+ table.Rows.Should().BeEmpty();
+
+ // Insert data to check the connection.
+ database.InsertInto("MyTable", new { Id = 1, Name = "Name 1" });
+ database.InsertInto("MyTable", new { Id = 2, Name = "Name 2" });
+
+ // Check the location of the database
+ File.Exists(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName.mdf")).Should().BeTrue();
+ File.Exists(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName_log.ldf")).Should().BeTrue();
+
+ var result = database.ExecuteQuery("SELECT * FROM [sys].[database_files] ORDER BY [physical_name]");
+
+ result.Rows.Should().HaveCount(2);
+
+ result.Rows[0]["name"].Should().Be("SqlServerDacExtensionsTest_DeployDacPackage_WithSpecificDataFileName");
+ result.Rows[0]["physical_name"].Should().Be(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName.mdf"));
+ result.Rows[0]["type_desc"].Should().Be("ROWS");
+
+ result.Rows[1]["name"].Should().Be("SqlServerDacExtensionsTest_DeployDacPackage_WithSpecificDataFileName_log");
+ result.Rows[1]["physical_name"].Should().Be(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName_log.ldf"));
+ result.Rows[1]["type_desc"].Should().Be("LOG");
+
+ // Delete the database (for deleting the temporary folder).
+ server.DeleteDatabase("SqlServerDacExtensionsTest_DeployDacPackage_WithSpecificDataFileName");
+ }
+
+ [Fact]
+ public void DeployDacPackage_WithDatabaseArgumentNull()
+ {
+ var act = () =>
+ {
+ SqlServerDacExtensions.DeployDacPackage(null, default, default);
+ };
+
+ act.Should().ThrowExactly()
+ .WithParameterName("server");
+ }
+
+ [Fact]
+ public void DeployDacPackage_WithFileNameArgumentNull()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.Invoking(s => s.DeployDacPackage(null, default))
+ .Should().ThrowExactly()
+ .WithParameterName("fileName");
+ }
+
+ [Fact]
+ public void DeployDacPackage_WithDatabaseNameArgumentNull()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.Invoking(s => s.DeployDacPackage("C:/Directory/FileNotFound.sql", null))
+ .Should().ThrowExactly()
+ .WithParameterName("databaseName");
+ }
+
+ [Fact]
+ public void DeployDacPackage_WithFileNotFound()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.Invoking(s => s.DeployDacPackage("C:/Directory/FileNotFound.sql", "The database"))
+ .Should().ThrowExactly()
+ .WithMessage("Could not find file 'C:/Directory/FileNotFound.sql'")
+ .Which.FileName.Should().Be("C:/Directory/FileNotFound.sql");
+ }
+
+ private static void CreateDatabase(string name)
+ {
+ var server = new SqlServer(ConnectionString);
+
+ var database = server.CreateEmptyDatabase(name);
+
+ database.ExecuteNonQuery("CREATE TABLE OtherTable (Id INT)");
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Dac.Tests/SqlServerDatabaseInitializerTest.cs b/tests/Testing.Databases.SqlServer.Dac.Tests/SqlServerDatabaseInitializerTest.cs
new file mode 100644
index 0000000..4af4ca1
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Dac.Tests/SqlServerDatabaseInitializerTest.cs
@@ -0,0 +1,223 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ [Collection("PosInformatique.Testing.Databases.SqlServer.Tests")]
+ public class SqlServerDatabaseInitializerTest : IClassFixture
+ {
+ private static readonly string ConnectionString = ConnectionStrings.Get(nameof(SqlServerDatabaseInitializerTest));
+
+ private readonly SqlServerDatabase database;
+
+ private readonly SqlServerDatabaseInitializer initializer;
+
+ public SqlServerDatabaseInitializerTest(SqlServerDatabaseInitializer initializer)
+ {
+ this.initializer = initializer;
+ this.database = initializer.Initialize("PosInformatique.Testing.Databases.SqlServer.Tests.DacPac.dacpac", ConnectionString);
+
+ var table = this.database.ExecuteQuery("SELECT * FROM MyTable");
+
+ table.Rows.Should().BeEmpty();
+
+ // Insert data to check the connection.
+ this.database.InsertInto("MyTable", new { Id = 1, Name = "Name 1" });
+ this.database.InsertInto("MyTable", new { Id = 2, Name = "Name 2" });
+ }
+
+ [Fact]
+ public void Test1()
+ {
+ this.initializer.IsInitialized.Should().BeTrue();
+
+ var currentUser = this.database.ExecuteQuery("SELECT SUSER_NAME()");
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
+
+ // Check the constructor has been called
+ var table = this.database.ExecuteQuery("SELECT * FROM MyTable");
+
+ table.Rows.Should().HaveCount(2);
+
+ table.Rows[0]["Id"].Should().Be(1);
+ table.Rows[0]["Name"].Should().Be("Name 1");
+
+ table.Rows[1]["Id"].Should().Be(2);
+ table.Rows[1]["Name"].Should().Be("Name 2");
+
+ // Insert a row which should not be use in other tests.
+ this.database.InsertInto("MyTable", new { Id = 99, Name = "Should not be here for the next test" });
+ }
+
+ [Fact]
+ public void Test2()
+ {
+ this.initializer.IsInitialized.Should().BeTrue();
+
+ var currentUser = this.database.ExecuteQuery("SELECT SUSER_NAME()");
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
+
+ // Check the constructor has been called
+ var table = this.database.ExecuteQuery("SELECT * FROM MyTable");
+
+ table.Rows.Should().HaveCount(2);
+
+ table.Rows[0]["Id"].Should().Be(1);
+ table.Rows[0]["Name"].Should().Be("Name 1");
+
+ table.Rows[1]["Id"].Should().Be(2);
+ table.Rows[1]["Name"].Should().Be("Name 2");
+
+ // Insert a row which should not be use in other tests.
+ this.database.InsertInto("MyTable", new { Id = 99, Name = "Should not be here for the next test" });
+ }
+
+ [Fact]
+ public async Task Test1Async()
+ {
+ this.initializer.IsInitialized.Should().BeTrue();
+
+ var currentUser = await this.database.ExecuteQueryAsync("SELECT SUSER_NAME()", TestContext.Current.CancellationToken);
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
+
+ // Check the constructor has been called
+ var table = await this.database.ExecuteQueryAsync("SELECT * FROM MyTable", TestContext.Current.CancellationToken);
+
+ table.Rows.Should().HaveCount(2);
+
+ table.Rows[0]["Id"].Should().Be(1);
+ table.Rows[0]["Name"].Should().Be("Name 1");
+
+ table.Rows[1]["Id"].Should().Be(2);
+ table.Rows[1]["Name"].Should().Be("Name 2");
+
+ // Insert a row which should not be use in other tests.
+ await this.database.InsertIntoAsync("MyTable", new { Id = 99, Name = "Should not be here for the next test" });
+ }
+
+ [Fact]
+ public async Task Test2Async()
+ {
+ this.initializer.IsInitialized.Should().BeTrue();
+
+ var currentUser = await this.database.ExecuteQueryAsync("SELECT SUSER_NAME()", TestContext.Current.CancellationToken);
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
+
+ // Check the constructor has been called
+ var table = await this.database.ExecuteQueryAsync("SELECT * FROM MyTable", TestContext.Current.CancellationToken);
+
+ table.Rows.Should().HaveCount(2);
+
+ table.Rows[0]["Id"].Should().Be(1);
+ table.Rows[0]["Name"].Should().Be("Name 1");
+
+ table.Rows[1]["Id"].Should().Be(2);
+ table.Rows[1]["Name"].Should().Be("Name 2");
+
+ // Insert a row which should not be use in other tests.
+ await this.database.InsertIntoAsync("MyTable", new { Id = 99, Name = "Should not be here for the next test" });
+ }
+
+ [Fact]
+ public void Initialize_WithSpecificDataFileName()
+ {
+ this.initializer.IsInitialized.Should().BeTrue();
+
+ // Create existing database to be sure the database is recreated when deploying the database with a DACPAC
+ CreateDatabase("SqlServerDatabaseInitializerTest_Initialize_WithSpecificDataFileName");
+
+ using var otherDataPath = OtherDatabasePath.Create();
+
+ var server = new SqlServer(ConnectionString);
+
+ var settings = new SqlServerDacDeploymentSettings()
+ {
+ DataFileName = Path.Combine(otherDataPath.Path, "TheSpecificDataFileName.mdf"),
+ };
+
+ var database = server.DeployDacPackage("PosInformatique.Testing.Databases.SqlServer.Tests.DacPac.dacpac", "SqlServerDatabaseInitializerTest_Initialize_WithSpecificDataFileName", settings);
+
+ var table = database.ExecuteQuery("SELECT * FROM MyTable");
+
+ table.Rows.Should().BeEmpty();
+
+ // Insert data to check the connection.
+ database.InsertInto("MyTable", new { Id = 1, Name = "Name 1" });
+ database.InsertInto("MyTable", new { Id = 2, Name = "Name 2" });
+
+ // Check the location of the database
+ File.Exists(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName.mdf")).Should().BeTrue();
+ File.Exists(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName_log.ldf")).Should().BeTrue();
+
+ var result = database.ExecuteQuery("SELECT * FROM [sys].[database_files] ORDER BY [physical_name]");
+
+ result.Rows.Should().HaveCount(2);
+
+ result.Rows[0]["name"].Should().Be("SqlServerDatabaseInitializerTest_Initialize_WithSpecificDataFileName");
+ result.Rows[0]["physical_name"].Should().Be(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName.mdf"));
+ result.Rows[0]["type_desc"].Should().Be("ROWS");
+
+ result.Rows[1]["name"].Should().Be("SqlServerDatabaseInitializerTest_Initialize_WithSpecificDataFileName_log");
+ result.Rows[1]["physical_name"].Should().Be(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName_log.ldf"));
+ result.Rows[1]["type_desc"].Should().Be("LOG");
+
+ // Delete the database (for deleting the temporary folder).
+ server.DeleteDatabase("SqlServerDatabaseInitializerTest_Initialize_WithSpecificDataFileName");
+ }
+
+ [Fact]
+ public void Initialize_WithInitializerArgumentNull()
+ {
+ var act = () =>
+ {
+ SqlServerDacDatabaseInitializer.Initialize(null, default, default);
+ };
+
+ act.Should().ThrowExactly()
+ .WithParameterName("initializer");
+ }
+
+ [Fact]
+ public void Initialize_WithFileNameArgumentNull()
+ {
+ var initializer = new SqlServerDatabaseInitializer();
+
+ initializer.Invoking(i => i.Initialize(null, default))
+ .Should().ThrowExactly()
+ .WithParameterName("packageName");
+ }
+
+ [Fact]
+ public void Initialize_WithConnectionStringArgumentNull()
+ {
+ var initializer = new SqlServerDatabaseInitializer();
+
+ initializer.Invoking(i => i.Initialize("The file name", null))
+ .Should().ThrowExactly()
+ .WithParameterName("connectionString");
+ }
+
+ [Fact]
+ public void Initialize_WithPackageNotFound()
+ {
+ var initializer = new SqlServerDatabaseInitializer();
+
+ initializer.Invoking(i => i.Initialize("C:/Directory/FileNotFound.sql", "The connection stirng"))
+ .Should().ThrowExactly()
+ .WithMessage("Could not find file 'C:/Directory/FileNotFound.sql'")
+ .Which.FileName.Should().Be("C:/Directory/FileNotFound.sql");
+ }
+
+ private static void CreateDatabase(string name)
+ {
+ var server = new SqlServer(ConnectionString);
+
+ var database = server.CreateEmptyDatabase(name);
+
+ database.ExecuteNonQuery("CREATE TABLE OtherTable (Id INT)");
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Dac.Tests/Testing.Databases.SqlServer.Dac.Tests.csproj b/tests/Testing.Databases.SqlServer.Dac.Tests/Testing.Databases.SqlServer.Dac.Tests.csproj
new file mode 100644
index 0000000..3ad7b37
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Dac.Tests/Testing.Databases.SqlServer.Dac.Tests.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net8.0
+ Exe
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Testing.Databases.SqlServer.EntityFramework.Tests/EntityFrameworkDatabaseInitializerExtensionsTest.cs b/tests/Testing.Databases.SqlServer.EntityFramework.Tests/EntityFrameworkDatabaseInitializerExtensionsTest.cs
index 4ab4d63..503d882 100644
--- a/tests/Testing.Databases.SqlServer.EntityFramework.Tests/EntityFrameworkDatabaseInitializerExtensionsTest.cs
+++ b/tests/Testing.Databases.SqlServer.EntityFramework.Tests/EntityFrameworkDatabaseInitializerExtensionsTest.cs
@@ -11,7 +11,7 @@ namespace PosInformatique.Testing.Databases.SqlServer.Tests
[Collection("PosInformatique.Testing.Databases.SqlServer.Tests")]
public class EntityFrameworkDatabaseInitializerExtensionsTest : IClassFixture
{
- private const string ConnectionString = $"Data Source=(localDB)\\posinfo-tests; Initial Catalog={nameof(EntityFrameworkDatabaseInitializerExtensionsTest)}; Integrated Security=True";
+ private static readonly string ConnectionString = ConnectionStrings.Get(nameof(EntityFrameworkDatabaseInitializerExtensionsTest));
private readonly SqlServerDatabase database;
@@ -36,7 +36,7 @@ public EntityFrameworkDatabaseInitializerExtensionsTest(SqlServerDatabaseInitial
public void Test1()
{
var currentUser = this.database.ExecuteQuery("SELECT SUSER_NAME()");
- currentUser.Rows[0][0].Should().Be($"{Environment.UserDomainName}\\{Environment.UserName}");
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
// Check the constructor has been called
var table = this.database.ExecuteQuery("SELECT * FROM MyTable");
@@ -57,7 +57,7 @@ public void Test1()
public void Test2()
{
var currentUser = this.database.ExecuteQuery("SELECT SUSER_NAME()");
- currentUser.Rows[0][0].Should().Be($"{Environment.UserDomainName}\\{Environment.UserName}");
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
// Check the constructor has been called
var table = this.database.ExecuteQuery("SELECT * FROM MyTable");
@@ -74,6 +74,28 @@ public void Test2()
this.database.InsertInto("MyTable", new { Id = 99, Name = "Should not be here for the next test" });
}
+ [Fact]
+ public void Initialize_WithInitializerArgumentNull()
+ {
+ var act = () =>
+ {
+ EntityFrameworkDatabaseInitializerExtensions.Initialize(null, default);
+ };
+
+ act.Should().ThrowExactly()
+ .WithParameterName("initializer");
+ }
+
+ [Fact]
+ public void Initialize_WithFileNameArgumentNull()
+ {
+ var initializer = new SqlServerDatabaseInitializer();
+
+ initializer.Invoking(i => i.Initialize(null))
+ .Should().ThrowExactly()
+ .WithParameterName("context");
+ }
+
private sealed class DbContextTest : DbContext
{
public DbContextTest(DbContextOptions options)
diff --git a/tests/Testing.Databases.SqlServer.EntityFramework.Tests/EntityFrameworkSqlServerExtensionsTest.cs b/tests/Testing.Databases.SqlServer.EntityFramework.Tests/EntityFrameworkSqlServerExtensionsTest.cs
index 1321b05..bf7b1e9 100644
--- a/tests/Testing.Databases.SqlServer.EntityFramework.Tests/EntityFrameworkSqlServerExtensionsTest.cs
+++ b/tests/Testing.Databases.SqlServer.EntityFramework.Tests/EntityFrameworkSqlServerExtensionsTest.cs
@@ -11,7 +11,7 @@ namespace PosInformatique.Testing.Databases.SqlServer.Tests
[Collection("PosInformatique.Testing.Databases.SqlServer.Tests")]
public class EntityFrameworkSqlServerExtensionsTest
{
- private const string ConnectionString = $"Data Source=(localDB)\\posinfo-tests; Integrated Security=True";
+ private static readonly string ConnectionString = ConnectionStrings.Get();
[Fact]
public async Task Create_WithNoExistingDatabase()
@@ -26,9 +26,9 @@ public async Task Create_WithNoExistingDatabase()
var database = server.CreateDatabase(nameof(EntityFrameworkSqlServerExtensionsTest), dbContext);
- database.ConnectionString.Should().Be("Data Source=(localDB)\\posinfo-tests;Initial Catalog=EntityFrameworkSqlServerExtensionsTest;Integrated Security=True");
+ database.ConnectionString.Should().Be(ConnectionStrings.Get("EntityFrameworkSqlServerExtensionsTest"));
- var tables = await database.GetTablesAsync();
+ var tables = await database.GetTablesAsync(TestContext.Current.CancellationToken);
tables.Should().HaveCount(1);
@@ -54,9 +54,9 @@ public async Task Create_WithAlreadyExistingDatabase()
var database = server.CreateDatabase(nameof(EntityFrameworkSqlServerExtensionsTest), dbContext);
- database.ConnectionString.Should().Be("Data Source=(localDB)\\posinfo-tests;Initial Catalog=EntityFrameworkSqlServerExtensionsTest;Integrated Security=True");
+ database.ConnectionString.Should().Be(ConnectionStrings.Get("EntityFrameworkSqlServerExtensionsTest"));
- var tables = await database.GetTablesAsync();
+ var tables = await database.GetTablesAsync(TestContext.Current.CancellationToken);
tables.Should().HaveCount(1);
@@ -76,13 +76,13 @@ public async Task CreateAsync_WithNoExistingDatabase()
using var dbContext = new DbContextTest(optionsBuilder.Options);
var server = new SqlServer(ConnectionString);
- await server.DeleteDatabaseAsync(nameof(EntityFrameworkSqlServerExtensionsTest));
+ await server.DeleteDatabaseAsync(nameof(EntityFrameworkSqlServerExtensionsTest), TestContext.Current.CancellationToken);
var database = await server.CreateDatabaseAsync(nameof(EntityFrameworkSqlServerExtensionsTest), dbContext);
- database.ConnectionString.Should().Be("Data Source=(localDB)\\posinfo-tests;Initial Catalog=EntityFrameworkSqlServerExtensionsTest;Integrated Security=True");
+ database.ConnectionString.Should().Be(ConnectionStrings.Get("EntityFrameworkSqlServerExtensionsTest"));
- var tables = await database.GetTablesAsync();
+ var tables = await database.GetTablesAsync(TestContext.Current.CancellationToken);
tables.Should().HaveCount(1);
@@ -102,15 +102,15 @@ public async Task CreateAsync_WithAlreadyExistingDatabase()
using var dbContext = new DbContextTest(optionsBuilder.Options);
var server = new SqlServer(ConnectionString);
- var emptyDatabase = await server.CreateEmptyDatabaseAsync(nameof(EntityFrameworkSqlServerExtensionsTest));
+ var emptyDatabase = await server.CreateEmptyDatabaseAsync(nameof(EntityFrameworkSqlServerExtensionsTest), default, TestContext.Current.CancellationToken);
- await emptyDatabase.ExecuteNonQueryAsync("CREATE TABLE [MustBeDeleted] ([Id] INT)");
+ await emptyDatabase.ExecuteNonQueryAsync("CREATE TABLE [MustBeDeleted] ([Id] INT)", TestContext.Current.CancellationToken);
var database = await server.CreateDatabaseAsync(nameof(EntityFrameworkSqlServerExtensionsTest), dbContext);
- database.ConnectionString.Should().Be("Data Source=(localDB)\\posinfo-tests;Initial Catalog=EntityFrameworkSqlServerExtensionsTest;Integrated Security=True");
+ database.ConnectionString.Should().Be(ConnectionStrings.Get("EntityFrameworkSqlServerExtensionsTest"));
- var tables = await database.GetTablesAsync();
+ var tables = await database.GetTablesAsync(TestContext.Current.CancellationToken);
tables.Should().HaveCount(1);
@@ -121,6 +121,70 @@ public async Task CreateAsync_WithAlreadyExistingDatabase()
tables[0].Columns[1].Name.Should().Be("Name");
}
+ [Fact]
+ public void CreateDatabase_WithServerNull()
+ {
+ var act = () =>
+ {
+ EntityFrameworkSqlServerExtensions.CreateDatabase(null, default, default);
+ };
+
+ act.Should().ThrowExactly()
+ .WithParameterName("server");
+ }
+
+ [Fact]
+ public void CreateDatabase_WithNameArgumentNull()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.Invoking(s => s.CreateDatabase(null, default))
+ .Should().ThrowExactly()
+ .WithParameterName("name");
+ }
+
+ [Fact]
+ public void CreateDatabase_WithContextArgumentNull()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.Invoking(s => s.CreateDatabase("The name", default))
+ .Should().ThrowExactly()
+ .WithParameterName("context");
+ }
+
+ [Fact]
+ public void CreateDatabaseAsync_WithServerNull()
+ {
+ var act = async () =>
+ {
+ await EntityFrameworkSqlServerExtensions.CreateDatabaseAsync(null, default, default);
+ };
+
+ act.Should().ThrowExactlyAsync()
+ .WithParameterName("server");
+ }
+
+ [Fact]
+ public void CreateDatabaseAsync_WithNameArgumentNull()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.Invoking(s => s.CreateDatabaseAsync(null, default))
+ .Should().ThrowExactlyAsync()
+ .WithParameterName("name");
+ }
+
+ [Fact]
+ public void CreateDatabaseAsync_WithContextArgumentNull()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.Invoking(s => s.CreateDatabaseAsync("The name", default))
+ .Should().ThrowExactlyAsync()
+ .WithParameterName("context");
+ }
+
private sealed class DbContextTest : DbContext
{
public DbContextTest(DbContextOptions options)
diff --git a/tests/Testing.Databases.SqlServer.EntityFramework.Tests/Testing.Databases.SqlServer.EntityFramework.Tests.csproj b/tests/Testing.Databases.SqlServer.EntityFramework.Tests/Testing.Databases.SqlServer.EntityFramework.Tests.csproj
index a9979d0..3fc3588 100644
--- a/tests/Testing.Databases.SqlServer.EntityFramework.Tests/Testing.Databases.SqlServer.EntityFramework.Tests.csproj
+++ b/tests/Testing.Databases.SqlServer.EntityFramework.Tests/Testing.Databases.SqlServer.EntityFramework.Tests.csproj
@@ -2,24 +2,13 @@
net8.0
+ Exe
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
diff --git a/tests/Testing.Databases.SqlServer.Shared.Tests/ConnectionStrings.cs b/tests/Testing.Databases.SqlServer.Shared.Tests/ConnectionStrings.cs
new file mode 100644
index 0000000..eac8d77
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Shared.Tests/ConnectionStrings.cs
@@ -0,0 +1,42 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer
+{
+ using Microsoft.Data.SqlClient;
+
+ public static class ConnectionStrings
+ {
+ public static string Get(string databaseName = "master")
+ {
+ var connectionString = Environment.GetEnvironmentVariable("SQL_SERVER_UNIT_TESTS_CONNECTION_STRING");
+
+ if (connectionString is null)
+ {
+ connectionString = $"Data Source=(localDB)\\posinfo-tests; Integrated Security=True";
+ }
+
+ var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString)
+ {
+ InitialCatalog = databaseName,
+ };
+
+ return connectionStringBuilder.ToString();
+ }
+
+ public static string ExtractUserName(string connectionString)
+ {
+ var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString);
+
+ if (connectionStringBuilder.IntegratedSecurity == true)
+ {
+ return $"{Environment.UserDomainName}\\{Environment.UserName}";
+ }
+
+ return connectionStringBuilder.UserID;
+ }
+ }
+}
diff --git a/tests/Testing.Databases.SqlServer.Shared.Tests/OtherDatabasePath.cs b/tests/Testing.Databases.SqlServer.Shared.Tests/OtherDatabasePath.cs
new file mode 100644
index 0000000..cd4a1b9
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Shared.Tests/OtherDatabasePath.cs
@@ -0,0 +1,54 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ public sealed class OtherDatabasePath : IDisposable
+ {
+ private readonly bool deleteOnDispose;
+
+ private OtherDatabasePath(string path, bool deleteOnDispose)
+ {
+ this.Path = path;
+ this.deleteOnDispose = deleteOnDispose;
+ }
+
+ public string Path { get; }
+
+ public static OtherDatabasePath Create()
+ {
+ var otherDataPath = Environment.GetEnvironmentVariable("SQL_SERVER_UNIT_TESTS_OTHER_DATA_PATH");
+
+ var deleteOnDispose = false;
+
+ if (otherDataPath is null)
+ {
+ otherDataPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "PosInformatique.Testing.Databases.SqlServer.Tests", Guid.NewGuid().ToString());
+
+ Directory.CreateDirectory(otherDataPath);
+
+ deleteOnDispose = true;
+ }
+
+ return new OtherDatabasePath(otherDataPath, deleteOnDispose);
+ }
+
+ public void Dispose()
+ {
+ if (this.deleteOnDispose)
+ {
+ try
+ {
+ Directory.Delete(this.Path, true);
+ }
+ catch (IOException)
+ {
+ // Ignore the errors.
+ }
+ }
+ }
+ }
+}
diff --git a/tests/Testing.Databases.SqlServer.Shared.Tests/Testing.Databases.SqlServer.Shared.Tests.projitems b/tests/Testing.Databases.SqlServer.Shared.Tests/Testing.Databases.SqlServer.Shared.Tests.projitems
new file mode 100644
index 0000000..277c408
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Shared.Tests/Testing.Databases.SqlServer.Shared.Tests.projitems
@@ -0,0 +1,15 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ 1554ea1b-37c8-4f10-9770-bf4dd8a7e80c
+
+
+ Testing.Databases.SqlServer.Shared.Tests
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Shared.Tests/Testing.Databases.SqlServer.Shared.Tests.shproj b/tests/Testing.Databases.SqlServer.Shared.Tests/Testing.Databases.SqlServer.Shared.Tests.shproj
new file mode 100644
index 0000000..f47ed9e
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Shared.Tests/Testing.Databases.SqlServer.Shared.Tests.shproj
@@ -0,0 +1,13 @@
+
+
+
+ 1554ea1b-37c8-4f10-9770-bf4dd8a7e80c
+ 14.0
+
+
+
+
+
+
+
+
diff --git a/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdCommandLineArgumentsBuilderTest.cs b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdCommandLineArgumentsBuilderTest.cs
new file mode 100644
index 0000000..adaed43
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdCommandLineArgumentsBuilderTest.cs
@@ -0,0 +1,188 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ using Microsoft.Data.SqlClient;
+
+ public class SqlCmdCommandLineArgumentsBuilderTest
+ {
+ [Fact]
+ public void Constructor()
+ {
+ var connectionString = new SqlConnectionStringBuilder()
+ {
+ DataSource = "The data source",
+ InitialCatalog = "The initial catalog",
+ IntegratedSecurity = true,
+ Password = "The password",
+ UserID = "The login",
+ };
+
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString);
+
+ argumentsBuilder.Database.Should().Be("The initial catalog");
+ argumentsBuilder.InputFile.Should().BeNull();
+ argumentsBuilder.LoginId.Should().Be("The login");
+ argumentsBuilder.Password.Should().Be("The password");
+ argumentsBuilder.Server.Should().Be("The data source");
+ argumentsBuilder.TrustedConnection.Should().BeTrue();
+ argumentsBuilder.Variables.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void Constructor_WithEmptyConnnectionString()
+ {
+ var connectionString = new SqlConnectionStringBuilder();
+
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString);
+
+ argumentsBuilder.Database.Should().BeEmpty();
+ argumentsBuilder.InputFile.Should().BeNull();
+ argumentsBuilder.LoginId.Should().BeEmpty();
+ argumentsBuilder.Password.Should().BeEmpty();
+ argumentsBuilder.Server.Should().BeEmpty();
+ argumentsBuilder.TrustedConnection.Should().BeFalse();
+ argumentsBuilder.Variables.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void Database_ValueChanged()
+ {
+ var connectionString = new SqlConnectionStringBuilder();
+
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString);
+
+ argumentsBuilder.Database = "The database";
+
+ argumentsBuilder.Database.Should().Be("The database");
+ }
+
+ [Fact]
+ public void InputFile_ValueChanged()
+ {
+ var connectionString = new SqlConnectionStringBuilder();
+
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString);
+
+ argumentsBuilder.InputFile = "The input file";
+
+ argumentsBuilder.InputFile.Should().Be("The input file");
+ }
+
+ [Fact]
+ public void Password_ValueChanged()
+ {
+ var connectionString = new SqlConnectionStringBuilder();
+
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString);
+
+ argumentsBuilder.Password = "The password";
+
+ argumentsBuilder.Password.Should().Be("The password");
+ }
+
+ [Fact]
+ public void Server_ValueChanged()
+ {
+ var connectionString = new SqlConnectionStringBuilder();
+
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString);
+
+ argumentsBuilder.Server = "The server";
+
+ argumentsBuilder.Server.Should().Be("The server");
+ }
+
+ [Fact]
+ public void LoginId_ValueChanged()
+ {
+ var connectionString = new SqlConnectionStringBuilder();
+
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString);
+
+ argumentsBuilder.LoginId = "The login";
+
+ argumentsBuilder.LoginId.Should().Be("The login");
+ }
+
+ [Fact]
+ public void TrustedConnection_ValueChanged()
+ {
+ var connectionString = new SqlConnectionStringBuilder();
+
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString);
+
+ argumentsBuilder.TrustedConnection = true;
+
+ argumentsBuilder.TrustedConnection.Should().BeTrue();
+ }
+
+ [Fact]
+ public void AddVariable()
+ {
+ var connectionString = new SqlConnectionStringBuilder();
+
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(connectionString);
+
+ argumentsBuilder.AddVariable("v1", "Value 1");
+ argumentsBuilder.AddVariable("v2", "Value 2");
+
+ argumentsBuilder.Variables.Should().HaveCount(2);
+
+ argumentsBuilder.Variables["v1"].Should().Be("Value 1");
+ argumentsBuilder.Variables["v2"].Should().Be("Value 2");
+ }
+
+ [Theory]
+ [InlineData("TheServer", "TheDatabase", "TheLogin", "ThePassword", false, "TheInputFile", "-d \"TheDatabase\" -i \"TheInputFile\" -U \"TheLogin\" -P \"ThePassword\" -S \"TheServer\" -v v1=\"Value 1\" -b")]
+ [InlineData("TheServer", "TheDatabase", null, null, true, "TheInputFile", "-d \"TheDatabase\" -i \"TheInputFile\" -E -S \"TheServer\" -v v1=\"Value 1\" -b")]
+ [InlineData("TheServer", "TheDatabase", "", "", true, "TheInputFile", "-d \"TheDatabase\" -i \"TheInputFile\" -E -S \"TheServer\" -v v1=\"Value 1\" -b")]
+ [InlineData("", "", "", "", false, "", "-v v1=\"Value 1\" -b")]
+ [InlineData(null, null, null, null, false, null, "-v v1=\"Value 1\" -b")]
+ public void ToString_ReturnsCommandLineArguments(string server, string database, string loginId, string password, bool trustedConnection, string inputFile, string expectedCommandLineArguments)
+ {
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(new SqlConnectionStringBuilder(string.Empty))
+ {
+ Database = database,
+ InputFile = inputFile,
+ LoginId = loginId,
+ Password = password,
+ Server = server,
+ TrustedConnection = trustedConnection,
+ };
+
+ argumentsBuilder.AddVariable("v1", "Value 1");
+
+ var commandLineArguments = argumentsBuilder.ToString();
+
+ commandLineArguments.Should().Be(expectedCommandLineArguments);
+ }
+
+ [Theory]
+ [InlineData("TheServer", "TheDatabase", "TheLogin", "ThePassword", false, "TheInputFile", "-d \"TheDatabase\" -i \"TheInputFile\" -U \"TheLogin\" -P \"ThePassword\" -S \"TheServer\" -b")]
+ [InlineData("TheServer", "TheDatabase", null, null, true, "TheInputFile", "-d \"TheDatabase\" -i \"TheInputFile\" -E -S \"TheServer\" -b")]
+ [InlineData("TheServer", "TheDatabase", "", "", true, "TheInputFile", "-d \"TheDatabase\" -i \"TheInputFile\" -E -S \"TheServer\" -b")]
+ [InlineData("", "", "", "", false, "", "-b")]
+ [InlineData(null, null, null, null, false, null, "-b")]
+ public void ToString_ReturnsCommandLineArguments_WithNoVariables(string server, string database, string loginId, string password, bool trustedConnection, string inputFile, string expectedCommandLineArguments)
+ {
+ var argumentsBuilder = new SqlCmdCommandLineArgumentsBuilder(new SqlConnectionStringBuilder(string.Empty))
+ {
+ Database = database,
+ InputFile = inputFile,
+ LoginId = loginId,
+ Password = password,
+ Server = server,
+ TrustedConnection = trustedConnection,
+ };
+
+ var commandLineArguments = argumentsBuilder.ToString();
+
+ commandLineArguments.Should().Be(expectedCommandLineArguments);
+ }
+ }
+}
diff --git a/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdDatabaseInitializerTest.Script.sql b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdDatabaseInitializerTest.Script.sql
new file mode 100644
index 0000000..177e0d6
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdDatabaseInitializerTest.Script.sql
@@ -0,0 +1,18 @@
+IF (DB_ID(N'$(DatabaseName)') IS NOT NULL)
+BEGIN
+ ALTER DATABASE [$(DatabaseName)]
+ SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
+ DROP DATABASE [$(DatabaseName)];
+END
+GO
+PRINT N'Creating database $(DatabaseName)...'
+GO
+CREATE DATABASE [$(DatabaseName)]
+GO
+USE [$(DatabaseName)];
+GO
+CREATE TABLE MyTable
+(
+ [Id] INT,
+ [Name] VARCHAR(50)
+)
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseInitializerTest.cs b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdDatabaseInitializerTest.cs
similarity index 51%
rename from tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseInitializerTest.cs
rename to tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdDatabaseInitializerTest.cs
index 44c72e0..35a427f 100644
--- a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseInitializerTest.cs
+++ b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdDatabaseInitializerTest.cs
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------
-//
+//
// Copyright (c) P.O.S Informatique. All rights reserved.
//
//-----------------------------------------------------------------------
@@ -7,20 +7,33 @@
namespace PosInformatique.Testing.Databases.SqlServer.Tests
{
[Collection("PosInformatique.Testing.Databases.SqlServer.Tests")]
- public class SqlServerDatabaseInitializerTest : IClassFixture
+ public class SqlCmdDatabaseInitializerTest : IClassFixture
{
- private const string ConnectionString = $"Data Source=(localDB)\\posinfo-tests; Initial Catalog={nameof(SqlServerDatabaseInitializerTest)}; Integrated Security=True";
+ private static readonly string ConnectionString = ConnectionStrings.Get(nameof(SqlCmdDatabaseInitializerTest));
private readonly SqlServerDatabase database;
- public SqlServerDatabaseInitializerTest(SqlServerDatabaseInitializer initializer)
+ private readonly SqlServerDatabaseInitializer initializer;
+
+ public SqlCmdDatabaseInitializerTest(SqlServerDatabaseInitializer initializer)
{
- this.database = initializer.Initialize("Testing.Databases.SqlServer.Tests.DacPac.dacpac", ConnectionString);
+ this.initializer = initializer;
+
+ var settings = new SqlCmdRunScriptSettings()
+ {
+ Variables =
+ {
+ { "DatabaseName", nameof(SqlCmdDatabaseInitializerTest) },
+ },
+ };
+
+ this.database = initializer.Initialize("SqlCmdDatabaseInitializerTest.Script.sql", ConnectionString, settings);
var table = this.database.ExecuteQuery("SELECT * FROM MyTable");
table.Rows.Should().BeEmpty();
+ // Insert data to check the connection.
this.database.InsertInto("MyTable", new { Id = 1, Name = "Name 1" });
this.database.InsertInto("MyTable", new { Id = 2, Name = "Name 2" });
}
@@ -28,8 +41,10 @@ public SqlServerDatabaseInitializerTest(SqlServerDatabaseInitializer initializer
[Fact]
public void Test1()
{
+ this.initializer.IsInitialized.Should().BeTrue();
+
var currentUser = this.database.ExecuteQuery("SELECT SUSER_NAME()");
- currentUser.Rows[0][0].Should().Be($"{Environment.UserDomainName}\\{Environment.UserName}");
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
// Check the constructor has been called
var table = this.database.ExecuteQuery("SELECT * FROM MyTable");
@@ -49,8 +64,10 @@ public void Test1()
[Fact]
public void Test2()
{
+ this.initializer.IsInitialized.Should().BeTrue();
+
var currentUser = this.database.ExecuteQuery("SELECT SUSER_NAME()");
- currentUser.Rows[0][0].Should().Be($"{Environment.UserDomainName}\\{Environment.UserName}");
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
// Check the constructor has been called
var table = this.database.ExecuteQuery("SELECT * FROM MyTable");
@@ -70,11 +87,13 @@ public void Test2()
[Fact]
public async Task Test1Async()
{
- var currentUser = await this.database.ExecuteQueryAsync("SELECT SUSER_NAME()");
- currentUser.Rows[0][0].Should().Be($"{Environment.UserDomainName}\\{Environment.UserName}");
+ this.initializer.IsInitialized.Should().BeTrue();
+
+ var currentUser = await this.database.ExecuteQueryAsync("SELECT SUSER_NAME()", TestContext.Current.CancellationToken);
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
// Check the constructor has been called
- var table = await this.database.ExecuteQueryAsync("SELECT * FROM MyTable");
+ var table = await this.database.ExecuteQueryAsync("SELECT * FROM MyTable", TestContext.Current.CancellationToken);
table.Rows.Should().HaveCount(2);
@@ -91,11 +110,13 @@ public async Task Test1Async()
[Fact]
public async Task Test2Async()
{
- var currentUser = await this.database.ExecuteQueryAsync("SELECT SUSER_NAME()");
- currentUser.Rows[0][0].Should().Be($"{Environment.UserDomainName}\\{Environment.UserName}");
+ this.initializer.IsInitialized.Should().BeTrue();
+
+ var currentUser = await this.database.ExecuteQueryAsync("SELECT SUSER_NAME()", TestContext.Current.CancellationToken);
+ currentUser.Rows[0][0].Should().Be(ConnectionStrings.ExtractUserName(ConnectionString));
// Check the constructor has been called
- var table = await this.database.ExecuteQueryAsync("SELECT * FROM MyTable");
+ var table = await this.database.ExecuteQueryAsync("SELECT * FROM MyTable", TestContext.Current.CancellationToken);
table.Rows.Should().HaveCount(2);
@@ -108,5 +129,48 @@ public async Task Test2Async()
// Insert a row which should not be use in other tests.
await this.database.InsertIntoAsync("MyTable", new { Id = 99, Name = "Should not be here for the next test" });
}
+
+ [Fact]
+ public void Initialize_WithInitializerArgumentNull()
+ {
+ var act = () =>
+ {
+ SqlCmdDatabaseInitializer.Initialize(null, default, default);
+ };
+
+ act.Should().ThrowExactly()
+ .WithParameterName("initializer");
+ }
+
+ [Fact]
+ public void Initialize_WithFileNameArgumentNull()
+ {
+ var initializer = new SqlServerDatabaseInitializer();
+
+ initializer.Invoking(i => i.Initialize(null, default))
+ .Should().ThrowExactly()
+ .WithParameterName("fileName");
+ }
+
+ [Fact]
+ public void Initialize_WithConnectionStringArgumentNull()
+ {
+ var initializer = new SqlServerDatabaseInitializer();
+
+ initializer.Invoking(i => i.Initialize("The file name", null))
+ .Should().ThrowExactly()
+ .WithParameterName("connectionString");
+ }
+
+ [Fact]
+ public void Initialize_WithFileNotFound()
+ {
+ var initializer = new SqlServerDatabaseInitializer();
+
+ initializer.Invoking(i => i.Initialize("C:/Directory/FileNotFound.sql", "The connection stirng"))
+ .Should().ThrowExactly()
+ .WithMessage("Could not find file 'C:/Directory/FileNotFound.sql'")
+ .Which.FileName.Should().Be("C:/Directory/FileNotFound.sql");
+ }
}
}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdExceptionTest.cs b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdExceptionTest.cs
new file mode 100644
index 0000000..d0243ea
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdExceptionTest.cs
@@ -0,0 +1,52 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ public class SqlCmdExceptionTest
+ {
+ [Fact]
+ public void Constructor()
+ {
+ var exception = new SqlCmdException();
+
+ exception.Message.Should().Be("Exception of type 'PosInformatique.Testing.Databases.SqlServer.SqlCmdException' was thrown.");
+ exception.Output.Should().BeNull();
+ exception.InnerException.Should().BeNull();
+ }
+
+ [Fact]
+ public void Constructor_WithMessage()
+ {
+ var exception = new SqlCmdException("The message");
+
+ exception.Message.Should().Be("The message");
+ exception.Output.Should().BeNull();
+ exception.InnerException.Should().BeNull();
+ }
+
+ [Fact]
+ public void Constructor_WithMessageAndOutput()
+ {
+ var exception = new SqlCmdException("The message", "The output");
+
+ exception.Message.Should().Be("The message");
+ exception.Output.Should().Be("The output");
+ exception.InnerException.Should().BeNull();
+ }
+
+ [Fact]
+ public void Constructor_WithMessageAndInnerException()
+ {
+ var innerException = new FormatException("The inner exception");
+ var exception = new SqlCmdException("The message", innerException);
+
+ exception.Message.Should().Be("The message");
+ exception.Output.Should().BeNull();
+ exception.InnerException.Should().BeSameAs(innerException);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdProcessTest.cs b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdProcessTest.cs
new file mode 100644
index 0000000..3c6c158
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdProcessTest.cs
@@ -0,0 +1,26 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ using Microsoft.Data.SqlClient;
+
+ public class SqlCmdProcessTest
+ {
+ [Fact]
+ public void WaitForExit_Disposed()
+ {
+ var process = SqlCmdProcess.RunScript(new SqlConnectionStringBuilder(), "NoFile.sql", new SqlCmdRunScriptSettings());
+
+ process.Dispose();
+
+ process.Invoking(p => p.WaitForExit())
+ .Should().ThrowExactly()
+ .WithMessage($"Cannot access a disposed object.{Environment.NewLine}Object name: 'PosInformatique.Testing.Databases.SqlServer.SqlCmdProcess'.")
+ .Which.ObjectName.Should().Be("PosInformatique.Testing.Databases.SqlServer.SqlCmdProcess");
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdRunScriptSettingsTest.cs b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdRunScriptSettingsTest.cs
new file mode 100644
index 0000000..2b4c560
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdRunScriptSettingsTest.cs
@@ -0,0 +1,19 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ public class SqlCmdRunScriptSettingsTest
+ {
+ [Fact]
+ public void Constructor()
+ {
+ var settings = new SqlCmdRunScriptSettings();
+
+ settings.Variables.Should().BeEmpty();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdSqlServerExtensionsTest.cs b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdSqlServerExtensionsTest.cs
new file mode 100644
index 0000000..27d45dd
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/SqlCmdSqlServerExtensionsTest.cs
@@ -0,0 +1,175 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ [Collection("PosInformatique.Testing.Databases.SqlServer.Tests")]
+ public class SqlCmdSqlServerExtensionsTest
+ {
+ private static readonly string ConnectionString = ConnectionStrings.Get();
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void RunScript(bool withSettings)
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.DeleteDatabase("SqlCmdSqlServerExtensionsTest_RunScript");
+
+ SqlCmdRunScriptSettings settings = null;
+
+ if (withSettings)
+ {
+ settings = new SqlCmdRunScriptSettings();
+ }
+
+ using var temporaryFile = TemporaryFile.Create();
+
+ File.WriteAllText(
+ temporaryFile.FileName,
+ """
+ PRINT 'GOOOOOO !'
+ GO
+ CREATE DATABASE SqlCmdSqlServerExtensionsTest_RunScript
+ GO
+ USE SqlCmdSqlServerExtensionsTest_RunScript
+ GO
+ CREATE TABLE MyTable (Name VARCHAR(50))
+ """);
+
+ server.Master.RunScript(temporaryFile.FileName, settings);
+
+ var database = server.GetDatabase("SqlCmdSqlServerExtensionsTest_RunScript");
+
+ var table = database.ExecuteQuery("SELECT * FROM MyTable");
+
+ table.Rows.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void RunScript_WithVariables()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.DeleteDatabase("SqlCmdSqlServerExtensionsTest_RunScript_WithVariables");
+
+ var settings = new SqlCmdRunScriptSettings()
+ {
+ Variables =
+ {
+ { "DatabaseName", "SqlCmdSqlServerExtensionsTest_RunScript_WithVariables" },
+ { "TableName", "MyTable" },
+ },
+ };
+
+ using var temporaryFile = TemporaryFile.Create();
+
+ File.WriteAllText(
+ temporaryFile.FileName,
+ """
+ PRINT 'GOOOOOO !'
+ GO
+ CREATE DATABASE [$(DatabaseName)]
+ GO
+ USE [$(DatabaseName)]
+ GO
+ CREATE TABLE [$(TableName)] (Name VARCHAR(50))
+ """);
+
+ server.Master.RunScript(temporaryFile.FileName, settings);
+
+ var database = server.GetDatabase("SqlCmdSqlServerExtensionsTest_RunScript_WithVariables");
+
+ var table = database.ExecuteQuery("SELECT * FROM MyTable");
+
+ table.Rows.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void RunScript_WithErrors()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.DeleteDatabase("SqlCmdSqlServerExtensionsTest_RunScript_WithErrors");
+
+ var settings = new SqlCmdRunScriptSettings()
+ {
+ Variables =
+ {
+ { "DatabaseName", "SqlCmdSqlServerExtensionsTest_RunScript_WithErrors" },
+ { "TableName", "MyTable" },
+ },
+ };
+
+ using var temporaryFile = TemporaryFile.Create();
+
+ File.WriteAllText(
+ temporaryFile.FileName,
+ """
+ PRINT 'GOOOOOO !'
+ GO
+ CREATE DATABASE [$(DatabaseName)]
+ GO
+ USE [$(DatabaseName)]
+ GO
+ CREATE TABLE ErrorBlabla
+ """);
+
+ var exception = server.Master.Invoking(m => m.RunScript(temporaryFile.FileName, settings))
+ .Should().ThrowExactly();
+
+ exception.Which.Output.Should().StartWith(
+ """
+ GOOOOOO !
+ Changed database context to 'SqlCmdSqlServerExtensionsTest_RunScript_WithErrors'.
+ Msg 102, Level 15, State 1,
+ """)
+ .And.EndWith("Incorrect syntax near 'ErrorBlabla'.");
+
+ exception.Which.Message.Should().Be($"Some errors has been occurred when executing the '{temporaryFile.FileName}'.{Environment.NewLine}{Environment.NewLine}-- Output --{Environment.NewLine}{exception.Which.Output}");
+
+ var database = server.GetDatabase("SqlCmdSqlServerExtensionsTest_RunScript_WithErrors");
+
+ var table = database.ExecuteQuery("SELECT * FROM sys.tables");
+
+ table.Rows.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void RunScript_WithDatabaseArgumentNull()
+ {
+ var act = () =>
+ {
+ SqlCmdSqlServerDatabaseExtensions.RunScript(null, default, default);
+ };
+
+ act.Should().ThrowExactly()
+ .WithParameterName("database");
+ }
+
+ [Fact]
+ public void RunScript_WithFileNameArgumentNull()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.Master.Invoking(m => m.RunScript(null, default))
+ .Should().ThrowExactly()
+ .WithParameterName("fileName");
+ }
+
+ [Fact]
+ public void RunScript_WithFileNotFound()
+ {
+ var server = new SqlServer(ConnectionString);
+
+ server.Master.Invoking(m => m.RunScript("C:/Directory/FileNotFound.sql", default))
+ .Should().ThrowExactly()
+ .WithMessage("Could not find file 'C:/Directory/FileNotFound.sql'")
+ .Which.FileName.Should().Be("C:/Directory/FileNotFound.sql");
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.SqlCmd.Tests/TemporaryFile.cs b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/TemporaryFile.cs
new file mode 100644
index 0000000..b1ffd60
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/TemporaryFile.cs
@@ -0,0 +1,40 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ internal sealed class TemporaryFile : IDisposable
+ {
+ private TemporaryFile(string fileName)
+ {
+ this.FileName = fileName;
+ }
+
+ public string FileName { get; }
+
+ public static TemporaryFile Create()
+ {
+ var temporaryFileName = Path.GetTempFileName();
+
+ return new TemporaryFile(temporaryFileName);
+ }
+
+ public void Dispose()
+ {
+ try
+ {
+ if (File.Exists(this.FileName))
+ {
+ File.Delete(this.FileName);
+ }
+ }
+ catch (IOException)
+ {
+ // Ignore the errors.
+ }
+ }
+ }
+}
diff --git a/tests/Testing.Databases.SqlServer.SqlCmd.Tests/Testing.Databases.SqlServer.SqlCmd.Tests.csproj b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/Testing.Databases.SqlServer.SqlCmd.Tests.csproj
new file mode 100644
index 0000000..5e8124b
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.SqlCmd.Tests/Testing.Databases.SqlServer.SqlCmd.Tests.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net8.0
+ Exe
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Testing.Databases.SqlServer.Tests.DacPac/Testing.Databases.SqlServer.Tests.DacPac.csproj b/tests/Testing.Databases.SqlServer.Tests.DacPac/Testing.Databases.SqlServer.Tests.DacPac.csproj
new file mode 100644
index 0000000..30ca847
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Tests.DacPac/Testing.Databases.SqlServer.Tests.DacPac.csproj
@@ -0,0 +1,26 @@
+
+
+ netstandard2.1
+ Sql150
+
+ True
+
+ True
+ False
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests.DacPac/Testing.Databases.SqlServer.Tests.DacPac.sqlproj b/tests/Testing.Databases.SqlServer.Tests.DacPac/Testing.Databases.SqlServer.Tests.DacPac.sqlproj
deleted file mode 100644
index c4e689c..0000000
--- a/tests/Testing.Databases.SqlServer.Tests.DacPac/Testing.Databases.SqlServer.Tests.DacPac.sqlproj
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
- Debug
- AnyCPU
- Testing.Databases.SqlServer.Tests.DacPac
- 2.0
- 4.1
- {5f618225-0e1c-46a7-bbcc-23a6243d5cee}
- Microsoft.Data.Tools.Schema.Sql.Sql150DatabaseSchemaProvider
- Database
-
-
- Testing.Databases.SqlServer.Tests.DacPac
- Testing.Databases.SqlServer.Tests.DacPac
- 1033, CI
- BySchemaAndSchemaType
- True
- v4.7.2
- CS
- Properties
- False
- True
- True
-
-
- bin\Release\
- $(MSBuildProjectName).sql
- False
- pdbonly
- true
- false
- true
- prompt
- 4
-
-
- bin\Debug\
- $(MSBuildProjectName).sql
- false
- true
- full
- false
- true
- true
- prompt
- 4
-
-
- 11.0
-
- True
- 11.0
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests.Source/Testing.Databases.SqlServer.Tests.Source.csproj b/tests/Testing.Databases.SqlServer.Tests.Source/Testing.Databases.SqlServer.Tests.Source.csproj
new file mode 100644
index 0000000..30ca847
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Tests.Source/Testing.Databases.SqlServer.Tests.Source.csproj
@@ -0,0 +1,26 @@
+
+
+ netstandard2.1
+ Sql150
+
+ True
+
+ True
+ False
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests.Source/Testing.Databases.SqlServer.Tests.Source.sqlproj b/tests/Testing.Databases.SqlServer.Tests.Source/Testing.Databases.SqlServer.Tests.Source.sqlproj
deleted file mode 100644
index 60bd7b0..0000000
--- a/tests/Testing.Databases.SqlServer.Tests.Source/Testing.Databases.SqlServer.Tests.Source.sqlproj
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-
- Debug
- AnyCPU
- Testing.Databases.SqlServer.Tests.Source
- 2.0
- 4.1
- {a261d4ff-9bea-475c-8671-e9bacfdce960}
- Microsoft.Data.Tools.Schema.Sql.Sql150DatabaseSchemaProvider
- Database
-
-
- Testing.Databases.SqlServer.Tests.Source
- Testing.Databases.SqlServer.Tests.Source
- 1033, CI
- BySchemaAndSchemaType
- True
- v4.7.2
- CS
- Properties
- False
- True
- True
-
-
- bin\Release\
- $(MSBuildProjectName).sql
- False
- pdbonly
- true
- false
- true
- prompt
- 4
-
-
- bin\Debug\
- $(MSBuildProjectName).sql
- false
- true
- full
- false
- true
- true
- prompt
- 4
-
-
- 11.0
-
- True
- 11.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests.Target/Testing.Databases.SqlServer.Tests.Target.csproj b/tests/Testing.Databases.SqlServer.Tests.Target/Testing.Databases.SqlServer.Tests.Target.csproj
new file mode 100644
index 0000000..c9df446
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Tests.Target/Testing.Databases.SqlServer.Tests.Target.csproj
@@ -0,0 +1,36 @@
+
+
+ netstandard2.1
+ Sql150
+
+ True
+
+ True
+ False
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests.Target/Testing.Databases.SqlServer.Tests.Target.sqlproj b/tests/Testing.Databases.SqlServer.Tests.Target/Testing.Databases.SqlServer.Tests.Target.sqlproj
deleted file mode 100644
index d34fed2..0000000
--- a/tests/Testing.Databases.SqlServer.Tests.Target/Testing.Databases.SqlServer.Tests.Target.sqlproj
+++ /dev/null
@@ -1,121 +0,0 @@
-
-
-
- Debug
- AnyCPU
- Testing.Databases.SqlServer.Tests.Target
- 2.0
- 4.1
- {6cd3f177-053f-4816-a37e-5ca6f293d34c}
- Microsoft.Data.Tools.Schema.Sql.Sql150DatabaseSchemaProvider
- Database
-
-
- Testing.Databases.SqlServer.Tests.Target
- Testing.Databases.SqlServer.Tests.Target
- 1033, CI
- BySchemaAndSchemaType
- True
- v4.7.2
- CS
- Properties
- False
- True
- True
-
-
- bin\Release\
- $(MSBuildProjectName).sql
- False
- pdbonly
- true
- false
- true
- prompt
- 4
-
-
- bin\Debug\
- $(MSBuildProjectName).sql
- false
- true
- full
- false
- true
- true
- prompt
- 4
-
-
- 11.0
-
- True
- 11.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Tables\TableIdentical.sql
-
-
-
-
- Tables\PrimaryKey\PrimaryKeyIdentical.sql
-
-
-
-
-
- Programmability\Types\TypeIdentical.sql
-
-
-
-
- Tables\UniqueConstraints\UniqueConstraintIdentical.sql
-
-
-
- Tables\ReferencedTable.sql
-
-
- Tables\ForeignKeys\ForeignKeyIdentical.sql
-
-
-
-
-
- Tables\Indexes\IndexIdentical.sql
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectDifferencesTest.cs b/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectDifferencesTest.cs
index bf57600..87b9249 100644
--- a/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectDifferencesTest.cs
+++ b/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectDifferencesTest.cs
@@ -22,7 +22,7 @@ public void ToStringTest()
var difference = new SqlObjectDifferences(source, target, default, properties);
- difference.ToString().Should().Be("The source\r\n * The prop1:\r\n Source: 10\r\n Target: 20\r\n * The prop2:\r\n Source: 30\r\n Target: 40\r\n");
+ difference.ToString().Should().Be($"The source{Environment.NewLine} * The prop1:{Environment.NewLine} Source: 10{Environment.NewLine} Target: 20{Environment.NewLine} * The prop2:{Environment.NewLine} Source: 30{Environment.NewLine} Target: 40{Environment.NewLine}");
}
}
}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectPropertyDifferenceTest.cs b/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectPropertyDifferenceTest.cs
index ec44380..7159078 100644
--- a/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectPropertyDifferenceTest.cs
+++ b/tests/Testing.Databases.SqlServer.Tests/Comparer/SqlObjectPropertyDifferenceTest.cs
@@ -13,7 +13,7 @@ public void ToString_NotNull()
{
var difference = new SqlObjectPropertyDifference("The name", 12, 34);
- difference.ToString().Should().Be("* The name:\r\n Source: 12\r\n Target: 34\r\n");
+ difference.ToString().Should().Be($"* The name:{Environment.NewLine} Source: 12{Environment.NewLine} Target: 34{Environment.NewLine}");
}
[Fact]
@@ -21,7 +21,7 @@ public void ToString_Null()
{
var difference = new SqlObjectPropertyDifference("The name", null, null);
- difference.ToString().Should().Be("* The name:\r\n Source: \r\n Target: \r\n");
+ difference.ToString().Should().Be($"* The name:{Environment.NewLine} Source: {Environment.NewLine} Target: {Environment.NewLine}");
}
}
}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests/SqlDatabaseCreationSettingsTest.cs b/tests/Testing.Databases.SqlServer.Tests/SqlDatabaseCreationSettingsTest.cs
new file mode 100644
index 0000000..7617dbe
--- /dev/null
+++ b/tests/Testing.Databases.SqlServer.Tests/SqlDatabaseCreationSettingsTest.cs
@@ -0,0 +1,29 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) P.O.S Informatique. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace PosInformatique.Testing.Databases.SqlServer.Tests
+{
+ public class SqlDatabaseCreationSettingsTest
+ {
+ [Fact]
+ public void Constructor()
+ {
+ var settings = new SqlDatabaseCreationSettings();
+
+ settings.DataFileName.Should().BeNull();
+ }
+
+ [Fact]
+ public void DataFileName_ValueChanged()
+ {
+ var settings = new SqlDatabaseCreationSettings();
+
+ settings.DataFileName = "New value";
+
+ settings.DataFileName.Should().Be("New value");
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseComparerTest.CompareAsync.txt b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseComparerTest.CompareAsync.txt
index 80b106f..ceb8e7e 100644
--- a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseComparerTest.CompareAsync.txt
+++ b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseComparerTest.CompareAsync.txt
@@ -142,6 +142,7 @@
BEGIN
PRINT 'From source'
END
+
Target:
CREATE TRIGGER [TriggerDifference]
ON [dbo].[TableDifference]
@@ -150,6 +151,7 @@
BEGIN
PRINT 'From target'
END
+
------ Unique constraints ------
- UniqueConstraintDifference
* Type:
@@ -197,6 +199,7 @@
AS
SELECT @param2
RETURN 0
+
Target:
CREATE PROCEDURE [dbo].[StoredProcedureDifference]
@param1 int = 0,
@@ -204,6 +207,7 @@
AS
SELECT @param1
RETURN 0
+
- dbo.StoredProcedureTarget (Missing in the source)
- dbo.StoredProcedureSource (Missing in the target)
------ User types ------
@@ -222,8 +226,10 @@
Source:
CREATE VIEW [dbo].[ViewDifference]
AS SELECT * FROM [TableDifference] WHERE [Type] = 10
+
Target:
CREATE VIEW [dbo].[ViewDifference]
AS SELECT * FROM [TableDifference] WHERE [Type] = 'The type'
+
- dbo.ViewTarget (Missing in the source)
- dbo.ViewSource (Missing in the target)
diff --git a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseComparerTest.cs b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseComparerTest.cs
index 03eb07b..43a5045 100644
--- a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseComparerTest.cs
+++ b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseComparerTest.cs
@@ -9,15 +9,15 @@ namespace PosInformatique.Testing.Databases.SqlServer.Tests
[Collection("PosInformatique.Testing.Databases.SqlServer.Tests")]
public class SqlServerDatabaseComparerTest
{
- private const string ConnectionString = $"Data Source=(localDB)\\posinfo-tests; Integrated Security=True";
+ private static readonly string ConnectionString = ConnectionStrings.Get();
[Fact]
public async Task CompareAsync()
{
var server = new SqlServer(ConnectionString);
- var sourceDatabase = Task.Run(() => server.DeployDacPackage("Testing.Databases.SqlServer.Tests.Source.dacpac", $"{nameof(SqlServerDatabaseComparerTest)}_Source"));
- var targetDatabase = Task.Run(() => server.DeployDacPackage("Testing.Databases.SqlServer.Tests.Target.dacpac", $"{nameof(SqlServerDatabaseComparerTest)}_Target"));
+ var sourceDatabase = Task.Run(() => server.Master.RunScript("Testing.Databases.SqlServer.Tests.Source_Create.sql"), TestContext.Current.CancellationToken);
+ var targetDatabase = Task.Run(() => server.Master.RunScript("Testing.Databases.SqlServer.Tests.Target_Create.sql"), TestContext.Current.CancellationToken);
await Task.WhenAll(sourceDatabase, targetDatabase);
@@ -30,7 +30,7 @@ public async Task CompareAsync()
},
};
- var differences = await SqlServerDatabaseComparer.CompareAsync(sourceDatabase.Result, targetDatabase.Result, options);
+ var differences = await SqlServerDatabaseComparer.CompareAsync(server.GetDatabase("Testing.Databases.SqlServer.Tests.Source"), server.GetDatabase("Testing.Databases.SqlServer.Tests.Target"), options, TestContext.Current.CancellationToken);
// StoredProcedures
differences.StoredProcedures.Should().HaveCount(3);
@@ -40,8 +40,8 @@ public async Task CompareAsync()
differences.StoredProcedures[0].Target.Schema.Should().Be("dbo");
differences.StoredProcedures[0].Properties.Should().HaveCount(1);
differences.StoredProcedures[0].Properties[0].Name.Should().Be("Code");
- differences.StoredProcedures[0].Properties[0].Source.Should().Be("CREATE PROCEDURE [dbo].[StoredProcedureDifference]\r\n\t@param1 int = 0,\r\n\t@param2 int\r\nAS\r\n\tSELECT @param2\r\nRETURN 0");
- differences.StoredProcedures[0].Properties[0].Target.Should().Be("CREATE PROCEDURE [dbo].[StoredProcedureDifference]\r\n\t@param1 int = 0,\r\n\t@param2 int\r\nAS\r\n\tSELECT @param1\r\nRETURN 0");
+ differences.StoredProcedures[0].Properties[0].Source.Should().Be($"CREATE PROCEDURE [dbo].[StoredProcedureDifference]{Environment.NewLine}\t@param1 int = 0,{Environment.NewLine}\t@param2 int{Environment.NewLine}AS{Environment.NewLine}\tSELECT @param2{Environment.NewLine}RETURN 0{Environment.NewLine}");
+ differences.StoredProcedures[0].Properties[0].Target.Should().Be($"CREATE PROCEDURE [dbo].[StoredProcedureDifference]{Environment.NewLine}\t@param1 int = 0,{Environment.NewLine}\t@param2 int{Environment.NewLine}AS{Environment.NewLine}\tSELECT @param1{Environment.NewLine}RETURN 0{Environment.NewLine}");
differences.StoredProcedures[1].Source.Should().BeNull();
differences.StoredProcedures[1].Target.Name.Should().Be("StoredProcedureTarget");
@@ -697,13 +697,13 @@ public async Task CompareAsync()
differences.Tables[0].Source.Triggers.Should().HaveCount(1);
differences.Tables[0].Source.Triggers[0].Name.Should().Be("TriggerDifference");
- differences.Tables[0].Source.Triggers[0].Code.Should().Be("CREATE TRIGGER [TriggerDifference]\r\n\tON [dbo].[TableDifference]\r\n\tINSTEAD OF INSERT\r\n\tAS\r\n\tBEGIN\r\n\t\tPRINT 'From source'\r\n\tEND");
+ differences.Tables[0].Source.Triggers[0].Code.Should().Be($"CREATE TRIGGER [TriggerDifference]{Environment.NewLine}\tON [dbo].[TableDifference]{Environment.NewLine}\tINSTEAD OF INSERT{Environment.NewLine}\tAS{Environment.NewLine}\tBEGIN{Environment.NewLine}\t\tPRINT 'From source'{Environment.NewLine}\tEND{Environment.NewLine}");
differences.Tables[0].Source.Triggers[0].IsInsteadOfTrigger.Should().BeTrue();
differences.Tables[0].Target.Triggers.Should().HaveCount(1);
differences.Tables[0].Target.Triggers[0].Name.Should().Be("TriggerDifference");
- differences.Tables[0].Target.Triggers[0].Code.Should().Be("CREATE TRIGGER [TriggerDifference]\r\n\tON [dbo].[TableDifference]\r\n\tFOR INSERT\r\n\tAS\r\n\tBEGIN\r\n\t\tPRINT 'From target'\r\n\tEND");
+ differences.Tables[0].Target.Triggers[0].Code.Should().Be($"CREATE TRIGGER [TriggerDifference]{Environment.NewLine}\tON [dbo].[TableDifference]{Environment.NewLine}\tFOR INSERT{Environment.NewLine}\tAS{Environment.NewLine}\tBEGIN{Environment.NewLine}\t\tPRINT 'From target'{Environment.NewLine}\tEND{Environment.NewLine}");
differences.Tables[0].Target.Triggers[0].IsInsteadOfTrigger.Should().BeFalse();
differences.Tables[0].Triggers.Should().HaveCount(1);
@@ -712,8 +712,8 @@ public async Task CompareAsync()
differences.Tables[0].Triggers[0].Properties[0].Source.Should().Be(true);
differences.Tables[0].Triggers[0].Properties[0].Target.Should().Be(false);
differences.Tables[0].Triggers[0].Properties[1].Name.Should().Be("Code");
- differences.Tables[0].Triggers[0].Properties[1].Source.Should().Be("CREATE TRIGGER [TriggerDifference]\r\n\tON [dbo].[TableDifference]\r\n\tINSTEAD OF INSERT\r\n\tAS\r\n\tBEGIN\r\n\t\tPRINT 'From source'\r\n\tEND");
- differences.Tables[0].Triggers[0].Properties[1].Target.Should().Be("CREATE TRIGGER [TriggerDifference]\r\n\tON [dbo].[TableDifference]\r\n\tFOR INSERT\r\n\tAS\r\n\tBEGIN\r\n\t\tPRINT 'From target'\r\n\tEND");
+ differences.Tables[0].Triggers[0].Properties[1].Source.Should().Be($"CREATE TRIGGER [TriggerDifference]{Environment.NewLine}\tON [dbo].[TableDifference]{Environment.NewLine}\tINSTEAD OF INSERT{Environment.NewLine}\tAS{Environment.NewLine}\tBEGIN{Environment.NewLine}\t\tPRINT 'From source'{Environment.NewLine}\tEND{Environment.NewLine}");
+ differences.Tables[0].Triggers[0].Properties[1].Target.Should().Be($"CREATE TRIGGER [TriggerDifference]{Environment.NewLine}\tON [dbo].[TableDifference]{Environment.NewLine}\tFOR INSERT{Environment.NewLine}\tAS{Environment.NewLine}\tBEGIN{Environment.NewLine}\t\tPRINT 'From target'{Environment.NewLine}\tEND{Environment.NewLine}");
differences.Tables[0].Triggers[0].Source.Should().BeSameAs(differences.Tables[0].Source.Triggers[0]);
differences.Tables[0].Triggers[0].Target.Should().BeSameAs(differences.Tables[0].Target.Triggers[0]);
differences.Tables[0].Triggers[0].Type.Should().Be(SqlObjectDifferenceType.Different);
@@ -827,7 +827,7 @@ public async Task CompareAsync()
differences.Tables[1].Target.Schema.Should().Be("dbo");
differences.Tables[1].Target.Triggers.Should().HaveCount(1);
differences.Tables[1].Target.Triggers[0].Name.Should().Be("TriggerTarget");
- differences.Tables[1].Target.Triggers[0].Code.Should().Be("CREATE TRIGGER [TriggerTarget]\r\n\tON [dbo].[TableTarget]\r\n\tFOR DELETE, INSERT, UPDATE\r\n\tAS\r\n\tBEGIN\r\n\t\tSET NOCOUNT ON\r\n\tEND");
+ differences.Tables[1].Target.Triggers[0].Code.Should().Be($"CREATE TRIGGER [TriggerTarget]{Environment.NewLine}\tON [dbo].[TableTarget]{Environment.NewLine}\tFOR DELETE, INSERT, UPDATE{Environment.NewLine}\tAS{Environment.NewLine}\tBEGIN{Environment.NewLine}\t\tSET NOCOUNT ON{Environment.NewLine}\tEND{Environment.NewLine}");
differences.Tables[1].Target.Triggers[0].IsInsteadOfTrigger.Should().BeFalse();
differences.Tables[1].Target.UniqueConstraints.Should().HaveCount(1);
differences.Tables[1].Target.UniqueConstraints[0].Columns.Should().HaveCount(1);
@@ -1047,7 +1047,7 @@ public async Task CompareAsync()
differences.Tables[4].Source.PrimaryKey.Type.Should().Be("CLUSTERED");
differences.Tables[4].Source.Triggers.Should().HaveCount(1);
differences.Tables[4].Source.Triggers[0].Name.Should().Be("TriggerSource");
- differences.Tables[4].Source.Triggers[0].Code.Should().Be("CREATE TRIGGER [TriggerSource]\r\n\tON [dbo].[TableSource]\r\n\tFOR DELETE, INSERT, UPDATE\r\n\tAS\r\n\tBEGIN\r\n\t\tSET NOCOUNT ON\r\n\tEND");
+ differences.Tables[4].Source.Triggers[0].Code.Should().Be($"CREATE TRIGGER [TriggerSource]{Environment.NewLine}\tON [dbo].[TableSource]{Environment.NewLine}\tFOR DELETE, INSERT, UPDATE{Environment.NewLine}\tAS{Environment.NewLine}\tBEGIN{Environment.NewLine}\t\tSET NOCOUNT ON{Environment.NewLine}\tEND{Environment.NewLine}");
differences.Tables[4].Source.Triggers[0].IsInsteadOfTrigger.Should().BeFalse();
differences.Tables[4].Source.UniqueConstraints.Should().HaveCount(1);
differences.Tables[4].Source.UniqueConstraints[0].Columns.Should().HaveCount(1);
@@ -1092,8 +1092,8 @@ public async Task CompareAsync()
differences.Views[0].Target.Schema.Should().Be("dbo");
differences.Views[0].Properties.Should().HaveCount(1);
differences.Views[0].Properties[0].Name.Should().Be("Code");
- differences.Views[0].Properties[0].Source.Should().Be("CREATE VIEW [dbo].[ViewDifference]\r\n\tAS SELECT * FROM [TableDifference] WHERE [Type] = 10");
- differences.Views[0].Properties[0].Target.Should().Be("CREATE VIEW [dbo].[ViewDifference]\r\n\tAS SELECT * FROM [TableDifference] WHERE [Type] = 'The type'");
+ differences.Views[0].Properties[0].Source.Should().Be($"CREATE VIEW [dbo].[ViewDifference]{Environment.NewLine}\tAS SELECT * FROM [TableDifference] WHERE [Type] = 10{Environment.NewLine}");
+ differences.Views[0].Properties[0].Target.Should().Be($"CREATE VIEW [dbo].[ViewDifference]{Environment.NewLine}\tAS SELECT * FROM [TableDifference] WHERE [Type] = 'The type'{Environment.NewLine}");
differences.Views[1].Source.Should().BeNull();
differences.Views[1].Target.Name.Should().Be("ViewTarget");
diff --git a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs
index 4718aec..5ca9f01 100644
--- a/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs
+++ b/tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseExtensionsTest.cs
@@ -9,7 +9,7 @@ namespace PosInformatique.Testing.Databases.SqlServer.Tests
[Collection("PosInformatique.Testing.Databases.SqlServer.Tests")]
public class SqlServerDatabaseExtensionsTest
{
- private const string ConnectionString = $"Data Source=(localDB)\\posinfo-tests; Initial Catalog={nameof(SqlServerDatabaseExtensionsTest)}; Integrated Security=True";
+ private static readonly string ConnectionString = ConnectionStrings.Get(nameof(SqlServerDatabaseExtensionsTest));
[Fact]
public void InsertInto_EnableIdentity()
@@ -227,7 +227,8 @@ public async Task ExecuteScriptAsync_String()
var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest");
- await database.ExecuteScriptAsync(@"
+ await database.ExecuteScriptAsync(
+ """
CREATE TABLE TableTest
(
Id INT NOT NULL
@@ -242,9 +243,11 @@ INSERT INTO [TableTest] ([Id]) VALUES (0)
UPDATE [TableTest]
SET [Id] = [Id] + 1
- GO 10");
+ GO 10
+ """,
+ TestContext.Current.CancellationToken);
- var table = await database.ExecuteQueryAsync("SELECT * FROM [TableTest]");
+ var table = await database.ExecuteQueryAsync("SELECT * FROM [TableTest]", TestContext.Current.CancellationToken);
table.Rows.Should().HaveCount(1);
@@ -258,7 +261,8 @@ public async Task ExecuteScriptAsync_String_WithEmptyLinesAtTheEnd()
var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest");
- await database.ExecuteScriptAsync(@"
+ await database.ExecuteScriptAsync(
+ """
CREATE TABLE TableTest
(
Id INT NOT NULL
@@ -274,10 +278,10 @@ UPDATE [TableTest]
SET [Id] = [Id] + 1
GO 10
+ """,
+ TestContext.Current.CancellationToken);
- ");
-
- var table = await database.ExecuteQueryAsync("SELECT * FROM [TableTest]");
+ var table = await database.ExecuteQueryAsync("SELECT * FROM [TableTest]", TestContext.Current.CancellationToken);
table.Rows.Should().HaveCount(1);
@@ -291,24 +295,28 @@ public async Task ExecuteScriptAsync_StringReader()
var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest");
- await database.ExecuteScriptAsync(new StringReader(@"
- CREATE TABLE TableTest
- (
- Id INT NOT NULL
- )
+ await database.ExecuteScriptAsync(
+ new StringReader(
+ """
+ CREATE TABLE TableTest
+ (
+ Id INT NOT NULL
+ )
- GO
- GO
+ GO
+ GO
- INSERT INTO [TableTest] ([Id]) VALUES (0)
+ INSERT INTO [TableTest] ([Id]) VALUES (0)
- GO
- UPDATE [TableTest]
- SET [Id] = [Id] + 1
+ GO
+ UPDATE [TableTest]
+ SET [Id] = [Id] + 1
- GO 10"));
+ GO 10
+ """),
+ TestContext.Current.CancellationToken);
- var table = await database.ExecuteQueryAsync("SELECT * FROM [TableTest]");
+ var table = await database.ExecuteQueryAsync("SELECT * FROM [TableTest]", TestContext.Current.CancellationToken);
table.Rows.Should().HaveCount(1);
@@ -322,27 +330,30 @@ public async Task ExecuteScriptAsync_StringReader_WithEmptyLinesAtTheEnd()
var database = server.CreateEmptyDatabase("SqlServerDatabaseExtensionsTest");
- await database.ExecuteScriptAsync(new StringReader(@"
- CREATE TABLE TableTest
- (
- Id INT NOT NULL
- )
+ await database.ExecuteScriptAsync(
+ new StringReader(
+ """
+ CREATE TABLE TableTest
+ (
+ Id INT NOT NULL
+ )
- GO
- GO
+ GO
+ GO
- INSERT INTO [TableTest] ([Id]) VALUES (0)
+ INSERT INTO [TableTest] ([Id]) VALUES (0)
- GO
- UPDATE [TableTest]
- SET [Id] = [Id] + 1
+ GO
+ UPDATE [TableTest]
+ SET [Id] = [Id] + 1
- GO 10
+ GO 10
- "));
+ """),
+ TestContext.Current.CancellationToken);
- var table = await database.ExecuteQueryAsync("SELECT * FROM [TableTest]");
+ var table = await database.ExecuteQueryAsync("SELECT * FROM [TableTest]", TestContext.Current.CancellationToken);
table.Rows.Should().HaveCount(1);
diff --git a/tests/Testing.Databases.SqlServer.Tests/SqlServerTest.cs b/tests/Testing.Databases.SqlServer.Tests/SqlServerTest.cs
index e4824da..c9f344e 100644
--- a/tests/Testing.Databases.SqlServer.Tests/SqlServerTest.cs
+++ b/tests/Testing.Databases.SqlServer.Tests/SqlServerTest.cs
@@ -9,7 +9,7 @@ namespace PosInformatique.Testing.Databases.SqlServer.Tests
[Collection("PosInformatique.Testing.Databases.SqlServer.Tests")]
public class SqlServerTest
{
- private const string ConnectionString = $"Data Source=(localDB)\\posinfo-tests; Initial Catalog={nameof(SqlServerTest)}; Integrated Security=True";
+ private static readonly string ConnectionString = ConnectionStrings.Get(nameof(SqlServerTest));
[Theory]
[InlineData("Data Source=TheServer; Initial Catalog=TheDB; User ID=TheID; Password=ThePassword", "Data Source=TheServer;Initial Catalog=master;User ID=TheID;Password=ThePassword")]
@@ -22,22 +22,134 @@ public void Constructor(string connectionString, string expectedMasterConnection
server.Master.Server.Should().BeSameAs(server);
}
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task CreateAndDelete(bool withCreationSettings)
+ {
+ var server = new SqlServer(ConnectionString);
+
+ SqlDatabaseCreationSettings settings = null;
+
+ if (withCreationSettings)
+ {
+ settings = new SqlDatabaseCreationSettings();
+ }
+
+ var database = server.CreateEmptyDatabase("CreateAndDeleteDB", settings);
+
+ database.ConnectionString.Should().Be(ConnectionStrings.Get("CreateAndDeleteDB"));
+
+ var table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB'", TestContext.Current.CancellationToken);
+ table.Rows.Should().HaveCount(1);
+
+ // Delete the database
+ server.DeleteDatabase("CreateAndDeleteDB");
+
+ table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB'", TestContext.Current.CancellationToken);
+ table.Rows.Should().BeEmpty();
+ }
+
+ [Fact]
+ public async Task CreateAndDelete_WithSpecificDataFileName()
+ {
+ using var otherDataPath = OtherDatabasePath.Create();
+
+ var server = new SqlServer(ConnectionString);
+
+ var settings = new SqlDatabaseCreationSettings()
+ {
+ DataFileName = Path.Combine(otherDataPath.Path, "TheSpecificDataFileName.mdf"),
+ };
+
+ var database = server.CreateEmptyDatabase("CreateAndDeleteDB_WithSpecificDataFileName", settings);
+
+ database.ConnectionString.Should().Be(ConnectionStrings.Get("CreateAndDeleteDB_WithSpecificDataFileName"));
+
+ var table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB_WithSpecificDataFileName'", TestContext.Current.CancellationToken);
+ table.Rows.Should().HaveCount(1);
+
+ // Check the location of the database
+ File.Exists(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName.mdf")).Should().BeTrue();
+ File.Exists(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName_log.ldf")).Should().BeTrue();
+
+ var result = database.ExecuteQuery("SELECT * FROM [sys].[database_files] ORDER BY [physical_name]");
+
+ result.Rows.Should().HaveCount(2);
+
+ result.Rows[0]["name"].Should().Be("CreateAndDeleteDB_WithSpecificDataFileName");
+ result.Rows[0]["physical_name"].Should().Be(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName.mdf"));
+ result.Rows[0]["type_desc"].Should().Be("ROWS");
+
+ result.Rows[1]["name"].Should().Be("CreateAndDeleteDB_WithSpecificDataFileName_log");
+ result.Rows[1]["physical_name"].Should().Be(Path.Combine(otherDataPath.Path, "TheSpecificDataFileName_log.ldf"));
+ result.Rows[1]["type_desc"].Should().Be("LOG");
+
+ // Delete the database
+ server.DeleteDatabase("CreateAndDeleteDB_WithSpecificDataFileName");
+
+ table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB_WithSpecificDataFileName'", TestContext.Current.CancellationToken);
+ table.Rows.Should().BeEmpty();
+ }
+
[Fact]
public async Task CreateAndDeleteAsync()
{
var server = new SqlServer(ConnectionString);
- var database = await server.CreateEmptyDatabaseAsync("CreateAndDeleteDB", CancellationToken.None);
+ var database = await server.CreateEmptyDatabaseAsync("CreateAndDeleteDBAsync", new SqlDatabaseCreationSettings(), CancellationToken.None);
- database.ConnectionString.Should().Be("Data Source=(localDB)\\posinfo-tests;Initial Catalog=CreateAndDeleteDB;Integrated Security=True");
+ database.ConnectionString.Should().Be(ConnectionStrings.Get("CreateAndDeleteDBAsync"));
- var table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB'");
+ var table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDBAsync'", TestContext.Current.CancellationToken);
table.Rows.Should().HaveCount(1);
// Delete the database
- await server.DeleteDatabaseAsync("CreateAndDeleteDB", CancellationToken.None);
+ await server.DeleteDatabaseAsync("CreateAndDeleteDBAsync", CancellationToken.None);
+
+ table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDBAsync'", TestContext.Current.CancellationToken);
+ table.Rows.Should().BeEmpty();
+ }
+
+ [Fact]
+ public async Task CreateAndDeleteAsync_WithSpecificDataFileName()
+ {
+ using var otherDataPath = OtherDatabasePath.Create();
+
+ var server = new SqlServer(ConnectionString);
+
+ var settings = new SqlDatabaseCreationSettings()
+ {
+ DataFileName = Path.Combine(otherDataPath.Path, "TheSpecificDataFileNameAsync.mdf"),
+ };
+
+ var database = await server.CreateEmptyDatabaseAsync("CreateAndDeleteDB_WithSpecificDataFileNameAsync", settings, TestContext.Current.CancellationToken);
+
+ database.ConnectionString.Should().Be(ConnectionStrings.Get("CreateAndDeleteDB_WithSpecificDataFileNameAsync"));
+
+ var table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB_WithSpecificDataFileNameAsync'", TestContext.Current.CancellationToken);
+ table.Rows.Should().HaveCount(1);
+
+ // Check the location of the database
+ File.Exists(Path.Combine(otherDataPath.Path, "TheSpecificDataFileNameAsync.mdf")).Should().BeTrue();
+ File.Exists(Path.Combine(otherDataPath.Path, "TheSpecificDataFileNameAsync_log.ldf")).Should().BeTrue();
+
+ var result = database.ExecuteQuery("SELECT * FROM [sys].[database_files] ORDER BY [physical_name]");
+
+ result.Rows.Should().HaveCount(2);
+
+ result.Rows[0]["name"].Should().Be("CreateAndDeleteDB_WithSpecificDataFileNameAsync");
+ result.Rows[0]["physical_name"].Should().Be(Path.Combine(otherDataPath.Path, "TheSpecificDataFileNameAsync.mdf"));
+ result.Rows[0]["type_desc"].Should().Be("ROWS");
+
+ result.Rows[1]["name"].Should().Be("CreateAndDeleteDB_WithSpecificDataFileNameAsync_log");
+ result.Rows[1]["physical_name"].Should().Be(Path.Combine(otherDataPath.Path, "TheSpecificDataFileNameAsync_log.ldf"));
+ result.Rows[1]["type_desc"].Should().Be("LOG");
+
+ // Delete the database
+ await server.DeleteDatabaseAsync("CreateAndDeleteDB_WithSpecificDataFileNameAsync", TestContext.Current.CancellationToken);
- table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB'");
+ table = await server.Master.ExecuteQueryAsync("SELECT * FROM [sys].[databases] WHERE [name] = 'CreateAndDeleteDB_WithSpecificDataFileNameAsync'", TestContext.Current.CancellationToken);
table.Rows.Should().BeEmpty();
}
}
diff --git a/tests/Testing.Databases.SqlServer.Tests/Testing.Databases.SqlServer.Tests.csproj b/tests/Testing.Databases.SqlServer.Tests/Testing.Databases.SqlServer.Tests.csproj
index 966431e..06277c8 100644
--- a/tests/Testing.Databases.SqlServer.Tests/Testing.Databases.SqlServer.Tests.csproj
+++ b/tests/Testing.Databases.SqlServer.Tests/Testing.Databases.SqlServer.Tests.csproj
@@ -2,6 +2,7 @@
net8.0
+ Exe
@@ -9,16 +10,10 @@
-
+
PreserveNewest
-
-
-
-
- PreserveNewest
-
-
+
PreserveNewest
@@ -27,24 +22,11 @@
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
+
+
-
-
-
-
-
-
+