|
| 1 | +using System; |
| 2 | +using NHibernate.Type; |
| 3 | +using NUnit.Framework; |
| 4 | + |
| 5 | +namespace NHibernate.Test.TypesTest |
| 6 | +{ |
| 7 | + /// <summary> |
| 8 | + /// Test fixture for type <see cref="TimestampUtcType"/>. |
| 9 | + /// </summary> |
| 10 | + [TestFixture] |
| 11 | + public class TimestampUtcTypeFixture : TypeFixtureBase |
| 12 | + { |
| 13 | + readonly TimestampUtcType _type = NHibernateUtil.TimestampUtc; |
| 14 | + readonly DateTime _utc = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Utc); |
| 15 | + readonly DateTime _local = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Local); |
| 16 | + readonly DateTime _unspecified = new DateTime(1976, 11, 30, 10, 0, 0, 300, DateTimeKind.Unspecified); |
| 17 | + |
| 18 | + /// <summary> |
| 19 | + /// 1976-11-30T10:00:00.3000000 |
| 20 | + /// </summary> |
| 21 | + const long DateInTicks = 623537928003000000; |
| 22 | + |
| 23 | + protected override string TypeName => "TimestampUtc"; |
| 24 | + |
| 25 | + [Test] |
| 26 | + public void Next() |
| 27 | + { |
| 28 | + var current = DateTime.Parse("2004-01-01"); |
| 29 | + var next = (DateTime)_type.Next(current, null); |
| 30 | + |
| 31 | + Assert.AreEqual(DateTimeKind.Utc, next.Kind, "Kind is not Utc"); |
| 32 | + Assert.IsTrue(next > current, "next should be greater than current (could be equal depending on how quickly this occurs)"); |
| 33 | + } |
| 34 | + |
| 35 | + /// <summary> |
| 36 | + /// Perform a 'seed' and check if the result is a datetime with kind set to Utc. |
| 37 | + /// </summary> |
| 38 | + [Test] |
| 39 | + public void Seed() |
| 40 | + { |
| 41 | + var type = NHibernateUtil.TimestampUtc; |
| 42 | + Assert.IsTrue(type.Seed(null) is DateTime, "Seed should be DateTime"); |
| 43 | + |
| 44 | + var value = (DateTime)type.Seed(null); |
| 45 | + Assert.AreEqual(DateTimeKind.Utc, value.Kind, "Kind should be Utc"); |
| 46 | + } |
| 47 | + |
| 48 | + /// <summary> |
| 49 | + /// Perform a basis write with a DateTime value where Kind is Local which should fail. |
| 50 | + /// </summary> |
| 51 | + [Test] |
| 52 | + [TestCase(DateTimeKind.Unspecified)] |
| 53 | + [TestCase(DateTimeKind.Local)] |
| 54 | + public void LocalReadWrite_Fail(DateTimeKind kind) |
| 55 | + { |
| 56 | + var entity = new TimestampUtcClass |
| 57 | + { |
| 58 | + Id = 1, |
| 59 | + Value = DateTime.SpecifyKind(DateTime.Now, kind) |
| 60 | + }; |
| 61 | + |
| 62 | + using(var session = OpenSession()) |
| 63 | + using(var tx = session.BeginTransaction()) |
| 64 | + { |
| 65 | + session.Save(entity); |
| 66 | + Assert.That(() => session.Flush(), Throws.TypeOf<PropertyValueException>()); |
| 67 | + tx.Rollback(); |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + /// <summary> |
| 72 | + /// Create two session. Write entity in the first and read it in the second and compare if |
| 73 | + /// the retrieved timestamp value still equals the original value. |
| 74 | + /// </summary> |
| 75 | + /// <remarks> This test takes the database precision into consideration.</remarks> |
| 76 | + [Test] |
| 77 | + public void UtcReadWrite_Success() |
| 78 | + { |
| 79 | + TimestampUtcClass entity; |
| 80 | + |
| 81 | + // Save |
| 82 | + using(var session = OpenSession()) |
| 83 | + using(var tx = session.BeginTransaction()) |
| 84 | + { |
| 85 | + // Create a new datetime value and round it to the precision that the database supports. This |
| 86 | + // code basically the same as in the implementation but here to guard posible changes. |
| 87 | + var resolution = session.GetSessionImplementation().Factory.Dialect.TimestampResolutionInTicks; |
| 88 | + var next = DateTime.UtcNow; |
| 89 | + next = next.AddTicks(-(next.Ticks % resolution)); |
| 90 | + |
| 91 | + entity = new TimestampUtcClass |
| 92 | + { |
| 93 | + Id = 1, |
| 94 | + Value = next |
| 95 | + }; |
| 96 | + |
| 97 | + session.Save(entity); |
| 98 | + tx.Commit(); |
| 99 | + session.Close(); |
| 100 | + } |
| 101 | + |
| 102 | + // Retrieve and compare |
| 103 | + using (var session = OpenSession()) |
| 104 | + using (var tx = session.BeginTransaction()) |
| 105 | + { |
| 106 | + var result = session.Get<TimestampUtcClass>(entity.Id); |
| 107 | + Assert.IsNotNull(result, "Entity not saved or cannot be retrieved by its key."); |
| 108 | + |
| 109 | + // Property: Value |
| 110 | + Assert.AreEqual(DateTimeKind.Utc, result.Value.Kind, "Kind is NOT Utc"); |
| 111 | + Assert.AreEqual(entity.Value.Ticks, result.Value.Ticks, "Value should be the same."); |
| 112 | + |
| 113 | + // Property: Revision |
| 114 | + var revision = result.Revision; |
| 115 | + Assert.AreEqual(DateTimeKind.Utc, revision.Kind, "Kind is NOT Utc"); |
| 116 | + |
| 117 | + var differenceInSeconds = Math.Abs((revision - DateTime.UtcNow).TotalSeconds); |
| 118 | + Assert.IsTrue(differenceInSeconds < 1d, "Difference should be less then 1 second."); |
| 119 | + |
| 120 | + tx.Commit(); |
| 121 | + session.Close(); |
| 122 | + } |
| 123 | + |
| 124 | + // Delete |
| 125 | + using (var session = OpenSession()) |
| 126 | + using (var tx = session.BeginTransaction()) |
| 127 | + { |
| 128 | + var result = session.Get<TimestampUtcClass>(entity.Id); |
| 129 | + session.Delete(result); |
| 130 | + tx.Commit(); |
| 131 | + session.Close(); |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + /// <summary> |
| 136 | + /// Tests if the type FromStringValue implementation behaves as expected. |
| 137 | + /// </summary> |
| 138 | + /// <param name="timestampValue"></param> |
| 139 | + [Test] |
| 140 | + [TestCase("2011-01-27T15:50:59.6220000+02:00")] |
| 141 | + [TestCase("2011-01-27T14:50:59.6220000+01:00")] |
| 142 | + [TestCase("2011-01-27T13:50:59.6220000Z")] |
| 143 | + public void FromStringValue_ParseValidValues(string timestampValue) |
| 144 | + { |
| 145 | + var timestamp = DateTime.Parse(timestampValue); |
| 146 | + |
| 147 | + Assert.AreEqual(DateTimeKind.Local, timestamp.Kind, "Kind is NOT Local. dotnet framework parses datetime values with kind set to Local and time correct to local timezone."); |
| 148 | + |
| 149 | + timestamp = timestamp.ToUniversalTime(); |
| 150 | + |
| 151 | + var value = (DateTime)_type.FromStringValue(timestampValue); |
| 152 | + |
| 153 | + Assert.AreEqual(timestamp, value, timestampValue); |
| 154 | + Assert.AreEqual(DateTimeKind.Utc, value.Kind, "Kind is NOT Utc"); |
| 155 | + } |
| 156 | + |
| 157 | + /// <summary> |
| 158 | + /// Test the framework tostring behavior. If the test fails then the <see cref="TimestampType"/> and <see cref="TimestampUtcType"/> implemention could not work propertly at run-time. |
| 159 | + /// </summary> |
| 160 | + [Test, Category("Expected framework behavior")] |
| 161 | + [TestCase(623537928003000000, DateTimeKind.Utc, ExpectedResult = "1976-11-30T10:00:00.3000000Z")] |
| 162 | + [TestCase(623537928003000000, DateTimeKind.Unspecified, ExpectedResult = "1976-11-30T10:00:00.3000000")] |
| 163 | + [TestCase(623537928003000000, DateTimeKind.Local, ExpectedResult = "1976-11-30T10:00:00.3000000+01:00", |
| 164 | + Ignore = "Offset depends on which system this test is run and can currently now be influenced via the .net framework", |
| 165 | + Description ="This test will ONLY succeed when the test is run on a system which if currently in a timezone with offset +01:00")] |
| 166 | + public string ExpectedToStringDotnetFrameworkBehavior(long ticks, DateTimeKind kind) |
| 167 | + { |
| 168 | + return new DateTime(ticks, kind).ToString("o"); |
| 169 | + } |
| 170 | + |
| 171 | + /// <summary> |
| 172 | + /// Test the framework tostring behavior. If the test fails then the <see cref="TimestampType"/> and <see cref="TimestampUtcType"/> implemention could not work propertly at run-time. |
| 173 | + /// </summary> |
| 174 | + [Test, Category("Expected framework behavior")] |
| 175 | + public void ExpectedIsEqualDotnetFrameworkBehavior() |
| 176 | + { |
| 177 | + const string assertMessage = "Values should be equal dotnet framework ignores Kind value."; |
| 178 | + Assert.AreEqual(_utc, _local, assertMessage); |
| 179 | + Assert.AreEqual(_utc, _unspecified, assertMessage); |
| 180 | + Assert.AreEqual(_unspecified, _local, assertMessage); |
| 181 | + } |
| 182 | + } |
| 183 | +} |
0 commit comments