Skip to content

Commit d27a04d

Browse files
committed
Add tests and some TODOs
1 parent 1157255 commit d27a04d

File tree

4 files changed

+223
-9
lines changed

4 files changed

+223
-9
lines changed

EntityFramework.Exceptions.Tests/DatabaseTests.cs

Lines changed: 188 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.EntityFrameworkCore;
55
using MySql.EntityFrameworkCore.Extensions;
66
using System;
7+
using System.Linq;
78
using System.Threading.Tasks;
89
using Xunit;
910

@@ -21,7 +22,8 @@ protected DatabaseTests(DemoContext demoContext, SameNameIndexesContext sameName
2122
DemoContext = demoContext;
2223
SameNameIndexesContext = sameNameIndexesContext;
2324

24-
isMySql = MySqlDatabaseFacadeExtensions.IsMySql(DemoContext.Database) || MySQLDatabaseFacadeExtensions.IsMySql(DemoContext.Database);
25+
isMySql = MySqlDatabaseFacadeExtensions.IsMySql(DemoContext.Database) ||
26+
MySQLDatabaseFacadeExtensions.IsMySql(DemoContext.Database);
2527
isSqlite = demoContext.Database.IsSqlite();
2628
}
2729

@@ -43,6 +45,24 @@ public virtual async Task UniqueColumnViolationThrowsUniqueConstraintException()
4345
}
4446
}
4547

48+
#if BULK_OPERATIONS
49+
[Fact]
50+
public virtual async Task UniqueColumnViolationThrowsUniqueConstraintExceptionThroughExecuteUpdate()
51+
{
52+
DemoContext.Products.Add(new Product { Name = "Bulk Update 1" });
53+
DemoContext.Products.Add(new Product { Name = "Bulk Update 2" });
54+
55+
await DemoContext.SaveChangesAsync();
56+
Assert.Throws<UniqueConstraintException>(() =>
57+
DemoContext.Products.ExecuteUpdate(p => p.SetProperty(pp => pp.Name, "Bulk Update 1")));
58+
await Assert.ThrowsAsync<UniqueConstraintException>(async () =>
59+
await DemoContext.Products.ExecuteUpdateAsync(p => p.SetProperty(pp => pp.Name, "Bulk Update 1")));
60+
await DemoContext.Products
61+
.Where(p => p.Name == "Bulk Update 1" || p.Name == "Bulk Update 2")
62+
.ExecuteDeleteAsync();
63+
}
64+
#endif
65+
4666
[Fact]
4767
public virtual async Task UniqueColumnViolationSameNamesIndexesInDifferentSchemasSetsCorrectTableName()
4868
{
@@ -56,13 +76,14 @@ public virtual async Task UniqueColumnViolationSameNamesIndexesInDifferentSchema
5676
{
5777
Name = "Rope Access"
5878
});
59-
79+
6080
SameNameIndexesContext.IncidentCategories.Add(new EFExceptionSchema.Entities.Incidents.Category
6181
{
6282
Name = "Rope Access"
6383
});
6484

65-
var uniqueConstraintException = Assert.Throws<UniqueConstraintException>(() => SameNameIndexesContext.SaveChanges());
85+
var uniqueConstraintException =
86+
Assert.Throws<UniqueConstraintException>(() => SameNameIndexesContext.SaveChanges());
6687
await Assert.ThrowsAsync<UniqueConstraintException>(() => SameNameIndexesContext.SaveChangesAsync());
6788

6889
if (!isSqlite)
@@ -94,7 +115,7 @@ public virtual async Task PrimaryKeyViolationThrowsUniqueConstraintException()
94115
Assert.False(string.IsNullOrEmpty(uniqueConstraintException.ConstraintName));
95116
Assert.False(string.IsNullOrEmpty(uniqueConstraintException.SchemaQualifiedTableName));
96117
Assert.NotEmpty(uniqueConstraintException.ConstraintProperties);
97-
Assert.Contains<string>(nameof(Product.Id), uniqueConstraintException.ConstraintProperties);
118+
Assert.Contains<string>(nameof(Product.Id), uniqueConstraintException.ConstraintProperties);
98119
}
99120
}
100121

