diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH1274ExportExclude/NH1274ExportExcludeFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH1274ExportExclude/NH1274ExportExcludeFixture.cs index cdcd11bb2f2..0a08de25c46 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH1274ExportExclude/NH1274ExportExcludeFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH1274ExportExclude/NH1274ExportExcludeFixture.cs @@ -95,7 +95,7 @@ public void SchemaExport_Validate_CausesValidateExceptionAsync() Assert.That( () => validator.ValidateAsync(), Throws.TypeOf<SchemaValidationException>() - .And.Message.EqualTo("Schema validation failed: see list of validation errors") + .And.Message.StartsWith("Schema validation failed: see list of validation errors") .And.Property("ValidationErrors").Contains("Missing table: Home_Validate")); } diff --git a/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/PostgresSchemaValidateFixture.cs b/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/PostgresSchemaValidateFixture.cs new file mode 100644 index 00000000000..6dd25fbf690 --- /dev/null +++ b/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/PostgresSchemaValidateFixture.cs @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + + +using System; +using NHibernate.Cfg; +using NHibernate.Dialect; +using NHibernate.Mapping.ByCode; +using NHibernate.Tool.hbm2ddl; +using NUnit.Framework; + +namespace NHibernate.Test.Tools.hbm2ddl.SchemaValidator +{ + using System.Threading.Tasks; + [TestFixture] + public class PostgresSchemaValidateFixtureAsync + { + [Test] + public async Task ShouldBeInvalidIfTimestampTzIsExpectedAndGotTimestampAsync() + { + var actual = BuildConfiguration("timestamp"); + Assume.That(Dialect.Dialect.GetDialect(actual.Properties) is PostgreSQLDialect); + + var expected = BuildConfiguration("timestamptz"); + + var export = new SchemaExport(actual); + await (export.CreateAsync(true, true)); + + try + { + var validator = new Tool.hbm2ddl.SchemaValidator(expected); + + var error = Assert.ThrowsAsync<SchemaValidationException>(() => validator.ValidateAsync()); + Assert.That(error, Has.Message.StartsWith("Schema validation failed: see list of validation errors")); + Assert.That( + error, + Has.Property("ValidationErrors").Some.Contains("Wrong column type").IgnoreCase + .And.Contains("for column CreatedAt. Found: timestamp, Expected timestamptz").IgnoreCase); + } + finally + { + await (export.DropAsync(true, true)); + } + } + + [Test] + public async Task ShouldBeInvalidIfTimestampIsExpectedAndGotTimestampTzAsync() + { + var actual = BuildConfiguration("timestamptz"); + Assume.That(Dialect.Dialect.GetDialect(actual.Properties) is PostgreSQLDialect); + + var expected = BuildConfiguration("timestamp"); + + var export = new SchemaExport(actual); + await (export.CreateAsync(true, true)); + + try + { + var validator = new Tool.hbm2ddl.SchemaValidator(expected); + + var error = Assert.ThrowsAsync<SchemaValidationException>(() => validator.ValidateAsync()); + Assert.That(error, Has.Message.StartsWith("Schema validation failed: see list of validation errors")); + Assert.That( + error, + Has.Property("ValidationErrors").Some.Contains("Wrong column type").IgnoreCase + .And.Contains("for column CreatedAt. Found: timestamptz, Expected timestamp").IgnoreCase); + } + finally + { + await (export.DropAsync(true, true)); + } + } + + private static Configuration BuildConfiguration(string type) + { + var mapper = new ModelMapper(); + mapper.Class<Entity>(c => + { + c.Table("Entity"); + c.Id(x => x.Id); + c.Property(x => x.CreatedAt, p => p.Column(cm => cm.SqlType(type))); + }); + + var cfg = TestConfigurationHelper.GetDefaultConfiguration(); + cfg.AddDeserializedMapping(mapper.CompileMappingForAllExplicitlyAddedEntities(), "Entity"); + return cfg; + } + + public class Entity + { + public virtual int Id { get; set; } + public virtual DateTime CreatedAt { get; set; } + } + } +} diff --git a/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/SchemaValidateFixture.cs b/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/SchemaValidateFixture.cs index a8809777bba..948e8ee7a59 100644 --- a/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/SchemaValidateFixture.cs +++ b/src/NHibernate.Test/Async/Tools/hbm2ddl/SchemaValidator/SchemaValidateFixture.cs @@ -52,7 +52,7 @@ public async Task ShouldVerifySameTableAsync() await (validator.ValidateAsync()); } - [Test, SetCulture("tr-TR"), SetUICulture("tr-TR"), NetFxOnly] + [Test, SetCulture("tr-TR"), SetUICulture("tr-TR")] public async Task ShouldVerifySameTableTurkishAsync() { //NH-3063 @@ -90,7 +90,7 @@ public void ShouldNotVerifyModifiedTableAsync() Assert.That( () => validatorV2.ValidateAsync(), Throws.TypeOf<SchemaValidationException>() - .And.Message.EqualTo("Schema validation failed: see list of validation errors") + .And.Message.StartsWith("Schema validation failed: see list of validation errors") .And.Property("ValidationErrors").Some.Contains("Missing column: Name in ").IgnoreCase.And.Contains("Version").IgnoreCase); } @@ -103,7 +103,7 @@ public void ShouldNotVerifyMultiModifiedTableAsync() var error = Assert.ThrowsAsync<SchemaValidationException>(() => validator.ValidateAsync()); Assert.That(error, - Has.Message.EqualTo("Schema validation failed: see list of validation errors") + Has.Message.StartsWith("Schema validation failed: see list of validation errors") .And.Property("ValidationErrors").Some.Contains("Missing column: Name in ").IgnoreCase.And.Contains("Version").IgnoreCase); Assert.That(error, Has.Property("ValidationErrors").Some.Contains("Missing column: Title in ").IgnoreCase.And.Contains("Version").IgnoreCase); diff --git a/src/NHibernate.Test/NHSpecificTest/NH1274ExportExclude/NH1274ExportExcludeFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH1274ExportExclude/NH1274ExportExcludeFixture.cs index 3cc8b6df1dc..95eebe4301c 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH1274ExportExclude/NH1274ExportExcludeFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH1274ExportExclude/NH1274ExportExcludeFixture.cs @@ -84,7 +84,7 @@ public void SchemaExport_Validate_CausesValidateException() Assert.That( () => validator.Validate(), Throws.TypeOf<SchemaValidationException>() - .And.Message.EqualTo("Schema validation failed: see list of validation errors") + .And.Message.StartsWith("Schema validation failed: see list of validation errors") .And.Property("ValidationErrors").Contains("Missing table: Home_Validate")); } diff --git a/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/PostgresSchemaValidateFixture.cs b/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/PostgresSchemaValidateFixture.cs new file mode 100644 index 00000000000..fe236739417 --- /dev/null +++ b/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/PostgresSchemaValidateFixture.cs @@ -0,0 +1,90 @@ +using System; +using NHibernate.Cfg; +using NHibernate.Dialect; +using NHibernate.Mapping.ByCode; +using NHibernate.Tool.hbm2ddl; +using NUnit.Framework; + +namespace NHibernate.Test.Tools.hbm2ddl.SchemaValidator +{ + [TestFixture] + public class PostgresSchemaValidateFixture + { + [Test] + public void ShouldBeInvalidIfTimestampTzIsExpectedAndGotTimestamp() + { + var actual = BuildConfiguration("timestamp"); + Assume.That(Dialect.Dialect.GetDialect(actual.Properties) is PostgreSQLDialect); + + var expected = BuildConfiguration("timestamptz"); + + var export = new SchemaExport(actual); + export.Create(true, true); + + try + { + var validator = new Tool.hbm2ddl.SchemaValidator(expected); + + var error = Assert.Throws<SchemaValidationException>(() => validator.Validate()); + Assert.That(error, Has.Message.StartsWith("Schema validation failed: see list of validation errors")); + Assert.That( + error, + Has.Property("ValidationErrors").Some.Contains("Wrong column type").IgnoreCase + .And.Contains("for column CreatedAt. Found: timestamp, Expected timestamptz").IgnoreCase); + } + finally + { + export.Drop(true, true); + } + } + + [Test] + public void ShouldBeInvalidIfTimestampIsExpectedAndGotTimestampTz() + { + var actual = BuildConfiguration("timestamptz"); + Assume.That(Dialect.Dialect.GetDialect(actual.Properties) is PostgreSQLDialect); + + var expected = BuildConfiguration("timestamp"); + + var export = new SchemaExport(actual); + export.Create(true, true); + + try + { + var validator = new Tool.hbm2ddl.SchemaValidator(expected); + + var error = Assert.Throws<SchemaValidationException>(() => validator.Validate()); + Assert.That(error, Has.Message.StartsWith("Schema validation failed: see list of validation errors")); + Assert.That( + error, + Has.Property("ValidationErrors").Some.Contains("Wrong column type").IgnoreCase + .And.Contains("for column CreatedAt. Found: timestamptz, Expected timestamp").IgnoreCase); + } + finally + { + export.Drop(true, true); + } + } + + private static Configuration BuildConfiguration(string type) + { + var mapper = new ModelMapper(); + mapper.Class<Entity>(c => + { + c.Table("Entity"); + c.Id(x => x.Id); + c.Property(x => x.CreatedAt, p => p.Column(cm => cm.SqlType(type))); + }); + + var cfg = TestConfigurationHelper.GetDefaultConfiguration(); + cfg.AddDeserializedMapping(mapper.CompileMappingForAllExplicitlyAddedEntities(), "Entity"); + return cfg; + } + + public class Entity + { + public virtual int Id { get; set; } + public virtual DateTime CreatedAt { get; set; } + } + } +} diff --git a/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/SchemaValidateFixture.cs b/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/SchemaValidateFixture.cs index ba8686986b4..bded7d7236b 100644 --- a/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/SchemaValidateFixture.cs +++ b/src/NHibernate.Test/Tools/hbm2ddl/SchemaValidator/SchemaValidateFixture.cs @@ -41,7 +41,7 @@ public void ShouldVerifySameTable() validator.Validate(); } - [Test, SetCulture("tr-TR"), SetUICulture("tr-TR"), NetFxOnly] + [Test, SetCulture("tr-TR"), SetUICulture("tr-TR")] public void ShouldVerifySameTableTurkish() { //NH-3063 @@ -79,7 +79,7 @@ public void ShouldNotVerifyModifiedTable() Assert.That( () => validatorV2.Validate(), Throws.TypeOf<SchemaValidationException>() - .And.Message.EqualTo("Schema validation failed: see list of validation errors") + .And.Message.StartsWith("Schema validation failed: see list of validation errors") .And.Property("ValidationErrors").Some.Contains("Missing column: Name in ").IgnoreCase.And.Contains("Version").IgnoreCase); } @@ -92,7 +92,7 @@ public void ShouldNotVerifyMultiModifiedTable() var error = Assert.Throws<SchemaValidationException>(() => validator.Validate()); Assert.That(error, - Has.Message.EqualTo("Schema validation failed: see list of validation errors") + Has.Message.StartsWith("Schema validation failed: see list of validation errors") .And.Property("ValidationErrors").Some.Contains("Missing column: Name in ").IgnoreCase.And.Contains("Version").IgnoreCase); Assert.That(error, Has.Property("ValidationErrors").Some.Contains("Missing column: Title in ").IgnoreCase.And.Contains("Version").IgnoreCase); diff --git a/src/NHibernate/Mapping/Table.cs b/src/NHibernate/Mapping/Table.cs index 3082a9c02b0..d20bb372c33 100644 --- a/src/NHibernate/Mapping/Table.cs +++ b/src/NHibernate/Mapping/Table.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using NHibernate.Dialect.Schema; using NHibernate.Engine; using NHibernate.Util; @@ -1035,7 +1036,13 @@ public IEnumerable<string> ValidateColumns(Dialect.Dialect dialect, IMapping map } //TODO: Add new method to ColumnMetadata :getTypeCode - bool typesMatch = column.GetSqlType(dialect, mapping).StartsWith(columnInfo.TypeName, StringComparison.OrdinalIgnoreCase); + var columnType = column.GetSqlType(dialect, mapping); + var typesMatch = + string.Equals(columnType, columnInfo.TypeName, StringComparison.OrdinalIgnoreCase) || + Regex.IsMatch( + columnType, + $@"^{Regex.Escape(columnInfo.TypeName)}\b", + RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); //|| columnInfo.get() == column.GetSqlTypeCode(mapping); if (!typesMatch) { @@ -1045,7 +1052,7 @@ public IEnumerable<string> ValidateColumns(Dialect.Dialect dialect, IMapping map dialect.Qualify(tableInfo.Catalog, tableInfo.Schema, tableInfo.Name), column.Name, columnInfo.TypeName.ToLowerInvariant(), - column.GetSqlType(dialect, mapping))); + columnType)); } } diff --git a/src/NHibernate/SchemaValidationException.cs b/src/NHibernate/SchemaValidationException.cs index 2d654a07583..29cb9a12e67 100644 --- a/src/NHibernate/SchemaValidationException.cs +++ b/src/NHibernate/SchemaValidationException.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Runtime.Serialization; using System.Security; +using System.Text; namespace NHibernate { @@ -28,5 +29,21 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont base.GetObjectData(info, context); info.AddValue("ValidationErrors", ValidationErrors); } + + public override string Message + { + get + { + var message = base.Message; + if (ValidationErrors == null || ValidationErrors.Count == 0) + return message; + + var sb = new StringBuilder(message).AppendLine().AppendLine("Validation errors:"); + foreach (var error in ValidationErrors) + sb.Append("\t- ").AppendLine(error); + + return sb.ToString(); + } + } } }