Skip to content

Commit 0ba96aa

Browse files
committed
Fix Linq .Second bug for PostgreSQL et al.
Refactor tests
1 parent 0c9e442 commit 0ba96aa

File tree

7 files changed

+438
-92
lines changed

7 files changed

+438
-92
lines changed

src/NHibernate.Test/Async/Linq/DateTimeTests.cs

+206-43
Original file line numberDiff line numberDiff line change
@@ -9,98 +9,261 @@
99

1010

1111
using System;
12+
using System.Data;
1213
using System.Linq;
14+
using NHibernate.Cfg;
15+
using NHibernate.SqlTypes;
16+
using NHibernate.Mapping.ByCode;
1317
using NUnit.Framework;
18+
using NHibernate.Type;
1419
using NHibernate.Linq;
20+
using System.Linq.Expressions;
1521

1622
namespace NHibernate.Test.Linq
1723
{
1824
using System.Threading.Tasks;
25+
using System.Threading;
1926
[TestFixture]
20-
public class DateTimeTestsAsync : LinqTestCase
27+
public class DateTimeTestsAsync : TestCase
2128
{
29+
private bool DialectSupportsDateTimeOffset => TestDialect.SupportsSqlType(new SqlType(DbType.DateTimeOffset));
30+
private bool DialectSupportsDateTimeWithScale => TestDialect.SupportsSqlType(new SqlType(DbType.DateTime,(byte) 2));
31+
private readonly DateTimeTestsClass[] _referenceEntities =
32+
[
33+
new() {Id =1, DateTimeValue = new DateTime(1998, 02, 26)},
34+
new() {Id =2, DateTimeValue = new DateTime(1998, 02, 26)},
35+
new() {Id =3, DateTimeValue = new DateTime(1998, 02, 26, 01, 01, 01)},
36+
new() {Id =4, DateTimeValue = new DateTime(1998, 02, 26, 02, 02, 02)},
37+
new() {Id =5, DateTimeValue = new DateTime(1998, 02, 26, 03, 03, 03)},
38+
new() {Id =6, DateTimeValue = new DateTime(1998, 02, 26, 04, 04, 04)},
39+
new() {Id =7, DateTimeValue = new DateTime(1998, 03, 01)},
40+
new() {Id =8, DateTimeValue = new DateTime(2000, 01, 01)}
41+
];
42+
43+
protected override string[] Mappings => default;
44+
protected override void AddMappings(Configuration configuration)
45+
{
46+
var modelMapper = new ModelMapper();
47+
48+
modelMapper.Class<DateTimeTestsClass>(m =>
49+
{
50+
m.Table("datetimetests");
51+
m.Lazy(false);
52+
m.Id(p => p.Id, p => p.Generator(Generators.Assigned));
53+
m.Property(p => p.DateValue, c => c.Type<DateType>());
54+
m.Property(p => p.DateTimeValue);
55+
m.Property(p => p.DateTimeValueWithScale, c => c.Scale(2));
56+
if (DialectSupportsDateTimeOffset)
57+
{
58+
m.Property(p => p.DateTimeOffsetValue);
59+
m.Property(p => p.DateTimeOffsetValueWithScale, c => c.Scale(2));
60+
}
61+
});
62+
var mapping = modelMapper.CompileMappingForAllExplicitlyAddedEntities();
63+
configuration.AddMapping(mapping);
64+
}
65+
66+
protected override void OnSetUp()
67+
{
68+
foreach (var entity in _referenceEntities)
69+
{
70+
entity.DateValue = entity.DateTimeValue.Date;
71+
entity.DateTimeValueWithScale = entity.DateTimeValue.AddSeconds(0.9);
72+
entity.DateTimeOffsetValue = new DateTimeOffset(entity.DateTimeValue, TimeSpan.FromHours(3));
73+
entity.DateTimeOffsetValueWithScale = new DateTimeOffset(entity.DateTimeValue, TimeSpan.FromHours(3));
74+
}
75+
76+
using (var session = OpenSession())
77+
using (var trans = session.BeginTransaction())
78+
{
79+
foreach (var entity in _referenceEntities)
80+
{
81+
session.Save(entity);
82+
}
83+
trans.Commit();
84+
}
85+
}
86+
87+
protected override void OnTearDown()
88+
{
89+
using (var session = OpenSession())
90+
using (var trans = session.BeginTransaction())
91+
{
92+
session.Query<DateTimeTestsClass>().Delete();
93+
trans.Commit();
94+
}
95+
}
96+
97+
private void AssertDateTimeOffsetSupported()
98+
{
99+
if (!DialectSupportsDateTimeOffset)
100+
{
101+
Assert.Ignore("Dialect doesn't support DateTimeOffset");
102+
}
103+
}
104+
105+
private async Task AssertDateTimeWithScaleSupportedAsync(CancellationToken cancellationToken = default(CancellationToken))
106+
{
107+
if (!DialectSupportsDateTimeWithScale)
108+
{
109+
Assert.Ignore("Dialect doesn't support DateTime with scale (2)");
110+
}
111+
using (var session = OpenSession())
112+
using (var trans = session.BeginTransaction())
113+
{
114+
var entity1 = await (session.GetAsync<DateTimeTestsClass>(_referenceEntities[0].Id, cancellationToken));
115+
if (entity1.DateTimeValueWithScale != entity1.DateTimeValue.AddSeconds(0.9))
116+
{
117+
Assert.Ignore("Current setup doesn't support DateTime with scale (2)");
118+
}
119+
}
120+
121+
}
122+
123+
private Task AssertQueryAsync(Expression<Func<DateTimeTestsClass, bool>> where, CancellationToken cancellationToken = default(CancellationToken)) => AssertQueryAsync(where, x => x.Id, cancellationToken);
124+
125+
private async Task AssertQueryAsync<TSelect>(Expression<Func<DateTimeTestsClass, bool>> where, Expression<Func<DateTimeTestsClass, TSelect>> select, CancellationToken cancellationToken = default(CancellationToken))
126+
{
127+
using var session = OpenSession();
128+
var fromDb = await (session.Query<DateTimeTestsClass>().Where(where).Select(select).ToListAsync(cancellationToken));
129+
var fromMemory = _referenceEntities.AsQueryable().Where(where).Select(select).AsEnumerable().ToList(); //AsEnumerable added to avoid async generator
130+
Assert.That(fromMemory, Has.Count.GreaterThan(0), "Inconclusive, since the query doesn't match anything in the defined set");
131+
Assert.That(fromDb, Has.Count.EqualTo(fromMemory.Count));
132+
Assert.That(fromDb, Is.EquivalentTo(fromMemory));
133+
}
134+
22135
[Test]
23136
public async Task CanQueryByYearAsync()
24137
{
25-
var x = await ((from o in db.Orders
26-
where o.OrderDate.Value.Year == 1998
27-
select o).ToListAsync());
138+
await (AssertQueryAsync(o => o.DateTimeValue.Year == 1998));
139+
}
28140

29-
Assert.AreEqual(270, x.Count());
141+
[Test]
142+
public async Task CanQueryDateTimeBySecondAsync()
143+
{
144+
await (AssertQueryAsync(o => o.DateTimeValue.Second == 4));
30145
}
31146

32147
[Test]
33-
public async Task CanQueryByDateAsync()
148+
public async Task CanQueryDateTimeByMinuteAsync()
34149
{
35-
var x = await ((from o in db.Orders
36-
where o.OrderDate.Value.Date == new DateTime(1998, 02, 26)
37-
select o).ToListAsync());
150+
await (AssertQueryAsync(o => o.DateTimeValue.Minute == 4));
151+
}
38152

39-
Assert.AreEqual(6, x.Count());
153+
[Test]
154+
public async Task CanQueryDateTimeByHourAsync()
155+
{
156+
await (AssertQueryAsync(o => o.DateTimeValue.Hour == 4));
40157
}
41158

42159
[Test]
43-
public async Task CanQueryByDateTimeAsync()
160+
public async Task CanQueryDateTimeBySecondWhenValueContainsFractionalSecondsAsync()
44161
{
45-
var x = await ((from o in db.Orders
46-
where o.OrderDate.Value == new DateTime(1998, 02, 26)
47-
select o).ToListAsync());
162+
await (AssertDateTimeWithScaleSupportedAsync());
163+
await (AssertQueryAsync(o => o.DateTimeValueWithScale.Second == 4));
164+
}
48165

49-
Assert.AreEqual(5, x.Count());
166+
[Test]
167+
public async Task CanQueryDateTimeOffsetBySecondAsync()
168+
{
169+
AssertDateTimeOffsetSupported();
170+
await (AssertQueryAsync(o => o.DateTimeOffsetValue.Second == 4));
50171
}
51172

52173
[Test]
53-
public async Task CanQueryByDateTime2Async()
174+
public async Task CanQueryDateTimeOffsetByMinuteAsync()
54175
{
55-
var x = await ((from o in db.Orders
56-
where o.OrderDate.Value == new DateTime(1998, 02, 26, 0, 1, 0)
57-
select o).ToListAsync());
176+
AssertDateTimeOffsetSupported();
177+
await (AssertQueryAsync(o => o.DateTimeOffsetValue.Minute == 4));
178+
}
58179

59-
Assert.AreEqual(1, x.Count());
180+
[Test]
181+
public async Task CanQueryDateTimeOffsetByHourAsync()
182+
{
183+
AssertDateTimeOffsetSupported();
184+
await (AssertQueryAsync(o => o.DateTimeOffsetValue.Hour == 4));
60185
}
61186

62187
[Test]
63-
public async Task CanSelectYearAsync()
188+
public async Task CanQueryDateTimeOffsetBySecondWhenValueContainsFractionalSecondsAsync()
64189
{
65-
var x = await ((from o in db.Orders
66-
where o.OrderDate.Value.Year == 1998
67-
select o.OrderDate.Value.Year).ToListAsync());
190+
AssertDateTimeOffsetSupported();
191+
await (AssertQueryAsync(o => o.DateTimeOffsetValueWithScale.Second == 4));
192+
}
68193

69-
Assert.That(x, Has.All.EqualTo(1998));
70-
Assert.AreEqual(270, x.Count());
194+
[Test]
195+
public async Task CanQueryByDateAsync()
196+
{
197+
await (AssertQueryAsync(o => o.DateTimeValue.Date == new DateTime(1998, 02, 26)));
71198
}
72199

73200
[Test]
74-
public async Task CanSelectDateAsync()
201+
public async Task CanQueryByDateTimeAsync()
75202
{
76-
var x = await ((from o in db.Orders
77-
where o.OrderDate.Value.Date == new DateTime(1998, 02, 26)
78-
select o.OrderDate.Value.Date).ToListAsync());
203+
await (AssertQueryAsync(o => o.DateTimeValue == new DateTime(1998, 02, 26)));
204+
}
79205

80-
Assert.That(x, Has.All.EqualTo(new DateTime(1998, 02, 26)));
81-
Assert.AreEqual(6, x.Count());
206+
[Test]
207+
public async Task CanQueryByDateTime2Async()
208+
{
209+
await (AssertQueryAsync(o => o.DateTimeValue == new DateTime(1998, 02, 26, 1, 1, 1)));
82210
}
83211

84212
[Test]
85-
public async Task CanSelectDateTimeAsync()
213+
public async Task CanSelectYearAsync()
214+
{
215+
await (AssertQueryAsync(o => o.DateTimeValue.Year == 1998, o => o.DateTimeValue.Year));
216+
}
217+
218+
[Test]
219+
public async Task CanSelectDateAsync()
86220
{
87-
var x = await ((from o in db.Orders
88-
where o.OrderDate.Value == new DateTime(1998, 02, 26)
89-
select o.OrderDate.Value).ToListAsync());
221+
await (AssertQueryAsync(o => o.DateTimeValue.Date == new DateTime(1998, 02, 26), o => o.DateTimeValue.Date));
222+
}
90223

91-
Assert.That(x, Has.All.EqualTo(new DateTime(1998, 02, 26)));
92-
Assert.AreEqual(5, x.Count());
224+
[Test]
225+
public async Task CanSelectDateTimeAsync()
226+
{
227+
await (AssertQueryAsync(o => o.DateTimeValue == new DateTime(1998, 02, 26), o => o.DateTimeValue));
93228
}
94229

95230
[Test]
96231
public async Task CanSelectDateTime2Async()
97232
{
98-
var x = await ((from o in db.Orders
99-
where o.OrderDate.Value == new DateTime(1998, 02, 26, 0, 1, 0)
100-
select o.OrderDate.Value).ToListAsync());
233+
await (AssertQueryAsync(o => o.DateTimeValue == new DateTime(1998, 02, 26, 1, 1, 1), o => o.DateTimeValue));
234+
}
235+
236+
[Test]
237+
public async Task CanSelectDateTimeWithScaleAsync()
238+
{
239+
await (AssertDateTimeWithScaleSupportedAsync());
240+
await (AssertQueryAsync(o => o.DateTimeValueWithScale == _referenceEntities[0].DateTimeValueWithScale, o => o.DateTimeValueWithScale));
241+
}
242+
243+
public class DateTimeTestsClass : IEquatable<DateTimeTestsClass>
244+
{
245+
public int Id { get; set; }
246+
public DateTime DateTimeValue { get; set; }
247+
public DateTime DateTimeValueWithScale { get; set; }
248+
public DateTimeOffset DateTimeOffsetValue { get; set; }
249+
public DateTimeOffset DateTimeOffsetValueWithScale { get; set; }
250+
public DateTime DateValue { get; set; }
251+
252+
public override bool Equals(object obj)
253+
{
254+
return Equals(obj as DateTimeTestsClass);
255+
}
256+
257+
public bool Equals(DateTimeTestsClass other)
258+
{
259+
return other is not null &&
260+
Id.Equals(other.Id);
261+
}
101262

102-
Assert.That(x, Has.All.EqualTo(new DateTime(1998, 02, 26, 0, 1, 0)));
103-
Assert.AreEqual(1, x.Count());
263+
public override int GetHashCode()
264+
{
265+
return HashCode.Combine(Id);
266+
}
104267
}
105268
}
106269
}

0 commit comments

Comments
 (0)