@@ -107,6 +128,21 @@ public virtual async Task RequiredColumnViolationThrowsCannotInsertNullException
107128
await Assert.ThrowsAsync<CannotInsertNullException>(() => DemoContext.SaveChangesAsync());
108129
}
109130

131+
#if BULK_OPERATIONS
132+
[Fact]
133+
public virtual async Task RequiredColumnViolationThrowsCannotInsertNullExceptionThroughExecuteUpdate()
134+
{
135+
DemoContext.Products.Add(new Product { Name = "Bulk Update 1" });
136+
await DemoContext.SaveChangesAsync();
137+
138+
Assert.Throws<CannotInsertNullException>(() =>
139+
DemoContext.Products.ExecuteUpdate(p => p.SetProperty(pp => pp.Name, (string)null)));
140+
await Assert.ThrowsAsync<CannotInsertNullException>(async () =>
141+
await DemoContext.Products.ExecuteUpdateAsync(p => p.SetProperty(pp => pp.Name, (string)null)));
142+
await DemoContext.Products.Where(p => p.Name == "Bulk Update 1").ExecuteDeleteAsync();
143+
}
144+
#endif
145+
110146
[Fact]
111147
public virtual async Task MaxLengthViolationThrowsMaxLengthExceededException()
112148
{
@@ -116,6 +152,42 @@ public virtual async Task MaxLengthViolationThrowsMaxLengthExceededException()
116152
await Assert.ThrowsAsync<MaxLengthExceededException>(() => DemoContext.SaveChangesAsync());
117153
}
118154

155+
#if BULK_OPERATIONS
156+
[Fact]
157+
public virtual async Task MaxLengthViolationThrowsMaxLengthExceededExceptionThroughExecuteUpdate()
158+
{
159+
DemoContext.Products.Add(new Product { Name = "Bulk Update 1" });
160+
await DemoContext.SaveChangesAsync();
161+
162+
CleanupContext();
163+
164+
Assert.Throws<MaxLengthExceededException>(Query);
165+
await Assert.ThrowsAsync<MaxLengthExceededException>(QueryAsync);
166+
167+
await DemoContext.Products
168+
.Where(p => p.Name == "Bulk Update 1")
169+
.ExecuteDeleteAsync();
170+
171+
return;
172+
173+
void Query()
174+
{
175+
DemoContext.Products
176+
.Where(p => p.Name == "Bulk Update 1")
177+
.ExecuteUpdate(p =>
178+
p.SetProperty(pp => pp.Name, new string('G', DemoContext.ProductNameMaxLength + 5)));
179+
}
180+
181+
async Task QueryAsync()
182+
{
183+
await DemoContext.Products
184+
.Where(p => p.Name == "Bulk Update 1")
185+
.ExecuteUpdateAsync(p =>
186+
p.SetProperty(pp => pp.Name, new string('G', DemoContext.ProductNameMaxLength + 5)));
187+
}
188+
}
189+
#endif
190+
119191
[Fact]
120192
public virtual async Task NumericOverflowViolationThrowsNumericOverflowException()
121193
{
@@ -127,6 +199,41 @@ public virtual async Task NumericOverflowViolationThrowsNumericOverflowException
127199
await Assert.ThrowsAsync<NumericOverflowException>(() => DemoContext.SaveChangesAsync());
128200
}
129201

202+
#if BULK_OPERATIONS
203+
[Fact]
204+
public virtual async Task NumericOverflowViolationThrowsNumericOverflowExceptionThroughExecuteUpdate()
205+
{
206+
var product = new Product { Name = "Numeric Overflow Test 2" };
207+
DemoContext.Products.Add(product);
208+
var sale = new ProductSale { Price = 1m, Product = product };
209+
DemoContext.ProductSales.Add(sale);
210+
await DemoContext.SaveChangesAsync();
211+
212+
Assert.Throws<NumericOverflowException>(Query);
213+
await Assert.ThrowsAsync<NumericOverflowException>(QueryAsync);
214+
215+
DemoContext.Remove(sale);
216+
DemoContext.Remove(product);
217+
await DemoContext.SaveChangesAsync();
218+
219+
return;
220+
221+
void Query()
222+
{
223+
DemoContext.ProductSales
224+
.Where(s => s.Id == sale.Id)
225+
.ExecuteUpdate(s => s.SetProperty(ss => ss.Price, 3141.59265m));
226+
}
227+
228+
async Task QueryAsync()
229+
{
230+
await DemoContext.ProductSales
231+
.Where(s => s.Id == sale.Id)
232+
.ExecuteUpdateAsync(s => s.SetProperty(ss => ss.Price, 3141.59265m));
233+
}
234+
}
235+
#endif
236+
130237
[Fact]
131238
public virtual async Task ReferenceViolationThrowsReferenceConstraintException()
132239
{
@@ -143,6 +250,48 @@ public virtual async Task ReferenceViolationThrowsReferenceConstraintException()
143250
}
144251
}
145252

