From 5bf561624168ca52c126ef433a9966394185eb23 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Mon, 15 Apr 2024 07:54:22 +0200 Subject: [PATCH] Bug 432: Allow for non-ASCII characters in scripts This had been working for some databases, but not all. * Added failing tests * Fixed character set for MariaDb * Fixed SQL server, fixed ScriptRunErrors * Removed a lot of unused propertied in ISyntax * Fixed error ScriptRunErrorsTable -> ScripRunErrors in unit tests * Turned of parallelization of test runs in Sqlite tests - had some spurious issues there. And, it's not a valid use case to run multiple migrations at once against new databases (I thinkg) * Cleaned up the Test setup/teardown for Sqlite --- src/grate.core/Infrastructure/ISyntax.cs | 9 - src/grate.core/Migration/AnsiSqlDatabase.cs | 15 +- src/grate.core/Migration/GrateMigrator.cs | 12 +- .../up/02_scripts_run_unicode_support.sql | 4 + .../03_scripts_run_errors_unicode_support.sql | 8 + .../up/03_version_table_unicode_support.sql | 5 + .../Infrastructure/MariaDbSyntax.cs | 9 - .../Infrastructure/OracleSyntax.cs | 10 - src/grate.oracle/Migration/OracleDatabase.cs | 2 +- .../Infrastructure/PostgreSqlSyntax.cs | 9 - .../Infrastructure/SqliteSyntax.cs | 9 - .../up/02_scripts_run_unicode_support.sql | 2 + .../03_scripts_run_errors_unicode_support.sql | 8 + .../Infrastructure/SqlServerSyntax.cs | 9 - .../ServiceCollectionTest.cs | 6 +- .../ScriptsRunErrorsTable.cs | 9 + .../ScriptsRunTable.cs | 9 + .../MariaDbGrateTestContext.cs | 13 +- .../ServiceCollectionTest.cs | 9 +- .../ScriptsRunErrorsTable.cs | 9 + .../ScriptsRunTable.cs | 10 + .../OracleGrateTestContext.cs | 14 +- .../ServiceCollectionTest.cs | 6 +- .../ScriptsRunErrorsTable.cs | 9 + .../ScriptsRunTable.cs | 9 + .../PostgreSqlGrateTestContext.cs | 13 +- .../ServiceCollectionTest.cs | 6 +- .../ScriptsRunErrorsTable.cs | 9 + .../ScriptsRunTable.cs | 9 + .../SqlServerGrateTestContext.cs | 5 +- .../ServiceCollectionTest.cs | 6 +- .../SqlServerGrateTestContext.cs | 19 +- unittests/Sqlite/Database.cs | 4 +- .../ServiceCollectionTest.cs | 4 +- .../ScriptsRunErrorsTable.cs | 9 + .../ScriptsRunTable.cs | 9 + unittests/Sqlite/Startup.cs | 2 + .../SqliteGrateTestContext.cs | 13 +- .../TestInfrastructure/SqliteTestDatabase.cs | 55 +-- .../GrateServiceCollectionTest.cs | 10 +- .../When_Grate_structure_already_exists.cs | 6 +- ...n_Grate_structure_is_not_latest_version.cs | 6 +- .../TestCommon/Generic/GenericDatabase.cs | 2 +- .../ScriptsRunErrorsTable.cs | 328 ++++++++++++++++++ .../ScriptsRunTable.cs | 106 ++++++ .../Failing_Scripts.cs | 63 ++-- .../Versioning_The_Database.cs | 2 +- .../TestInfrastructure/GrateTestContext.cs | 5 +- .../TestInfrastructure/IGrateTestContext.cs | 1 - .../TestInfrastructure/TestConfig.cs | 13 +- .../TestContainerDatabase.cs | 6 +- 51 files changed, 678 insertions(+), 247 deletions(-) create mode 100644 src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/02_scripts_run_unicode_support.sql create mode 100644 src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/03_scripts_run_errors_unicode_support.sql create mode 100644 src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/03_version_table_unicode_support.sql create mode 100644 src/grate.sqlserver/Bootstrapping/Sql/GrateStructure/up/02_scripts_run_unicode_support.sql create mode 100644 src/grate.sqlserver/Bootstrapping/Sql/GrateStructure/up/03_scripts_run_errors_unicode_support.sql create mode 100644 unittests/MariaDB/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs create mode 100644 unittests/MariaDB/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs create mode 100644 unittests/Oracle/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs create mode 100644 unittests/Oracle/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs create mode 100644 unittests/PostgreSQL/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs create mode 100644 unittests/PostgreSQL/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs create mode 100644 unittests/SqlServer/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs create mode 100644 unittests/SqlServer/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs create mode 100644 unittests/Sqlite/Reported_issues/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs create mode 100644 unittests/Sqlite/Reported_issues/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs create mode 100644 unittests/TestCommon/Generic/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs create mode 100644 unittests/TestCommon/Generic/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs diff --git a/src/grate.core/Infrastructure/ISyntax.cs b/src/grate.core/Infrastructure/ISyntax.cs index 9f1d38e4..7343e2d4 100644 --- a/src/grate.core/Infrastructure/ISyntax.cs +++ b/src/grate.core/Infrastructure/ISyntax.cs @@ -8,11 +8,6 @@ public interface ISyntax string StatementSeparatorRegex { get; } string CurrentDatabase { get; } string ListDatabases { get; } - string VarcharType { get; } - string TextType { get; } - string BigintType { get; } - string BooleanType { get; } - string CreateSchema(string schemaName); string CreateDatabase(string databaseName, string? password); /// /// Syntax to drop a database if it exists, and do nothing if not. @@ -23,9 +18,5 @@ public interface ISyntax string TableWithSchema(string schemaName, string tableName); string LimitN(string sql, int n); string ReturnId { get; } - string TimestampType { get; } - string Quote(string text); - string PrimaryKeyColumn(string columnName); - string PrimaryKeyConstraint(string tableName, string column); string ResetIdentity(string schemaName, string tableName, long value); } diff --git a/src/grate.core/Migration/AnsiSqlDatabase.cs b/src/grate.core/Migration/AnsiSqlDatabase.cs index 00b08f48..937148f4 100644 --- a/src/grate.core/Migration/AnsiSqlDatabase.cs +++ b/src/grate.core/Migration/AnsiSqlDatabase.cs @@ -343,7 +343,7 @@ INSERT INTO {VersionTable} newVersion, entryDate = DateTime.UtcNow, modifiedDate = DateTime.UtcNow, - enteredBy = ClaimsPrincipal.Current?.Identity?.Name ?? Environment.UserName, + enteredBy = GetUserName(), status = MigrationStatus.InProgress }); } @@ -365,6 +365,11 @@ INSERT INTO {VersionTable} return versionId; } + protected string GetUserName() + { + return ClaimsPrincipal.Current?.Identity?.Name ?? Environment.UserName; + } + public virtual async Task ChangeVersionStatus(string status, long versionId) { var updateSql = Parameterize($@" @@ -532,7 +537,7 @@ INSERT INTO {ScriptsRunTable} scriptRun.Add(nameof(hash), hash); scriptRun.Add(nameof(runOnce), Bool(runOnce)); scriptRun.Add(Now, DateTime.UtcNow); - scriptRun.Add(User, Environment.UserName); + scriptRun.Add(User, GetUserName()); if (Config!.DeferWritingToRunTables) { @@ -564,7 +569,7 @@ INSERT INTO {ScriptsRunErrorsTable} scriptRunErrors.Add(nameof(errorSql), errorSql, DbType.String); scriptRunErrors.Add(nameof(errorMessage), errorMessage, DbType.String); scriptRunErrors.Add(Now, DateTime.UtcNow); - scriptRunErrors.Add(User, Environment.UserName); + scriptRunErrors.Add(User, GetUserName()); if (Config!.DeferWritingToRunTables) { @@ -655,7 +660,7 @@ public async ValueTask DisposeAsync() if (_deferredWrites.Any()) { await OpenActiveConnection(); - foreach (var deferredWrite in _deferredWrites) + foreach (var deferredWrite in _deferredWrites.ToArray()) { await deferredWrite(ActiveConnection); } @@ -664,7 +669,7 @@ public async ValueTask DisposeAsync() await CloseConnection(); await CloseAdminConnection(); await Close(ActiveConnection); - + GC.SuppressFinalize(this); } diff --git a/src/grate.core/Migration/GrateMigrator.cs b/src/grate.core/Migration/GrateMigrator.cs index 5017716c..5fca449e 100644 --- a/src/grate.core/Migration/GrateMigrator.cs +++ b/src/grate.core/Migration/GrateMigrator.cs @@ -515,6 +515,7 @@ public static string ChangeDropFolder(GrateConfiguration config, string? server, .Append(':') .Append(',') .Append('+') + .Append('/') .ToArray(); private static string RemoveInvalidPathChars(string? path) @@ -557,18 +558,21 @@ private async Task RunInternalMigrations(string internalFolderName) // First, make sure we have created the "internal meta tables" // (GrateScriptsRun, GrateScriptsRunErrors, GrateVersion), which are used to track // changes to the grate internal tables (ScriptsRun, ScriptsRunErrors, Version). - await using (var migrator1 = this.WithConfiguration(await GetBootstrapInternalGrateConfiguration(internalFolderName))) + await using (var migrator1 = + this.WithConfiguration(await GetBootstrapInternalGrateConfiguration(internalFolderName))) { await migrator1.Migrate(); } // Then, make sure we have created the "grate tables" for the database we are migrating. // (ScriptsRun, ScriptsRunErrors, Version). Turtles all the way down! - await using var migrator2 = this.WithConfiguration(await GetInternalGrateConfiguration(internalFolderName)); - await migrator2.Migrate(); + await using (var migrator2 = + this.WithConfiguration(await GetInternalGrateConfiguration(internalFolderName))) + { + await migrator2.Migrate(); + } } - private async Task GetBootstrapInternalGrateConfiguration(string internalFolderName) => await GetInternalGrateConfiguration(internalFolderName, "grate-internal") with { diff --git a/src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/02_scripts_run_unicode_support.sql b/src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/02_scripts_run_unicode_support.sql new file mode 100644 index 00000000..d38eb32f --- /dev/null +++ b/src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/02_scripts_run_unicode_support.sql @@ -0,0 +1,4 @@ +ALTER TABLE {{SchemaName}}_{{ScriptsRunTable}} + MODIFY script_name varchar(255) CHARACTER SET utf8mb4 NULL, + MODIFY text_of_script text CHARACTER SET utf8mb4 NULL, + MODIFY entered_by varchar(50) CHARACTER SET utf8mb4 NULL \ No newline at end of file diff --git a/src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/03_scripts_run_errors_unicode_support.sql b/src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/03_scripts_run_errors_unicode_support.sql new file mode 100644 index 00000000..32314b13 --- /dev/null +++ b/src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/03_scripts_run_errors_unicode_support.sql @@ -0,0 +1,8 @@ +ALTER TABLE {{SchemaName}}_{{ScriptsRunErrorsTable}} + MODIFY repository_path varchar(255) CHARACTER SET utf8mb4 NULL, + MODIFY version varchar(50) CHARACTER SET utf8mb4 NULL, + MODIFY script_name varchar(255) CHARACTER SET utf8mb4 NULL, + MODIFY text_of_script text CHARACTER SET utf8mb4 NULL, + MODIFY erroneous_part_of_script text CHARACTER SET utf8mb4 NULL, + MODIFY error_message text CHARACTER SET utf8mb4 NULL, + MODIFY entered_by varchar(50) CHARACTER SET utf8mb4 NULL \ No newline at end of file diff --git a/src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/03_version_table_unicode_support.sql b/src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/03_version_table_unicode_support.sql new file mode 100644 index 00000000..8e118979 --- /dev/null +++ b/src/grate.mariadb/Bootstrapping/Sql/GrateStructure/up/03_version_table_unicode_support.sql @@ -0,0 +1,5 @@ +ALTER TABLE {{SchemaName}}_{{VersionTable}} + MODIFY repository_path varchar(255) CHARACTER SET utf8mb4 NULL, + MODIFY version varchar(50) CHARACTER SET utf8mb4 NULL, + MODIFY entered_by varchar(50) CHARACTER SET utf8mb4 NULL, + MODIFY status varchar(50) CHARACTER SET utf8mb4 NULL \ No newline at end of file diff --git a/src/grate.mariadb/Infrastructure/MariaDbSyntax.cs b/src/grate.mariadb/Infrastructure/MariaDbSyntax.cs index 20a1a3a1..5909ecb7 100644 --- a/src/grate.mariadb/Infrastructure/MariaDbSyntax.cs +++ b/src/grate.mariadb/Infrastructure/MariaDbSyntax.cs @@ -17,19 +17,10 @@ public string StatementSeparatorRegex public string CurrentDatabase => "SELECT DATABASE()"; public string ListDatabases => "SHOW DATABASES"; - public string VarcharType => "varchar"; - public string TextType => "text"; - public string BigintType => "BIGINT"; - public string BooleanType => "boolean"; - public string PrimaryKeyColumn(string columnName) => $"{columnName} bigint NOT NULL AUTO_INCREMENT"; - public string CreateSchema(string schemaName) => @$"CREATE SCHEMA {schemaName}"; public string CreateDatabase(string databaseName, string? _) => @$"CREATE DATABASE {databaseName}"; public string DropDatabase(string databaseName) => @$"DROP DATABASE IF EXISTS `{databaseName}`;"; public string TableWithSchema(string schemaName, string tableName) => $"{schemaName}_{tableName}"; public string ReturnId => ";SELECT LAST_INSERT_ID();"; - public string TimestampType => "timestamp"; - public string Quote(string text) => $"`{text}`"; - public string PrimaryKeyConstraint(string tableName, string column) => $",\nCONSTRAINT PK_{tableName}_{column} PRIMARY KEY ({column})"; public string LimitN(string sql, int n) => sql + "\nLIMIT 1"; // any idea to reset identity without using value is welcome. diff --git a/src/grate.oracle/Infrastructure/OracleSyntax.cs b/src/grate.oracle/Infrastructure/OracleSyntax.cs index 93c2f53c..99c5cbd9 100644 --- a/src/grate.oracle/Infrastructure/OracleSyntax.cs +++ b/src/grate.oracle/Infrastructure/OracleSyntax.cs @@ -18,13 +18,6 @@ public string StatementSeparatorRegex public string CurrentDatabase => "select user from dual"; public string ListDatabases => "SELECT * FROM all_users"; - public string VarcharType => "VARCHAR2"; - public string TextType => "CLOB"; - public string BigintType => "NUMBER(19)"; - public string BooleanType => "CHAR(1)"; - public string PrimaryKeyColumn(string columnName) => $"{columnName} NUMBER(19) GENERATED ALWAYS AS IDENTITY NOT NULL PRIMARY KEY "; - - public string CreateSchema(string schemaName) => throw new NotImplementedException("Create schema is not implemented for Oracle DB"); public string CreateDatabase(string userName, string? password) => $@" begin @@ -48,9 +41,6 @@ FOR ln_cur IN (SELECT sid, serial# FROM v$session WHERE username = usr) "; public string TableWithSchema(string schemaName, string tableName) => $"{schemaName}_{tableName}"; public string ReturnId => "RETURNING id;"; - public string TimestampType => "timestamp"; - public string Quote(string text) => $"\"{text}\""; - public string PrimaryKeyConstraint(string tableName, string column) => ""; public string LimitN(string sql, int n) => sql + $"\nLIMIT {n}"; public string ResetIdentity(string schemaName, string tableName, long _) => $"ALTER TABLE {TableWithSchema(schemaName, tableName)} MODIFY id NUMBER(19) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH LIMIT VALUE)"; } diff --git a/src/grate.oracle/Migration/OracleDatabase.cs b/src/grate.oracle/Migration/OracleDatabase.cs index 1db6422a..cb046190 100644 --- a/src/grate.oracle/Migration/OracleDatabase.cs +++ b/src/grate.oracle/Migration/OracleDatabase.cs @@ -109,7 +109,7 @@ INSERT INTO {VersionTable} newVersion, entryDate = DateTime.UtcNow, modifiedDate = DateTime.UtcNow, - enteredBy = ClaimsPrincipal.Current?.Identity?.Name ?? Environment.UserName, + enteredBy = GetUserName(), status = MigrationStatus.InProgress }; var dynParams = new DynamicParameters(parameters); diff --git a/src/grate.postgresql/Infrastructure/PostgreSqlSyntax.cs b/src/grate.postgresql/Infrastructure/PostgreSqlSyntax.cs index 0c2ae5b3..78c8ad73 100644 --- a/src/grate.postgresql/Infrastructure/PostgreSqlSyntax.cs +++ b/src/grate.postgresql/Infrastructure/PostgreSqlSyntax.cs @@ -19,21 +19,12 @@ public string StatementSeparatorRegex public string CurrentDatabase => "SELECT current_database()"; public string ListDatabases => "SELECT datname FROM pg_database"; - public string VarcharType => "varchar"; - public string TextType => "text"; - public string BigintType => "BIGINT"; - public string BooleanType => "boolean"; - public string PrimaryKeyColumn(string columnName) => $"{columnName} bigint GENERATED ALWAYS AS IDENTITY NOT NULL"; - public string CreateSchema(string schemaName) => @$"CREATE SCHEMA ""{schemaName}"";"; public string CreateDatabase(string databaseName, string? _) => @$"CREATE DATABASE ""{databaseName}"""; public string DropDatabase(string databaseName) => @$"select pg_terminate_backend(pid) from pg_stat_activity where datname='{databaseName}'; COMMIT; DROP DATABASE IF EXISTS ""{databaseName}"";"; public string TableWithSchema(string schemaName, string tableName) => $"\"{schemaName}\".\"{tableName}\""; public string ReturnId => "RETURNING id;"; - public string TimestampType => "timestamp"; - public string Quote(string text) => $"\"{text}\""; - public string PrimaryKeyConstraint(string tableName, string column) => $",\nCONSTRAINT PK_{tableName}_{column} PRIMARY KEY ({column})"; public string LimitN(string sql, int n) => sql + $"\nLIMIT {n}"; public string ResetIdentity(string schemaName, string tableName, long _) => @$"SELECT setval(pg_get_serial_sequence('{TableWithSchema(schemaName, tableName)}', 'id'), coalesce(MAX(id), 1)) from {TableWithSchema(schemaName, tableName)};"; } diff --git a/src/grate.sqlite/Infrastructure/SqliteSyntax.cs b/src/grate.sqlite/Infrastructure/SqliteSyntax.cs index ecb13675..9b80b95e 100644 --- a/src/grate.sqlite/Infrastructure/SqliteSyntax.cs +++ b/src/grate.sqlite/Infrastructure/SqliteSyntax.cs @@ -17,12 +17,6 @@ public string StatementSeparatorRegex public string CurrentDatabase => "SELECT name FROM pragma_database_list ORDER BY seq DESC LIMIT 1"; public string ListDatabases => "select name from pragma_database_list"; - public string VarcharType => "nvarchar"; - public string TextType => "ntext"; - public string BigintType => "BIGINT"; - public string BooleanType => "bit"; - public string PrimaryKeyColumn(string columnName) => $"{columnName} INTEGER PRIMARY KEY AUTOINCREMENT"; - public string CreateSchema(string schemaName) => @$"CREATE SCHEMA ""{schemaName}"";"; // The "Create database" is a no-op with Sqlite, so we just provide a dummy SQL that just selects current DB public string CreateDatabase(string databaseName, string? _) => CurrentDatabase; @@ -32,9 +26,6 @@ public string StatementSeparatorRegex public string TableWithSchema(string schemaName, string tableName) => $"{schemaName}_{tableName}"; public string ReturnId => "returning id;"; - public string TimestampType => "datetime"; - public string Quote(string text) => $"\"{text}\""; - public string PrimaryKeyConstraint(string tableName, string column) => ""; public string LimitN(string sql, int n) => sql + $"\nLIMIT {n}"; public string ResetIdentity(string schemaName, string tableName, long _) => @$"UPDATE `sqlite_sequence` SET `seq` = (SELECT MAX(`id`) FROM '{TableWithSchema(schemaName, tableName)}') diff --git a/src/grate.sqlserver/Bootstrapping/Sql/GrateStructure/up/02_scripts_run_unicode_support.sql b/src/grate.sqlserver/Bootstrapping/Sql/GrateStructure/up/02_scripts_run_unicode_support.sql new file mode 100644 index 00000000..f0017ef8 --- /dev/null +++ b/src/grate.sqlserver/Bootstrapping/Sql/GrateStructure/up/02_scripts_run_unicode_support.sql @@ -0,0 +1,2 @@ +ALTER TABLE {{SchemaName}}.{{ScriptsRunTable}} + ALTER COLUMN text_of_script NVARCHAR(MAX) NULL \ No newline at end of file diff --git a/src/grate.sqlserver/Bootstrapping/Sql/GrateStructure/up/03_scripts_run_errors_unicode_support.sql b/src/grate.sqlserver/Bootstrapping/Sql/GrateStructure/up/03_scripts_run_errors_unicode_support.sql new file mode 100644 index 00000000..de457d13 --- /dev/null +++ b/src/grate.sqlserver/Bootstrapping/Sql/GrateStructure/up/03_scripts_run_errors_unicode_support.sql @@ -0,0 +1,8 @@ +ALTER TABLE {{SchemaName}}.{{ScriptsRunErrorsTable}} + ALTER COLUMN text_of_script NVARCHAR(MAX) NULL + +ALTER TABLE {{SchemaName}}.{{ScriptsRunErrorsTable}} + ALTER COLUMN erroneous_part_of_script NVARCHAR(MAX) NULL + +ALTER TABLE {{SchemaName}}.{{ScriptsRunErrorsTable}} + ALTER COLUMN error_message NVARCHAR(MAX) NULL diff --git a/src/grate.sqlserver/Infrastructure/SqlServerSyntax.cs b/src/grate.sqlserver/Infrastructure/SqlServerSyntax.cs index 6bc6da35..a8ec4a5f 100644 --- a/src/grate.sqlserver/Infrastructure/SqlServerSyntax.cs +++ b/src/grate.sqlserver/Infrastructure/SqlServerSyntax.cs @@ -17,12 +17,6 @@ public string StatementSeparatorRegex public string CurrentDatabase => "SELECT DB_NAME()"; public string ListDatabases => "SELECT name FROM sys.databases"; - public string VarcharType => "nvarchar"; - public string TextType => "ntext"; - public string BigintType => "BIGINT"; - public string BooleanType => "bit"; - public string PrimaryKeyColumn(string columnName) => $"{columnName} bigint IDENTITY(1,1) NOT NULL"; - public string CreateSchema(string schemaName) => @$"CREATE SCHEMA ""{schemaName}"";"; public string CreateDatabase(string databaseName, string? _) => @$"CREATE DATABASE ""{databaseName}"""; public string DropDatabase(string databaseName) => @$"USE master; IF EXISTS(SELECT * FROM sysdatabases WHERE [name] = '{databaseName}') @@ -32,9 +26,6 @@ DROP DATABASE [{databaseName}] END"; public string TableWithSchema(string schemaName, string tableName) => $"{schemaName}.[{tableName}]"; public string ReturnId => ";SELECT @@IDENTITY"; - public string TimestampType => "datetime"; - public string Quote(string text) => $"\"{text}\""; - public string PrimaryKeyConstraint(string tableName, string column) => $",\nCONSTRAINT PK_{tableName}_{column} PRIMARY KEY CLUSTERED ({column})"; public string LimitN(string sql, int n) => $"TOP {n}\n" + sql; public string ResetIdentity(string schemaName, string tableName, long _) => @$"DECLARE @max INT SELECT @max=ISNULL(max([id]),0) from [{schemaName}].[{tableName}]; DBCC CHECKIDENT ('[{schemaName}].[{tableName}]', RESEED, @max );"; } diff --git a/unittests/MariaDB/DependencyInjection/ServiceCollectionTest.cs b/unittests/MariaDB/DependencyInjection/ServiceCollectionTest.cs index 1aea9546..05d2afeb 100644 --- a/unittests/MariaDB/DependencyInjection/ServiceCollectionTest.cs +++ b/unittests/MariaDB/DependencyInjection/ServiceCollectionTest.cs @@ -5,4 +5,8 @@ namespace MariaDB.DependencyInjection; [Collection(nameof(MariaDbGrateTestContext))] // ReSharper disable once UnusedType.Global public class ServiceCollectionTest(MariaDbGrateTestContext context) - : TestCommon.DependencyInjection.GrateServiceCollectionTest(context); + : TestCommon.DependencyInjection.GrateServiceCollectionTest(context) +{ + protected override string VarcharType => "varchar"; + protected override string BigintType => "BIGINT"; +} diff --git a/unittests/MariaDB/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs b/unittests/MariaDB/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs new file mode 100644 index 00000000..948a16d4 --- /dev/null +++ b/unittests/MariaDB/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs @@ -0,0 +1,9 @@ +using MariaDB.TestInfrastructure; + +namespace MariaDB.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(MariaDbGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunErrorsTable(MariaDbGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunErrorsTable(testContext, testOutput); + diff --git a/unittests/MariaDB/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs b/unittests/MariaDB/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs new file mode 100644 index 00000000..dd327001 --- /dev/null +++ b/unittests/MariaDB/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs @@ -0,0 +1,9 @@ +using MariaDB.TestInfrastructure; + +namespace MariaDB.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(MariaDbGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunTable(MariaDbGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunTable(testContext, testOutput); + diff --git a/unittests/MariaDB/TestInfrastructure/MariaDbGrateTestContext.cs b/unittests/MariaDB/TestInfrastructure/MariaDbGrateTestContext.cs index 739b98a8..b14cde7a 100644 --- a/unittests/MariaDB/TestInfrastructure/MariaDbGrateTestContext.cs +++ b/unittests/MariaDB/TestInfrastructure/MariaDbGrateTestContext.cs @@ -11,17 +11,10 @@ namespace MariaDB.TestInfrastructure; [CollectionDefinition(nameof(MariaDbGrateTestContext))] public class MariaDbTestCollection : ICollectionFixture; -public class MariaDbGrateTestContext : GrateTestContext +public class MariaDbGrateTestContext( + IGrateMigrator migrator, + ITestDatabase testDatabase) : GrateTestContext(migrator, testDatabase) { - public MariaDbGrateTestContext( - IGrateMigrator migrator, - ITestDatabase testDatabase) : base(testDatabase) - { - Migrator = migrator; - } - - public override IGrateMigrator Migrator { get; } - public override IDbConnection GetDbConnection(string connectionString) => new MySqlConnection(connectionString); public override ISyntax Syntax { get; } = new MariaDbSyntax(); diff --git a/unittests/Oracle/DependencyInjection/ServiceCollectionTest.cs b/unittests/Oracle/DependencyInjection/ServiceCollectionTest.cs index 961cc4b4..8b04c338 100644 --- a/unittests/Oracle/DependencyInjection/ServiceCollectionTest.cs +++ b/unittests/Oracle/DependencyInjection/ServiceCollectionTest.cs @@ -1,8 +1,5 @@ -using grate.Infrastructure; -using grate.Oracle.Migration; -using Oracle.TestInfrastructure; +using Oracle.TestInfrastructure; using TestCommon.DependencyInjection; -using TestCommon.TestInfrastructure; namespace Oracle.DependencyInjection; @@ -10,6 +7,6 @@ namespace Oracle.DependencyInjection; public class ServiceCollectionTest(OracleGrateTestContext testContext) : GrateServiceCollectionTest(testContext) { - protected virtual Type DatabaseType => typeof(OracleDatabase); - protected virtual ISyntax Syntax => OracleDatabase.Syntax; + protected override string VarcharType => "VARCHAR2"; + protected override string BigintType => "NUMBER(19)"; } diff --git a/unittests/Oracle/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs b/unittests/Oracle/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs new file mode 100644 index 00000000..60f69de5 --- /dev/null +++ b/unittests/Oracle/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs @@ -0,0 +1,9 @@ +using Oracle.TestInfrastructure; + +namespace Oracle.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(OracleGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunErrorsTable(OracleGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunErrorsTable(testContext, testOutput); + diff --git a/unittests/Oracle/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs b/unittests/Oracle/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs new file mode 100644 index 00000000..b53a30de --- /dev/null +++ b/unittests/Oracle/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs @@ -0,0 +1,10 @@ + +using Oracle.TestInfrastructure; + +namespace Oracle.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(OracleGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunTable(OracleGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunTable(testContext, testOutput); + diff --git a/unittests/Oracle/TestInfrastructure/OracleGrateTestContext.cs b/unittests/Oracle/TestInfrastructure/OracleGrateTestContext.cs index 0be8a750..bbfbceb1 100644 --- a/unittests/Oracle/TestInfrastructure/OracleGrateTestContext.cs +++ b/unittests/Oracle/TestInfrastructure/OracleGrateTestContext.cs @@ -13,17 +13,10 @@ namespace Oracle.TestInfrastructure; [CollectionDefinition(nameof(OracleGrateTestContext))] public class OracleTestCollection : ICollectionFixture; -public class OracleGrateTestContext : GrateTestContext +public class OracleGrateTestContext( + IGrateMigrator migrator, + ITestDatabase testDatabase) : GrateTestContext(migrator, testDatabase) { - public OracleGrateTestContext( - IGrateMigrator grateMigrator, - ITestDatabase testDatabase) : base(testDatabase) - { - Migrator = grateMigrator; - } - - public override IGrateMigrator Migrator { get; } - // public string DockerCommand(string serverName, string adminPassword) => // $"run -d --name {serverName} -p 1521 -e ORACLE_ENABLE_XDB=true -e ORACLE_PWD={adminPassword} -P container-registry.oracle.com/database/express:21.3.0-xe"; @@ -44,4 +37,5 @@ public OracleGrateTestContext( public override string ExpectedVersionPrefix => "Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production"; public override bool SupportsCreateDatabase => true; public override bool SupportsSchemas => false; + } diff --git a/unittests/PostgreSQL/DependencyInjection/ServiceCollectionTest.cs b/unittests/PostgreSQL/DependencyInjection/ServiceCollectionTest.cs index e2cc7b3a..47f3a166 100644 --- a/unittests/PostgreSQL/DependencyInjection/ServiceCollectionTest.cs +++ b/unittests/PostgreSQL/DependencyInjection/ServiceCollectionTest.cs @@ -5,4 +5,8 @@ namespace PostgreSQL.DependencyInjection; [Collection(nameof(PostgreSqlGrateTestContext))] public class ServiceCollectionTest(PostgreSqlGrateTestContext context) - : TestCommon.DependencyInjection.GrateServiceCollectionTest(context); + : TestCommon.DependencyInjection.GrateServiceCollectionTest(context) +{ + protected override string VarcharType => "varchar"; + protected override string BigintType => "BIGINT"; +} diff --git a/unittests/PostgreSQL/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs b/unittests/PostgreSQL/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs new file mode 100644 index 00000000..a055037f --- /dev/null +++ b/unittests/PostgreSQL/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs @@ -0,0 +1,9 @@ +using PostgreSQL.TestInfrastructure; + +namespace PostgreSQL.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(PostgreSqlGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunErrorsTable(PostgreSqlGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunErrorsTable(testContext, testOutput); + diff --git a/unittests/PostgreSQL/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs b/unittests/PostgreSQL/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs new file mode 100644 index 00000000..c4a16ffd --- /dev/null +++ b/unittests/PostgreSQL/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs @@ -0,0 +1,9 @@ +using PostgreSQL.TestInfrastructure; + +namespace PostgreSQL.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(PostgreSqlGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunTable(PostgreSqlGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunTable(testContext, testOutput); + diff --git a/unittests/PostgreSQL/TestInfrastructure/PostgreSqlGrateTestContext.cs b/unittests/PostgreSQL/TestInfrastructure/PostgreSqlGrateTestContext.cs index 6fb48c75..5d64d421 100644 --- a/unittests/PostgreSQL/TestInfrastructure/PostgreSqlGrateTestContext.cs +++ b/unittests/PostgreSQL/TestInfrastructure/PostgreSqlGrateTestContext.cs @@ -12,17 +12,10 @@ namespace PostgreSQL.TestInfrastructure; [CollectionDefinition(nameof(PostgreSqlGrateTestContext))] public class PostgresqlTestCollection : ICollectionFixture; -public class PostgreSqlGrateTestContext : GrateTestContext +public class PostgreSqlGrateTestContext( + IGrateMigrator migrator, + ITestDatabase testDatabase) : GrateTestContext(migrator, testDatabase) { - public PostgreSqlGrateTestContext( - IGrateMigrator migrator, - ITestDatabase testDatabase) : base(testDatabase) - { - Migrator = migrator; - } - - public override IGrateMigrator Migrator { get; } - public override IDbConnection GetDbConnection(string connectionString) => new NpgsqlConnection(connectionString); public override ISyntax Syntax => new PostgreSqlSyntax(); diff --git a/unittests/SqlServer/DependencyInjection/ServiceCollectionTest.cs b/unittests/SqlServer/DependencyInjection/ServiceCollectionTest.cs index 69b4a05c..710a125f 100644 --- a/unittests/SqlServer/DependencyInjection/ServiceCollectionTest.cs +++ b/unittests/SqlServer/DependencyInjection/ServiceCollectionTest.cs @@ -5,4 +5,8 @@ namespace SqlServer.DependencyInjection; [Collection(nameof(SqlServerGrateTestContext))] public class ServiceCollectionTest(SqlServerGrateTestContext context) - : TestCommon.DependencyInjection.GrateServiceCollectionTest(context); + : TestCommon.DependencyInjection.GrateServiceCollectionTest(context) +{ + protected override string VarcharType => "nvarchar"; + protected override string BigintType => "BIGINT"; +} diff --git a/unittests/SqlServer/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs b/unittests/SqlServer/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs new file mode 100644 index 00000000..f3a3518b --- /dev/null +++ b/unittests/SqlServer/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs @@ -0,0 +1,9 @@ +using SqlServer.TestInfrastructure; + +namespace SqlServer.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(SqlServerGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunErrorsTable(SqlServerGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunErrorsTable(testContext, testOutput); + diff --git a/unittests/SqlServer/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs b/unittests/SqlServer/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs new file mode 100644 index 00000000..014d017d --- /dev/null +++ b/unittests/SqlServer/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs @@ -0,0 +1,9 @@ +using SqlServer.TestInfrastructure; + +namespace SqlServer.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(SqlServerGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunTable(SqlServerGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunTable(testContext, testOutput); + diff --git a/unittests/SqlServer/TestInfrastructure/SqlServerGrateTestContext.cs b/unittests/SqlServer/TestInfrastructure/SqlServerGrateTestContext.cs index db6b9889..1bd30b82 100644 --- a/unittests/SqlServer/TestInfrastructure/SqlServerGrateTestContext.cs +++ b/unittests/SqlServer/TestInfrastructure/SqlServerGrateTestContext.cs @@ -13,14 +13,11 @@ public class SqlServerTestCollection : ICollectionFixture "nvarchar"; + protected override string BigintType => "BIGINT"; +} diff --git a/unittests/SqlServerCaseSensitive/TestInfrastructure/SqlServerGrateTestContext.cs b/unittests/SqlServerCaseSensitive/TestInfrastructure/SqlServerGrateTestContext.cs index 8b2fc84f..d57f6f48 100644 --- a/unittests/SqlServerCaseSensitive/TestInfrastructure/SqlServerGrateTestContext.cs +++ b/unittests/SqlServerCaseSensitive/TestInfrastructure/SqlServerGrateTestContext.cs @@ -12,19 +12,12 @@ namespace SqlServerCaseSensitive.TestInfrastructure; [CollectionDefinition(nameof(SqlServerGrateTestContext))] public class SqlServerTestCollection : ICollectionFixture; -public class SqlServerGrateTestContext : GrateTestContext +public class SqlServerGrateTestContext( + IGrateMigrator migrator, + string serverCollation, + ITestDatabase testDatabase) + : GrateTestContext(migrator, testDatabase) { - public override IGrateMigrator Migrator { get; } - - public SqlServerGrateTestContext( - IGrateMigrator migrator, - string serverCollation, - ITestDatabase testDatabase) : base(testDatabase) - { - Migrator = migrator; - ServerCollation = serverCollation; - } - // ReSharper disable once UnusedMember.Global public SqlServerGrateTestContext( IGrateMigrator migrator, @@ -62,5 +55,5 @@ public SqlServerGrateTestContext( public override bool SupportsCreateDatabase => true; public override bool SupportsSchemas => true; - public string ServerCollation { get; } + public string ServerCollation { get; } = serverCollation; } diff --git a/unittests/Sqlite/Database.cs b/unittests/Sqlite/Database.cs index 63653cbe..d2092dbb 100644 --- a/unittests/Sqlite/Database.cs +++ b/unittests/Sqlite/Database.cs @@ -30,7 +30,9 @@ protected override async Task CreateDatabaseFromConnectionString(string db, stri protected override async Task> GetDatabases() { - var dbFiles = Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.db"); + var builder = new SqliteConnectionStringBuilder(this.Context.AdminConnectionString); + var root = Path.GetDirectoryName(builder.DataSource) ?? Directory.CreateTempSubdirectory().ToString() ; + var dbFiles = Directory.EnumerateFiles(root, "*.db"); IEnumerable dbNames = dbFiles .Select(Path.GetFileNameWithoutExtension) .Where(name => name is not null) diff --git a/unittests/Sqlite/DependencyInjection/ServiceCollectionTest.cs b/unittests/Sqlite/DependencyInjection/ServiceCollectionTest.cs index acff87ff..fb126313 100644 --- a/unittests/Sqlite/DependencyInjection/ServiceCollectionTest.cs +++ b/unittests/Sqlite/DependencyInjection/ServiceCollectionTest.cs @@ -6,6 +6,6 @@ namespace Sqlite.DependencyInjection; public class ServiceCollectionTest(SqliteGrateTestContext context) : TestCommon.DependencyInjection.GrateServiceCollectionTest(context) { - protected virtual Type DatabaseType => typeof(SqliteDatabase); - protected virtual ISyntax Syntax => SqliteDatabase.Syntax; + protected override string VarcharType => "nvarchar"; + protected override string BigintType => "BIGINT"; } diff --git a/unittests/Sqlite/Reported_issues/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs b/unittests/Sqlite/Reported_issues/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs new file mode 100644 index 00000000..7fc88cd2 --- /dev/null +++ b/unittests/Sqlite/Reported_issues/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs @@ -0,0 +1,9 @@ +using Sqlite.TestInfrastructure; + +namespace Sqlite.Reported_issues.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(SqliteGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunErrorsTable(SqliteGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunErrorsTable(testContext, testOutput); + diff --git a/unittests/Sqlite/Reported_issues/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs b/unittests/Sqlite/Reported_issues/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs new file mode 100644 index 00000000..55af90b6 --- /dev/null +++ b/unittests/Sqlite/Reported_issues/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs @@ -0,0 +1,9 @@ +using Sqlite.TestInfrastructure; + +namespace Sqlite.Reported_issues.Reported_issues.Non_ascii_characters_in_script; + +[Collection(nameof(SqliteGrateTestContext))] +// ReSharper disable once InconsistentNaming +public class ScriptsRunTable(SqliteGrateTestContext testContext, ITestOutputHelper testOutput) + : TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script.ScriptsRunTable(testContext, testOutput); + diff --git a/unittests/Sqlite/Startup.cs b/unittests/Sqlite/Startup.cs index c35676f5..af408b11 100644 --- a/unittests/Sqlite/Startup.cs +++ b/unittests/Sqlite/Startup.cs @@ -5,6 +5,8 @@ using Sqlite.TestInfrastructure; using TestCommon.TestInfrastructure; +[assembly: CollectionBehavior(DisableTestParallelization = true)] + namespace Sqlite; // ReSharper disable once UnusedType.Global diff --git a/unittests/Sqlite/TestInfrastructure/SqliteGrateTestContext.cs b/unittests/Sqlite/TestInfrastructure/SqliteGrateTestContext.cs index 78b5b986..12d21a40 100644 --- a/unittests/Sqlite/TestInfrastructure/SqliteGrateTestContext.cs +++ b/unittests/Sqlite/TestInfrastructure/SqliteGrateTestContext.cs @@ -13,15 +13,10 @@ namespace Sqlite.TestInfrastructure; [CollectionDefinition(nameof(SqliteGrateTestContext))] public class SqliteTestCollection : ICollectionFixture; -public class SqliteGrateTestContext : GrateTestContext +public class SqliteGrateTestContext( + IGrateMigrator migrator, + ITestDatabase testDatabase) : GrateTestContext(migrator, testDatabase) { - public SqliteGrateTestContext( - IGrateMigrator migrator, - ITestDatabase testDatabase) : base(testDatabase) - { - Migrator = migrator; - } - public override IDbConnection GetDbConnection(string connectionString) => new SqliteConnection(connectionString); public override ISyntax Syntax => new SqliteSyntax(); @@ -39,6 +34,4 @@ public SqliteGrateTestContext( public override string ExpectedVersionPrefix => throw new NotSupportedException("Sqlite does not support versioning"); public override bool SupportsCreateDatabase => false; public override bool SupportsSchemas => false; - - public override IGrateMigrator Migrator { get; } } diff --git a/unittests/Sqlite/TestInfrastructure/SqliteTestDatabase.cs b/unittests/Sqlite/TestInfrastructure/SqliteTestDatabase.cs index e6f93397..d6885e9c 100644 --- a/unittests/Sqlite/TestInfrastructure/SqliteTestDatabase.cs +++ b/unittests/Sqlite/TestInfrastructure/SqliteTestDatabase.cs @@ -1,23 +1,17 @@ - +using Xunit.Sdk; -using Microsoft.Data.Sqlite; -using Xunit.Sdk; namespace TestCommon.TestInfrastructure; -public class SqliteTestDatabase(IMessageSink messageSink) : ITestDatabase, IAsyncLifetime +// ReSharper disable once ClassNeverInstantiated.Global +public class SqliteTestDatabase : ITestDatabase, IAsyncLifetime { + private readonly string _root = Directory.CreateTempSubdirectory("grate-sqlite-tests-").ToString(); + public Task DisposeAsync() { - SqliteConnection.ClearAllPools(); - - var currentDirectory = Directory.GetCurrentDirectory(); - var dbFiles = Directory.GetFiles(currentDirectory, "*.db"); - var message = new DiagnosticMessage("After tests. Deleting DB files."); - messageSink.OnMessage(message); + var dbFiles = Directory.GetFiles(_root, "*.db"); foreach (var dbFile in dbFiles) { - var deleteMessage = new DiagnosticMessage("File: {0}", dbFile); - messageSink.OnMessage(deleteMessage); File.Delete(dbFile); } return Task.CompletedTask; @@ -25,38 +19,15 @@ public Task DisposeAsync() public Task InitializeAsync() { - var currentDirectory = Directory.GetCurrentDirectory(); - var dbFiles = Directory.GetFiles(currentDirectory, "*.db"); - var message = new DiagnosticMessage("Before tests. Deleting old DB files."); - messageSink.OnMessage(message); - foreach (var dbFile in dbFiles) - { - TryDeletingFile(dbFile); - } return Task.CompletedTask; } - private void TryDeletingFile(string dbFile) + + public string AdminConnectionString => $"Data Source={Wrap("grate-sqlite.db")}"; + public string ConnectionString(string database) => $"Data Source={Wrap(database + ".db")}"; + public string UserConnectionString(string database) => $"Data Source={Wrap(database + ".db")}"; + + private string Wrap(string database) { - var i = 0; - var sleepTime = 300; - const int maxTries = 5; - while (i++ < maxTries) - { - try - { - var message = new DiagnosticMessage("File: {0}", dbFile); - messageSink.OnMessage(message); - File.Delete(dbFile); - return; - } - catch (IOException) when (i <= maxTries) - { - Thread.Sleep(sleepTime); - } - } + return Path.Combine(_root, database); } - - public string AdminConnectionString => $"Data Source=grate-sqlite.db"; - public string ConnectionString(string database) => $"Data Source={database}.db"; - public string UserConnectionString(string database) => $"Data Source={database}.db"; } diff --git a/unittests/TestCommon/DependencyInjection/GrateServiceCollectionTest.cs b/unittests/TestCommon/DependencyInjection/GrateServiceCollectionTest.cs index 873f67a0..6275ff3f 100644 --- a/unittests/TestCommon/DependencyInjection/GrateServiceCollectionTest.cs +++ b/unittests/TestCommon/DependencyInjection/GrateServiceCollectionTest.cs @@ -17,6 +17,9 @@ public abstract class GrateServiceCollectionTest(IGrateTestContext context) { protected IGrateTestContext Context { get; } = context; + protected abstract string BigintType { get; } + protected abstract string VarcharType { get; } + private void ConfigureService(GrateConfigurationBuilder grateConfigurationBuilder) { var connectionString = Context.ConnectionString(TestConfig.RandomDatabase()); @@ -126,7 +129,8 @@ protected void ValidateService(IServiceCollection serviceCollection, Type servic public void Should_throw_invalid_operation_exception_when_no_database_is_configured() { var serviceCollection = new ServiceCollection() - .AddGrate(); + .AddLogging() + .AddGrate(); var serviceProvider = serviceCollection.BuildServiceProvider(); Action action = () => serviceProvider.GetService(); action.Should().Throw("You forgot to configure the database. Please .UseXXX on the grate configuration."); @@ -138,8 +142,8 @@ protected virtual string CreateMigrationScript(DirectoryInfo sqlFolder, ISyntax var tableName = "grate_test"; var create_table = @$" CREATE TABLE {tableName} ( - id {syntax.BigintType} NOT NULL PRIMARY KEY, - name {syntax.VarcharType}(255) NOT NULL + id {BigintType} NOT NULL PRIMARY KEY, + name {VarcharType}(255) NOT NULL )"; MigrationsScriptsBase.WriteSql(sqlFolder, knownFolders[Up]!.Path, $"{tableName}_001_create_test_table.sql", create_table); var insert_test_data = @$" diff --git a/unittests/TestCommon/Generic/Bootstrapping/When_Grate_structure_already_exists.cs b/unittests/TestCommon/Generic/Bootstrapping/When_Grate_structure_already_exists.cs index 3a60ce85..531f006a 100644 --- a/unittests/TestCommon/Generic/Bootstrapping/When_Grate_structure_already_exists.cs +++ b/unittests/TestCommon/Generic/Bootstrapping/When_Grate_structure_already_exists.cs @@ -44,9 +44,9 @@ public async Task The_initial_structure_is_created_as_a_baseline() { var resourceText = await TestInfrastructure.Bootstrapping.GetContent(this.Context.DatabaseType.Assembly, resource); - resourceText = resourceText.Replace("{{ScriptsRunTable}}", "ScriptsRun"); - resourceText = resourceText.Replace("{{ScriptsRunErrorsTable}}", "ScriptsRunErrorsTable"); - resourceText = resourceText.Replace("{{VersionTable}}", "Version"); + resourceText = resourceText.Replace("{{ScriptsRunTable}}", config.ScriptsRunTableName); + resourceText = resourceText.Replace("{{ScriptsRunErrorsTable}}", config.ScriptsRunErrorsTableName); + resourceText = resourceText.Replace("{{VersionTable}}", config.VersionTableName); resourceText = resourceText.Replace("{{SchemaName}}", config.SchemaName); await conn.ExecuteAsync(resourceText); diff --git a/unittests/TestCommon/Generic/Bootstrapping/When_Grate_structure_is_not_latest_version.cs b/unittests/TestCommon/Generic/Bootstrapping/When_Grate_structure_is_not_latest_version.cs index 036fe31e..e6f592f1 100644 --- a/unittests/TestCommon/Generic/Bootstrapping/When_Grate_structure_is_not_latest_version.cs +++ b/unittests/TestCommon/Generic/Bootstrapping/When_Grate_structure_is_not_latest_version.cs @@ -59,9 +59,9 @@ public virtual async Task The_latest_version_is_applied() ); } - resourceText = resourceText.Replace("{{ScriptsRunTable}}", "ScriptsRun"); - resourceText = resourceText.Replace("{{ScriptsRunErrorsTable}}", "ScriptsRunErrorsTable"); - resourceText = resourceText.Replace("{{VersionTable}}", "Version"); + resourceText = resourceText.Replace("{{ScriptsRunTable}}", config.ScriptsRunTableName); + resourceText = resourceText.Replace("{{ScriptsRunErrorsTable}}", config.ScriptsRunErrorsTableName); + resourceText = resourceText.Replace("{{VersionTable}}", config.VersionTableName); resourceText = resourceText.Replace("{{SchemaName}}", config.SchemaName); await conn.ExecuteAsync(resourceText); diff --git a/unittests/TestCommon/Generic/GenericDatabase.cs b/unittests/TestCommon/Generic/GenericDatabase.cs index 4235aef4..ae786c15 100644 --- a/unittests/TestCommon/Generic/GenericDatabase.cs +++ b/unittests/TestCommon/Generic/GenericDatabase.cs @@ -211,7 +211,7 @@ protected virtual async Task> GetDatabases() try { using var conn = Context.CreateAdminDbConnection(); - databases = await conn.QueryAsync(sql); + databases = (await conn.QueryAsync(sql)).ToArray(); break; } catch (DbException) { } diff --git a/unittests/TestCommon/Generic/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs b/unittests/TestCommon/Generic/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs new file mode 100644 index 00000000..12c85257 --- /dev/null +++ b/unittests/TestCommon/Generic/Reported_issues/Non_ascii_characters_in_script/ScriptsRunErrorsTable.cs @@ -0,0 +1,328 @@ +using System.Security.Claims; +using Dapper; +using FluentAssertions; +using grate.Configuration; +using grate.Exceptions; +using TestCommon.Generic.Running_MigrationScripts; +using TestCommon.TestInfrastructure; +using Xunit.Abstractions; + +namespace TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script; + +public abstract class ScriptsRunErrorsTable : MigrationsScriptsBase +{ + private readonly string _sql; + + protected ScriptsRunErrorsTable(IGrateTestContext context, ITestOutputHelper testOutput) : base(context, testOutput) + { + _sql = $""" + Klønete bråkjekkas + {Context.Sql.LineComment} This is a comment: комментарий + {Context.Sql.SelectVersion} + """; + } + + + [Theory] + [InlineData("Blåbærkake")] + [InlineData("لا أحب الطقس الممطر")] + [InlineData("Я коричневая черепаха")] + [InlineData("✨")] + [InlineData("🎉")] + [InlineData("👍")] + public async Task Repository_path(string repositoryPath) + { + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = Folders.Default; + var filename = "1_script_with_utf_characters.sql"; + WriteSql(Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path), filename, _sql); + + var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) + .WithConnectionString(Context.ConnectionString(db)) + .WithFolders(knownFolders) + .WithSqlFilesDirectory(parent) + .WithRepositoryPath(repositoryPath) + .Build(); + + await using (var migrator = Context.Migrator.WithConfiguration(config)) + { + await Assert.ThrowsAnyAsync(() => migrator.Migrate()); + } + + string[] repositoryPaths; + string selectSql = $"SELECT repository_path FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + + using (var conn = Context.CreateDbConnection(db)) + { + repositoryPaths = (await conn.QueryAsync(selectSql)).ToArray(); + } + + repositoryPaths.Should().HaveCount(1); + repositoryPaths.First().Should().Be(repositoryPath); + } + + [Theory] + [InlineData("Værsjooon ein")] + [InlineData("الأول")] + [InlineData("первый")] + public async Task Version(string version) + { + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = Folders.Default; + var filename = "1_a_script.sql"; + WriteSql(Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path), filename, _sql); + + var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) + .WithConnectionString(Context.ConnectionString(db)) + .WithFolders(knownFolders) + .WithSqlFilesDirectory(parent) + .WithVersion(version) + .Build(); + + await using (var migrator = Context.Migrator.WithConfiguration(config)) + { + await Assert.ThrowsAnyAsync(() => migrator.Migrate()); + } + + string[] versions; + string selectSql = $"SELECT version FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + + using (var conn = Context.CreateDbConnection(db)) + { + versions = (await conn.QueryAsync(selectSql)).ToArray(); + } + + versions.Should().HaveCount(1); + versions.First().Should().Be(version); + } + + + [Theory] + [InlineData("det_første.sql")] + [InlineData("الأول.sql")] + [InlineData("первый_script.sql")] + [InlineData("✨.sql")] + [InlineData("🎉.sql")] + [InlineData("👍.sql")] + public async Task Script_name(string scriptName) + { + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = Folders.Default; + var filename = scriptName; + WriteSql(Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path), filename, _sql); + + var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) + .WithConnectionString(Context.ConnectionString(db)) + .WithFolders(knownFolders) + .WithSqlFilesDirectory(parent) + .Build(); + + await using (var migrator = Context.Migrator.WithConfiguration(config)) + { + await Assert.ThrowsAnyAsync(() => migrator.Migrate()); + } + + string[] scriptNames; + string selectSql = $"SELECT script_name FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + + using (var conn = Context.CreateDbConnection(db)) + { + scriptNames = (await conn.QueryAsync(selectSql)).ToArray(); + } + + scriptNames.Should().HaveCount(1); + scriptNames.First().Should().Be(scriptName); + } + + [Theory] + [InlineData("Blåbærkake")] + [InlineData("لا أحب الطقس الممطر")] + [InlineData("Я коричневая черепаха")] + [InlineData("✨")] + [InlineData("🎉")] + [InlineData("👍")] + public async Task Text_of_script(string characters) + { + var sql = $""" + {characters} + {Context.Sql.LineComment} This is a comment: {characters} + {Context.Sql.SelectVersion} + """; + + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = Folders.Default; + var filename = "1_script_with_utf_characters.sql"; + WriteSql(Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path), filename, sql); + + var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) + .WithConnectionString(Context.ConnectionString(db)) + .WithFolders(knownFolders) + .WithSqlFilesDirectory(parent) + .Build(); + + await using (var migrator = Context.Migrator.WithConfiguration(config)) + { + await Assert.ThrowsAnyAsync(() => migrator.Migrate()); + } + + string[] scripts; + string selectSql = $"SELECT text_of_script FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + + using (var conn = Context.CreateDbConnection(db)) + { + scripts = (await conn.QueryAsync(selectSql)).ToArray(); + } + + scripts.Should().HaveCount(1); + scripts.First().Should().Be(sql); + scripts.First().Should().Contain(characters); + } + + [Theory] + [InlineData("Blåbærkake")] + [InlineData("لا أحب الطقس الممطر")] + [InlineData("Я коричневая черепаха")] + [InlineData("✨")] + [InlineData("🎉")] + [InlineData("👍")] + public async Task Erroneous_part_of_script(string characters) + { + var sql = $""" + {characters} + {Context.Sql.SelectVersion} + """; + + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = Folders.Default; + var filename = "1_script_with_utf_characters.sql"; + WriteSql(Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path), filename, sql); + + var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) + .WithConnectionString(Context.ConnectionString(db)) + .WithFolders(knownFolders) + .WithSqlFilesDirectory(parent) + .Build(); + + await using (var migrator = Context.Migrator.WithConfiguration(config)) + { + await Assert.ThrowsAnyAsync(() => migrator.Migrate()); + } + + string[] errors; + string selectSql = $"SELECT erroneous_part_of_script FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + + using (var conn = Context.CreateDbConnection(db)) + { + errors = (await conn.QueryAsync(selectSql)).ToArray(); + } + + errors.Should().HaveCount(1); + errors.First().Should().Contain(characters); + } + + + [Theory] + [InlineData("Blåbærkake")] + [InlineData("لا أحب الطقس الممطر")] + [InlineData("Я коричневая черепаха")] + [InlineData("✨")] + [InlineData("🎉")] + [InlineData("👍")] + public async Task Error_message(string characters) + { + var sql = $""" + {characters}! + {Context.Sql.SelectVersion} + """; + + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = Folders.Default; + var filename = "1_script_with_utf_characters.sql"; + WriteSql(Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path), filename, sql); + + var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) + .WithConnectionString(Context.ConnectionString(db)) + .WithFolders(knownFolders) + .WithSqlFilesDirectory(parent) + .Build(); + + await using (var migrator = Context.Migrator.WithConfiguration(config)) + { + await Assert.ThrowsAnyAsync(() => migrator.Migrate()); + } + + string[] errors; + string selectSql = $"SELECT error_message FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + + using (var conn = Context.CreateDbConnection(db)) + { + errors = (await conn.QueryAsync(selectSql)).ToArray(); + } + + errors.Should().HaveCount(1); + // This doesn't work well for MariaDb with 🎉 and 👍, they are only '?' in the actual error message from the DB + //errors.First().Should().Contain(characters); + } + + + [Theory] + [InlineData("JanBanan")] + [InlineData("Blåbærkake")] + // [InlineData("لا أحب الطقس الممطر")] + // [InlineData("Я коричневая черепаха")] + // [InlineData("✨")] + // [InlineData("🎉")] + // [InlineData("👍")] + public async Task Entered_by_does_not_bootstrap_well_with_unicode(string enteredBy) + { + var sql = $""" + {enteredBy} + {Context.Sql.SelectVersion} + """; + + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = Folders.Default; + var filename = "1_script_with_utf_characters.sql"; + WriteSql(Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path), filename, sql); + + var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) + .WithConnectionString(Context.ConnectionString(db)) + .WithFolders(knownFolders) + .WithSqlFilesDirectory(parent) + .Build(); + + Thread.CurrentPrincipal = + new ClaimsPrincipal(new[] { new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, enteredBy) }) }); + + await using (var migrator = Context.Migrator.WithConfiguration(config)) + { + await Assert.ThrowsAnyAsync(() => migrator.Migrate()); + } + + string[] user; + string selectSql = $"SELECT entered_by FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + + using (var conn = Context.CreateDbConnection(db)) + { + user = (await conn.QueryAsync(selectSql)).ToArray(); + } + + user.Should().HaveCount(1); + user.First().Should().Contain(enteredBy); + } + +} diff --git a/unittests/TestCommon/Generic/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs b/unittests/TestCommon/Generic/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs new file mode 100644 index 00000000..e26b3145 --- /dev/null +++ b/unittests/TestCommon/Generic/Reported_issues/Non_ascii_characters_in_script/ScriptsRunTable.cs @@ -0,0 +1,106 @@ +using Dapper; +using FluentAssertions; +using grate.Configuration; +using TestCommon.Generic.Running_MigrationScripts; +using TestCommon.TestInfrastructure; +using Xunit.Abstractions; + +namespace TestCommon.Generic.Reported_issues.Non_ascii_characters_in_script; + +public abstract class ScriptsRunTable(IGrateTestContext context, ITestOutputHelper testOutput) + : MigrationsScriptsBase(context, testOutput) +{ + protected ScriptsRunTable() : this(null!, null!) + { + } + + + [Theory] + [InlineData("Blåbærkake")] + [InlineData("لا أحب الطقس الممطر")] + [InlineData("Я коричневая черепаха")] + [InlineData("✨")] + [InlineData("🎉")] + [InlineData("👍")] + public async Task Text_of_script(string characters) + { + var sql = $""" + {Context.Sql.LineComment} This is a comment: {characters} + {Context.Sql.SelectVersion} + """; + + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = Folders.Default; + var filename = "1_script_with_utf_characters.sql"; + WriteSql(Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path), filename, sql); + + var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) + .WithConnectionString(Context.ConnectionString(db)) + .WithFolders(knownFolders) + .WithSqlFilesDirectory(parent) + .Build(); + + await using (var migrator = Context.Migrator.WithConfiguration(config)) + { + await migrator.Migrate(); + } + + string[] scripts; + string selectSql = $"SELECT text_of_script FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRun")}"; + + using (var conn = Context.CreateDbConnection(db)) + { + scripts = (await conn.QueryAsync(selectSql)).ToArray(); + } + + scripts.Should().HaveCount(1); + scripts.First().Should().Be(sql); + scripts.First().Should().Contain(characters); + } + + [Theory] + [InlineData("det_første.sql")] + [InlineData("الأول.sql")] + [InlineData("первый_script.sql")] + [InlineData("✨.sql")] + [InlineData("🎉.sql")] + [InlineData("👍.sql")] + public async Task Script_name(string scriptName) + { + var sql = $""" + {Context.Sql.LineComment} This is a comment: комментарий + {Context.Sql.SelectVersion} + """; + + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = Folders.Default; + var filename = scriptName; + WriteSql(Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path), filename, sql); + + var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) + .WithConnectionString(Context.ConnectionString(db)) + .WithFolders(knownFolders) + .WithSqlFilesDirectory(parent) + .Build(); + + await using (var migrator = Context.Migrator.WithConfiguration(config)) + { + await migrator.Migrate(); + } + + string[] scripts; + string selectSql = $"SELECT script_name FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRun")}"; + + using (var conn = Context.CreateDbConnection(db)) + { + scripts = (await conn.QueryAsync(selectSql)).ToArray(); + } + + scripts.Should().HaveCount(1); + scripts.First().Should().Be(scriptName); + } +} diff --git a/unittests/TestCommon/Generic/Running_MigrationScripts/Failing_Scripts.cs b/unittests/TestCommon/Generic/Running_MigrationScripts/Failing_Scripts.cs index aa4690d3..1ba0cfce 100644 --- a/unittests/TestCommon/Generic/Running_MigrationScripts/Failing_Scripts.cs +++ b/unittests/TestCommon/Generic/Running_MigrationScripts/Failing_Scripts.cs @@ -123,21 +123,16 @@ public async Task Inserts_Failed_Scripts_Into_ScriptRunErrors_Table() await using (var migrator = Context.Migrator.WithConfiguration(config)) { - try - { - await migrator.Migrate(); - } - catch (MigrationFailed) - { - } + var ex = await Assert.ThrowsAsync(migrator.Migrate); + ex.Should().NotBeNull(); } string[] scripts; - string sql = $"SELECT script_name FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + string sql = $"SELECT script_name FROM {Context.Syntax.TableWithSchema(config.SchemaName, config.ScriptsRunErrorsTableName)}"; using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) { - using var conn = Context.CreateDbConnection(db); + using var conn = Context.GetDbConnection(Context.ConnectionString(db)); scripts = (await conn.QueryAsync(sql)).ToArray(); } @@ -164,13 +159,7 @@ public async Task Inserts_RepositoryPath_Into_ScriptRunErrors_Table() await using (var migrator = Context.Migrator.WithConfiguration(config)) { - try - { - await migrator.Migrate(); - } - catch (MigrationFailed) - { - } + await Assert.ThrowsAsync(migrator.Migrate); } string? loggedRepositoryPath; @@ -178,7 +167,7 @@ public async Task Inserts_RepositoryPath_Into_ScriptRunErrors_Table() using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) { - using var conn = Context.CreateDbConnection(db); + using var conn = Context.GetDbConnection(Context.ConnectionString(db)); loggedRepositoryPath = (await conn.QuerySingleOrDefaultAsync(sql)); } @@ -193,8 +182,10 @@ public async Task Inserts_Large_Failed_Scripts_Into_ScriptRunErrors_Table() var parent = TestConfig.CreateRandomTempDirectory(); var knownFolders = global::grate.Configuration.Folders.Default; - + CreateLongInvalidSql(parent, knownFolders[Up]); + + string fileContent = await File.ReadAllTextAsync(Path.Combine(parent.ToString(), knownFolders[Up]!.Path, "2_failing.sql")); var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration) .WithConnectionString(Context.ConnectionString(db)) @@ -204,20 +195,19 @@ public async Task Inserts_Large_Failed_Scripts_Into_ScriptRunErrors_Table() await using (var migrator = Context.Migrator.WithConfiguration(config)) { - await Assert.ThrowsAsync(() => migrator.Migrate()); + await Assert.ThrowsAsync(migrator.Migrate); } - string fileContent = await File.ReadAllTextAsync(Path.Combine(parent.ToString(), knownFolders[Up]!.Path, "2_failing.sql")); - string[] scripts; - string sql = $"SELECT text_of_script FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + string sql = $"SELECT text_of_script FROM {Context.Syntax.TableWithSchema(config.SchemaName, config.ScriptsRunErrorsTableName )}"; using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) { - using var conn = Context.CreateDbConnection(db); + using var conn = Context.GetDbConnection(Context.ConnectionString(db)); scripts = (await conn.QueryAsync(sql)).ToArray(); } + scripts.Should().HaveCount(1); scripts.First().Should().Be(fileContent); } @@ -334,22 +324,17 @@ public async Task Create_a_version_in_error_if_ran_without_transaction() await using (migrator = Context.Migrator.WithConfiguration(config)) { - try - { - await migrator.Migrate(); - } - catch (MigrationFailed) - { - } + var ex = await Assert.ThrowsAsync(migrator.Migrate); + ex.Should().NotBeNull(); } - + string[] versions; - string sql = $"SELECT status FROM {Context.Syntax.TableWithSchema("grate", "Version")}"; + string sql = $"SELECT status FROM {Context.Syntax.TableWithSchema(config.SchemaName, config.VersionTableName)}"; using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) { - using var conn = Context.CreateDbConnection(db); + using var conn = Context.GetDbConnection(Context.ConnectionString(db)); versions = (await conn.QueryAsync(sql)).ToArray(); } @@ -378,18 +363,12 @@ private async Task RunMigration(MigrationsFolder folder, string filena await using (var migrator = Context.Migrator.WithConfiguration(config)) { - try - { - await migrator.Migrate(); - } - catch (MigrationFailed) - { - } + await Assert.ThrowsAsync(migrator.Migrate); } - string sql = $"SELECT script_name FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRun")}"; + string sql = $"SELECT script_name FROM {Context.Syntax.TableWithSchema(config.SchemaName, config.ScriptsRunTableName)}"; - using (var conn = Context.CreateDbConnection(db)) + using (var conn = Context.GetDbConnection(Context.ConnectionString(db))) { scripts = (await conn.QueryAsync(sql)).ToArray(); } diff --git a/unittests/TestCommon/Generic/Running_MigrationScripts/Versioning_The_Database.cs b/unittests/TestCommon/Generic/Running_MigrationScripts/Versioning_The_Database.cs index bb98121a..04a234c9 100644 --- a/unittests/TestCommon/Generic/Running_MigrationScripts/Versioning_The_Database.cs +++ b/unittests/TestCommon/Generic/Running_MigrationScripts/Versioning_The_Database.cs @@ -106,7 +106,7 @@ public async Task Creates_a_new_version_with_status_InProgress() using (var conn = Context.CreateDbConnection(db)) { - entries = await conn.QueryAsync<(string version, string status)>(sql); + entries = (await conn.QueryAsync<(string version, string status)>(sql)).ToArray(); } entries.Should().HaveCount(2); diff --git a/unittests/TestCommon/TestInfrastructure/GrateTestContext.cs b/unittests/TestCommon/TestInfrastructure/GrateTestContext.cs index 90ad9cbd..3bf55b49 100644 --- a/unittests/TestCommon/TestInfrastructure/GrateTestContext.cs +++ b/unittests/TestCommon/TestInfrastructure/GrateTestContext.cs @@ -4,8 +4,9 @@ namespace TestCommon.TestInfrastructure; -public abstract class GrateTestContext(ITestDatabase testDatabase) : IGrateTestContext, IAsyncLifetime +public abstract class GrateTestContext(IGrateMigrator migrator, ITestDatabase testDatabase) : IGrateTestContext, IAsyncLifetime { + public IGrateMigrator Migrator { get; } = migrator; protected ITestDatabase TestDatabase { get; } = testDatabase; public string AdminConnectionString => TestDatabase.AdminConnectionString; @@ -34,9 +35,9 @@ public async Task DisposeAsync() public abstract bool SupportsTransaction { get; } public abstract SqlStatements Sql { get; } public abstract string ExpectedVersionPrefix { get; } - public abstract IGrateMigrator Migrator { get; } public abstract bool SupportsCreateDatabase { get; } public abstract bool SupportsSchemas { get; } public abstract IDbConnection GetDbConnection(string connectionString); + } diff --git a/unittests/TestCommon/TestInfrastructure/IGrateTestContext.cs b/unittests/TestCommon/TestInfrastructure/IGrateTestContext.cs index f1e06e30..3e5aa2ff 100644 --- a/unittests/TestCommon/TestInfrastructure/IGrateTestContext.cs +++ b/unittests/TestCommon/TestInfrastructure/IGrateTestContext.cs @@ -39,7 +39,6 @@ public interface IGrateTestContext //public bool SupportsSchemas => Migrator.SupportsSchemas(); - bool SupportsCreateDatabase { get; } bool SupportsSchemas { get; } IDbConnection GetDbConnection(string connectionString); diff --git a/unittests/TestCommon/TestInfrastructure/TestConfig.cs b/unittests/TestCommon/TestInfrastructure/TestConfig.cs index 4ea420eb..50fbc44e 100644 --- a/unittests/TestCommon/TestInfrastructure/TestConfig.cs +++ b/unittests/TestCommon/TestInfrastructure/TestConfig.cs @@ -8,20 +8,11 @@ public static class TestConfig { private static readonly Random Random = Random.Shared; - public static string RandomDatabase() => Random.GetString(15); + public static string RandomDatabase() => Random.GetString(16); public static DirectoryInfo CreateRandomTempDirectory() { - var dummyFile = Path.GetTempFileName(); - File.Delete(dummyFile); - - if (Directory.Exists(dummyFile)) - { - Directory.Delete(dummyFile, true); - } - - var scriptsDir = Directory.CreateDirectory(dummyFile); - return scriptsDir; + return Directory.CreateTempSubdirectory(); } public static DirectoryInfo Wrap(DirectoryInfo root, string? subFolder) => new(Path.Combine(root.ToString(), subFolder ?? "")); diff --git a/unittests/TestCommon/TestInfrastructure/TestContainerDatabase.cs b/unittests/TestCommon/TestInfrastructure/TestContainerDatabase.cs index 40fd07c8..230efd65 100644 --- a/unittests/TestCommon/TestInfrastructure/TestContainerDatabase.cs +++ b/unittests/TestCommon/TestInfrastructure/TestContainerDatabase.cs @@ -5,9 +5,9 @@ namespace TestCommon.TestInfrastructure; public abstract class TestContainerDatabase : ITestDatabase, IAsyncLifetime { - public abstract string DockerImage { get; } - - private readonly Random _random = new(); + public abstract string DockerImage { get; } + + private readonly Random _random = Random.Shared; public string AdminPassword { get; } public int Port => TestContainer.GetMappedPublicPort(InternalPort);