Skip to content

Commit af2d7b8

Browse files
authored
Only run exact cosmos EntityTooLarge tests on a real cosmos and add additional tests for emulator (#37028)
* Remove flaky tests for cosmos db and replace with slightly different tests * Readd tests that only succeed on emulator and make them only succeed on a real cosmos db with CosmosCondition.IsNotEmulator
1 parent ab1973d commit af2d7b8

File tree

1 file changed

+89
-42
lines changed

1 file changed

+89
-42
lines changed

test/EFCore.Cosmos.FunctionalTests/CosmosTransactionalBatchTest.cs

Lines changed: 89 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,24 @@ public virtual async Task SaveChanges_too_large_entry_after_smaller_throws_after
360360
Assert.Equal("1", (await assertContext.Customers.FirstAsync()).Id);
361361
}
362362

363+
[ConditionalFact]
364+
public virtual async Task SaveChanges_transaction_behaviour_always_payload_exactly_2_mib()
365+
{
366+
var contextFactory = await InitializeAsync<TransactionalBatchContext>();
367+
368+
using var context = contextFactory.CreateContext();
369+
context.Database.AutoTransactionBehavior = AutoTransactionBehavior.Always;
370+
371+
context.Customers.Add(new Customer { Id = "1", Name = new string('x', 1048291), PartitionKey = "1" });
372+
context.Customers.Add(new Customer { Id = "2", Name = new string('x', 1048291), PartitionKey = "1" });
373+
374+
await context.SaveChangesAsync();
375+
376+
using var assertContext = contextFactory.CreateContext();
377+
var customersCount = await assertContext.Customers.CountAsync();
378+
Assert.Equal(2, customersCount);
379+
}
380+
363381
[ConditionalFact]
364382
public virtual async Task SaveChanges_transaction_behaviour_always_payload_larger_than_cosmos_limit_throws()
365383
{
@@ -381,80 +399,106 @@ public virtual async Task SaveChanges_transaction_behaviour_always_payload_large
381399
Assert.Equal(0, customersCount);
382400
}
383401

384-
// The tests below will fail if the cosmos db sdk is updated and the serialization logic for transactional batches has changed
402+
private const int nameLengthToExceed2MiBWithSpecialCharIdOnUpdate = 1046358;
385403

386404
[ConditionalTheory, InlineData(true), InlineData(false)]
387-
public virtual async Task SaveChanges_transaction_behaviour_always_single_entity_payload_can_be_exactly_cosmos_limit_and_throws_when_1byte_over(bool oneByteOver)
405+
public virtual async Task SaveChanges_update_id_contains_special_chars_which_makes_request_larger_than_2_mib_splits_into_2_batches(bool isIdSpecialChar)
388406
{
389407
var contextFactory = await InitializeAsync<TransactionalBatchContext>();
390-
408+
391409
using var context = contextFactory.CreateContext();
392-
context.Database.AutoTransactionBehavior = AutoTransactionBehavior.Always;
393410

394-
var customer = new Customer { Id = new string('x', 1_000), PartitionKey = new string('x', 1_000) };
411+
var id1 = isIdSpecialChar ? new string('€', 341) : new string('x', 341);
412+
var id2 = isIdSpecialChar ? new string('Ω', 341) : new string('y', 341);
413+
414+
var customer1 = new Customer { Id = id1, PartitionKey = new string('€', 341) };
415+
var customer2 = new Customer { Id = id2, PartitionKey = new string('€', 341) };
416+
417+
context.Customers.Add(customer1);
418+
context.Customers.Add(customer2);
395419

396-
context.Customers.Add(customer);
397420
await context.SaveChangesAsync();
421+
ListLoggerFactory.Clear();
398422

399-
// Total document size will be: 2_097_503. Total request size will be: 2_098_541
400-
// Normally 2MiB is 2_097_152, but cosmos appears to allow ~1Kib (1389 bytes) extra
401-
var str = new string('x', 2_095_228);
402-
customer.Name = str;
423+
customer1.Name = new string('x', nameLengthToExceed2MiBWithSpecialCharIdOnUpdate);
424+
customer2.Name = new string('x', nameLengthToExceed2MiBWithSpecialCharIdOnUpdate);
403425

404-
if (oneByteOver)
426+
await context.SaveChangesAsync();
427+
using var assertContext = contextFactory.CreateContext();
428+
Assert.Equal(2, (await context.Customers.ToListAsync()).Count);
429+
430+
// The id being a special character should make the difference whether this fits in 1 batch.
431+
if (isIdSpecialChar)
405432
{
406-
customer.Name += 'x';
407-
var ex = await Assert.ThrowsAsync<DbUpdateException>(() => context.SaveChangesAsync());
408-
Assert.IsType<CosmosException>(ex.InnerException);
433+
Assert.Equal(2, ListLoggerFactory.Log.Count(x => x.Id == CosmosEventId.ExecutedTransactionalBatch));
409434
}
410435
else
411436
{
412-
await context.SaveChangesAsync();
413-
414-
using var assertContext = contextFactory.CreateContext();
415-
var dbCustomer = await assertContext.Customers.FirstAsync();
416-
Assert.Equal(dbCustomer.Name, str);
437+
Assert.Equal(1, ListLoggerFactory.Log.Count(x => x.Id == CosmosEventId.ExecutedTransactionalBatch));
417438
}
418439
}
419440