253+
#if BULK_OPERATIONS
254+
[Fact]
255+
public virtual async Task ReferenceViolationThrowsReferenceConstraintExceptionThroughExecuteUpdate()
256+
{
257+
var product = new Product { Name = "RefConstraint Violation 1" };
258+
DemoContext.Products.Add(product);
259+
var sale = new ProductSale { Price = 1m, Product = product };
260+
DemoContext.ProductSales.Add(sale);
261+
await DemoContext.SaveChangesAsync();
262+
263+
var exception = Assert.Throws<ReferenceConstraintException>(Query);
264+
var asyncException = await Assert.ThrowsAsync<ReferenceConstraintException>(QueryAsync);
265+
266+
if (!isSqlite)
267+
{
268+
Assert.False(string.IsNullOrEmpty(exception.ConstraintName));
269+
Assert.NotEmpty(exception.ConstraintProperties);
270+
Assert.Contains<string>(nameof(ProductSale.ProductId), exception.ConstraintProperties);
271+
272+
Assert.False(string.IsNullOrEmpty(asyncException.ConstraintName));
273+
Assert.NotEmpty(asyncException.ConstraintProperties);
274+
Assert.Contains<string>(nameof(ProductSale.ProductId), asyncException.ConstraintProperties);
275+
}
276+
277+
return;
278+
279+
void Query()
280+
{
281+
DemoContext.ProductSales
282+
.Where(s => s.Id == sale.Id)
283+
.ExecuteUpdate(s => s.SetProperty(ss => ss.ProductId, 0));
284+
}
285+
286+
async Task QueryAsync()
287+
{
288+
await DemoContext.ProductSales
289+
.Where(s => s.Id == sale.Id)
290+
.ExecuteUpdateAsync(s => s.SetProperty(ss => ss.ProductId, 0));
291+
}
292+
}
293+
#endif
294+
146295
[Fact]
147296
public virtual async Task DatabaseUnrelatedExceptionThrowsOriginalException()
148297
{
@@ -163,7 +312,8 @@ public virtual async Task DatabaseUnrelatedExceptionThrowsOriginalException()
163312
public virtual async Task DeleteParentItemThrowsReferenceConstraintException()
164313
{
165314
var product = new Product { Name = "AN" };
166-
var productPriceHistory = new ProductPriceHistory { Product = product, Price = 15.27m, EffectiveDate = DateTimeOffset.UtcNow };
315+
var productPriceHistory = new ProductPriceHistory
316+
{ Product = product, Price = 15.27m, EffectiveDate = DateTimeOffset.UtcNow };
167317
DemoContext.ProductPriceHistories.Add(productPriceHistory);
168318
await DemoContext.SaveChangesAsync();
169319

@@ -176,6 +326,39 @@ public virtual async Task DeleteParentItemThrowsReferenceConstraintException()
176326
await Assert.ThrowsAsync<ReferenceConstraintException>(() => DemoContext.SaveChangesAsync());
177327
}
178328

329+
#if BULK_OPERATIONS
330+
[Fact]
331+
public virtual async Task DeleteParentItemThrowsReferenceConstraintExceptionThroughExecuteDelete()
332+
{
333+
var product = new Product { Name = "AN2" };
334+
var productPriceHistory = new ProductPriceHistory
335+
{ Product = product, Price = 15.27m, EffectiveDate = DateTimeOffset.UtcNow };
336+
DemoContext.ProductPriceHistories.Add(productPriceHistory);
337+
await DemoContext.SaveChangesAsync();
338+
339+
CleanupContext();
340+
341+
Assert.Throws<ReferenceConstraintException>(Query);
342+
await Assert.ThrowsAsync<ReferenceConstraintException>(QueryAsync);
343+
344+
return;
345+
346+
void Query()
347+
{
348+
DemoContext.Products
349+
.Where(p => p.Name == "AN2")
350+
.ExecuteDelete();
351+
}
352+
353+
async Task QueryAsync()
354+
{
355+
await DemoContext.Products
356+
.Where(p => p.Name == "AN2")
357+
.ExecuteDeleteAsync();
358+
}
359+
}
360+
#endif
361+
179362
[Fact]
180363
public async Task NotHandledViolationReThrowsOriginalException()
181364
{

EntityFramework.Exceptions.Tests/OracleTests.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using EntityFramework.Exceptions.Oracle;
1+
using System.Threading.Tasks;
2+
using EntityFramework.Exceptions.Oracle;
23
using Microsoft.EntityFrameworkCore;
34
using Testcontainers.Oracle;
45
using Xunit;
@@ -10,6 +11,15 @@ public class OracleTests : DatabaseTests, IClassFixture<OracleTestContextFixture
1011
public OracleTests(OracleTestContextFixture fixture) : base(fixture.DemoContext)
1112
{
1213
}
14+
15+
#if BULK_OPERATIONS
16+
// TODO support ORA-01407: cannot update (string) to NULL
17+
[Fact(Skip = "Oracle has differing error codes for inserting null and updating to null")]
18+
public override Task RequiredColumnViolationThrowsCannotInsertNullExceptionThroughExecuteUpdate()
19+
{
20+
return Task.CompletedTask;
21+
}
22+
#endif
1323
}
1424

1525
public class OracleTestContextFixture : DemoContextFixture<OracleContainer>

EntityFramework.Exceptions.Tests/SqliteTests.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,33 @@ public override Task NumericOverflowViolationThrowsNumericOverflowException()
4040
{
4141
return Task.CompletedTask;
4242
}
43+
44+
#if BULK_OPERATIONS
45+
// TODO figure this out
46+
[Fact(Skip = "Skipping because SQLite appears to not check this")]
47+
public override Task MaxLengthViolationThrowsMaxLengthExceededExceptionThroughExecuteUpdate()
48+
{
49+
return Task.CompletedTask;
50+
}
51+
52+
[Fact(Skip = "Skipping as SQLite does not enforce numeric length")]
53+
public override Task NumericOverflowViolationThrowsNumericOverflowExceptionThroughExecuteUpdate()
54+
{
55+
return Task.CompletedTask;
56+
}
57+
#endif
4358
}
4459

4560
public class SqliteDemoContextFixture : DemoContextFixture<IContainer>
4661
{
4762
private const string ConnectionString = "DataSource=file::memory:?cache=shared";
4863

49-
protected override DbContextOptionsBuilder<DemoContext> BuildDemoContextOptions(DbContextOptionsBuilder<DemoContext> builder, string connectionString)
64+
protected override DbContextOptionsBuilder<DemoContext> BuildDemoContextOptions(
65+
DbContextOptionsBuilder<DemoContext> builder, string connectionString)
5066
=> builder.UseSqlite(ConnectionString).UseExceptionProcessor();
5167

52-
protected override DbContextOptionsBuilder BuildSameNameIndexesContextOptions(DbContextOptionsBuilder builder, string connectionString)
68+
protected override DbContextOptionsBuilder BuildSameNameIndexesContextOptions(DbContextOptionsBuilder builder,
69+
string connectionString)
5370
=> builder.UseSqlite(ConnectionString).UseExceptionProcessor();
5471
}
55-
}
72+
}

EntityFramework.Exceptions.Tests/Tests.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
<PackageReference Include="Oracle.EntityFrameworkCore" Version="8.21.121" />
4949
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
5050
</ItemGroup>
51+
52+
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0'">
53+
<DefineConstants>BULK_OPERATIONS</DefineConstants>
54+
</PropertyGroup>
5155

5256
<ItemGroup>
5357
<ProjectReference Include="..\EntityFramework.Exceptions.MySQL.Pomelo\MySQL.Pomelo.csproj" />

0 commit comments

Comments
 (0)