420441
[ConditionalTheory, InlineData(true), InlineData(false)]
421-
public virtual async Task SaveChanges_update_id_contains_special_chars_which_makes_request_larger_than_2_mib_splits_into_2_batches(bool isIdSpecialChar)
442+
public virtual async Task SaveChanges_create_id_contains_special_chars_which_would_make_request_larger_than_2_mib_on_update_does_not_split_into_2_batches_for_create(bool isIdSpecialChar)
422443
{
423444
var contextFactory = await InitializeAsync<TransactionalBatchContext>();
424445

425446
using var context = contextFactory.CreateContext();
426447

427-
string id1 = isIdSpecialChar ? new string('€', 341) : new string('x', 341);
428-
string id2 = isIdSpecialChar ? new string('Ω', 341) : new string('y', 341);
448+
var id1 = isIdSpecialChar ? new string('€', 341) : new string('x', 341);
449+
var id2 = isIdSpecialChar ? new string('Ω', 341) : new string('y', 341);
429450

430-
var customer1 = new Customer { Id = id1, PartitionKey = new string('€', 341) };
431-
var customer2 = new Customer { Id = id2, PartitionKey = new string('€', 341) };
451+
var customer1 = new Customer { Id = id1, Name = new string('x', nameLengthToExceed2MiBWithSpecialCharIdOnUpdate), PartitionKey = new string('€', 341) };
452+
var customer2 = new Customer { Id = id2, Name = new string('x', nameLengthToExceed2MiBWithSpecialCharIdOnUpdate), PartitionKey = new string('€', 341) };
432453

433454
context.Customers.Add(customer1);
434455
context.Customers.Add(customer2);
435456

436457
await context.SaveChangesAsync();
437-
ListLoggerFactory.Clear();
458+
using var assertContext = contextFactory.CreateContext();
459+
Assert.Equal(2, (await context.Customers.ToListAsync()).Count);
460+
461+
// The id being a special character should not make the difference whether this fits in 1 batch, as id is duplicated in the payload on create.
462+
Assert.Equal(1, ListLoggerFactory.Log.Count(x => x.Id == CosmosEventId.ExecutedTransactionalBatch));
463+
}
464+
465+
[ConditionalTheory, InlineData(true), InlineData(false)]
466+
[CosmosCondition(CosmosCondition.IsNotEmulator)]
467+
public virtual async Task SaveChanges_transaction_behaviour_always_single_entity_payload_can_be_exactly_cosmos_limit_and_throws_when_1byte_over(bool oneByteOver)
468+
{
469+
var contextFactory = await InitializeAsync<TransactionalBatchContext>();
470+
471+
using var context = contextFactory.CreateContext();
472+
context.Database.AutoTransactionBehavior = AutoTransactionBehavior.Always;
438473

439-
customer1.Name = new string('x', 1046358);
440-
customer2.Name = new string('x', 1046358);
474+
var customer = new Customer { Id = new string('x', 1_000), PartitionKey = new string('x', 1_000) };
441475

476+
context.Customers.Add(customer);
442477
await context.SaveChangesAsync();
443-
using var assertContext = contextFactory.CreateContext();
444-
Assert.Equal(2, (await context.Customers.ToListAsync()).Count);
445478

446-
// The id being a special character should make the difference whether this fits in 1 batch.
447-
if (isIdSpecialChar)
479+
// Total document size will be: 2_097_510. Total request size will be: 2_098_548
480+
// Normally 2MiB is 2_097_152, but cosmos appears to allow ~1Kib (1396 bytes) extra
481+
var str = new string('x', 2_095_235);
482+
customer.Name = str;
483+
484+
if (oneByteOver)
448485
{
449-
Assert.Equal(2, ListLoggerFactory.Log.Count(x => x.Id == CosmosEventId.ExecutedTransactionalBatch));
486+
customer.Name += 'x';
487+
var ex = await Assert.ThrowsAsync<DbUpdateException>(() => context.SaveChangesAsync());
488+
Assert.IsType<CosmosException>(ex.InnerException);
450489
}
451490
else
452491
{
453-
Assert.Equal(1, ListLoggerFactory.Log.Count(x => x.Id == CosmosEventId.ExecutedTransactionalBatch));
492+
await context.SaveChangesAsync();
493+
494+
using var assertContext = contextFactory.CreateContext();
495+
var dbCustomer = await assertContext.Customers.FirstAsync();
496+
Assert.Equal(dbCustomer.Name, str);
454497
}
455498
}
456499

457500
[ConditionalTheory, InlineData(true), InlineData(false)]
501+
[CosmosCondition(CosmosCondition.IsNotEmulator)]
458502
public virtual async Task SaveChanges_transaction_behaviour_always_update_entities_payload_can_be_exactly_cosmos_limit_and_throws_when_1byte_over(bool oneByteOver)
459503
{
460504
var contextFactory = await InitializeAsync<TransactionalBatchContext>();
@@ -470,8 +514,8 @@ public virtual async Task SaveChanges_transaction_behaviour_always_update_entiti
470514

471515
await context.SaveChangesAsync();
472516

473-
customer1.Name = new string('x', 1097582);
474-
customer2.Name = new string('x', 1097583);
517+
customer1.Name = new string('x', 1097589);
518+
customer2.Name = new string('x', 1097590);
475519

476520
if (oneByteOver)
477521
{
@@ -486,6 +530,7 @@ public virtual async Task SaveChanges_transaction_behaviour_always_update_entiti
486530
}
487531

488532
[ConditionalTheory, InlineData(true), InlineData(false)]
533+
[CosmosCondition(CosmosCondition.IsNotEmulator)]
489534
public virtual async Task SaveChanges_id_counts_double_toward_request_size_on_update(bool oneByteOver)
490535
{
491536
var contextFactory = await InitializeAsync<TransactionalBatchContext>();
@@ -495,14 +540,14 @@ public virtual async Task SaveChanges_id_counts_double_toward_request_size_on_up
495540

496541
var customer1 = new Customer { Id = new string('x', 1), PartitionKey = new string('x', 1_023) };
497542
var customer2 = new Customer { Id = new string('y', 1_023), PartitionKey = new string('x', 1_023) };
498-
543+
499544
context.Customers.Add(customer1);
500545
context.Customers.Add(customer2);
501546

502547
await context.SaveChangesAsync();
503548

504-
customer1.Name = new string('x', 1097582 + 1_022 * 2 + 1);
505-
customer2.Name = new string('x', 1097583);
549+
customer1.Name = new string('x', 1097590 + 1_022 * 2);
550+
customer2.Name = new string('x', 1097590);
506551

507552
if (oneByteOver)
508553
{
@@ -517,15 +562,16 @@ public virtual async Task SaveChanges_id_counts_double_toward_request_size_on_up
517562
}
518563

519564
[ConditionalTheory, InlineData(true), InlineData(false)]
565+
[CosmosCondition(CosmosCondition.IsNotEmulator)]
520566
public virtual async Task SaveChanges_transaction_behaviour_always_create_entities_payload_can_be_exactly_cosmos_limit_and_throws_when_1byte_over(bool oneByteOver)
521567
{
522568
var contextFactory = await InitializeAsync<TransactionalBatchContext>();
523569

524570
using var context = contextFactory.CreateContext();
525571
context.Database.AutoTransactionBehavior = AutoTransactionBehavior.Always;
526572

527-
var customer1 = new Customer { Id = new string('x', 1_023), Name = new string('x', 1098841), PartitionKey = new string('x', 1_023) };
528-
var customer2 = new Customer { Id = new string('y', 1_023), Name = new string('x', 1098841), PartitionKey = new string('x', 1_023) };
573+
var customer1 = new Customer { Id = new string('x', 1_023), Name = new string('x', 1098848), PartitionKey = new string('x', 1_023) };
574+
var customer2 = new Customer { Id = new string('y', 1_023), Name = new string('x', 1098848), PartitionKey = new string('x', 1_023) };
529575
if (oneByteOver)
530576
{
531577
customer1.Name += 'x';
@@ -545,15 +591,16 @@ public virtual async Task SaveChanges_transaction_behaviour_always_create_entiti
545591
}
546592

547593
[ConditionalTheory, InlineData(true), InlineData(false)]
594+
[CosmosCondition(CosmosCondition.IsNotEmulator)]
548595
public virtual async Task SaveChanges_id_does_not_count_double_toward_request_size_on_create(bool oneByteOver)
549596
{
550597
var contextFactory = await InitializeAsync<TransactionalBatchContext>();
551598

552599
using var context = contextFactory.CreateContext();
553600
context.Database.AutoTransactionBehavior = AutoTransactionBehavior.Always;
554601

555-
var customer1 = new Customer { Id = new string('x', 1), Name = new string('x', 1098841 + 1_022), PartitionKey = new string('x', 1_023) };
556-
var customer2 = new Customer { Id = new string('y', 1_023), Name = new string('x', 1098841), PartitionKey = new string('x', 1_023) };
602+
var customer1 = new Customer { Id = new string('x', 1), Name = new string('x', 1098848 + 1_022), PartitionKey = new string('x', 1_023) };
603+
var customer2 = new Customer { Id = new string('y', 1_023), Name = new string('x', 1098848), PartitionKey = new string('x', 1_023) };
557604
if (oneByteOver)
558605
{
559606
customer1.Name += 'x';

0 commit comments

Comments
 (0)