mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-07 06:59:05 +00:00
Align the second test project with the site data-service contract
- Recording fake forwards the new site-code and open-work-order queries - Drop the LocalDB hard-delete test; sites are now tombstoned
This commit is contained in:
parent
c3865e56ac
commit
60b1afd8e0
2 changed files with 14 additions and 147 deletions
|
|
@ -5,7 +5,6 @@ using Microsoft.Data.SqlClient;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using SeaHaven.DataServices.Implementation;
|
|
||||||
|
|
||||||
namespace SeaHavenIndustries.Tests;
|
namespace SeaHavenIndustries.Tests;
|
||||||
|
|
||||||
|
|
@ -84,150 +83,6 @@ public class SH138SiteContactsSqlServerTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SH138_LocationDelete_DetachesSoftDeletedContacts_WithRestrictFk_WhenLocalDbAvailable()
|
|
||||||
{
|
|
||||||
var masterConnectionString = await ResolveMasterConnectionStringAsync();
|
|
||||||
if (masterConnectionString == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var dbName = $"SH138LocationDelete_{Guid.NewGuid():N}";
|
|
||||||
var connectionString = WithDatabase(masterConnectionString, dbName);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await CreateDatabaseAsync(masterConnectionString, dbName);
|
|
||||||
|
|
||||||
await using var connection = new SqlConnection(connectionString);
|
|
||||||
await connection.OpenAsync();
|
|
||||||
|
|
||||||
await using (var createTables = connection.CreateCommand())
|
|
||||||
{
|
|
||||||
createTables.CommandText =
|
|
||||||
"""
|
|
||||||
CREATE TABLE Locations (
|
|
||||||
Id int NOT NULL IDENTITY PRIMARY KEY,
|
|
||||||
AccountId int NULL,
|
|
||||||
Title nvarchar(max) NULL,
|
|
||||||
Name nvarchar(max) NULL,
|
|
||||||
Latitude nvarchar(max) NULL,
|
|
||||||
Longitude nvarchar(max) NULL,
|
|
||||||
Address1 nvarchar(max) NULL,
|
|
||||||
Address2 nvarchar(max) NULL,
|
|
||||||
City nvarchar(max) NULL,
|
|
||||||
State nvarchar(max) NULL,
|
|
||||||
Zip nvarchar(max) NULL,
|
|
||||||
PhoneNumber nvarchar(max) NULL,
|
|
||||||
Email nvarchar(max) NULL,
|
|
||||||
Status nvarchar(max) NULL,
|
|
||||||
ExternalSource nvarchar(max) NULL,
|
|
||||||
ExternalLocationId nvarchar(max) NULL,
|
|
||||||
IsDeleted bit NULL,
|
|
||||||
createdby nvarchar(max) NULL,
|
|
||||||
DeleterUserId nvarchar(max) NULL,
|
|
||||||
DeletionTime datetime2 NULL,
|
|
||||||
CreatedDate datetime2 NULL,
|
|
||||||
LastModificationTime datetime2 NULL,
|
|
||||||
LastModifierUserId int NULL
|
|
||||||
);
|
|
||||||
CREATE TABLE Contacts (
|
|
||||||
Id int NOT NULL IDENTITY PRIMARY KEY,
|
|
||||||
AccountId int NULL,
|
|
||||||
LocationId int NULL,
|
|
||||||
Title nvarchar(max) NULL,
|
|
||||||
Owner nvarchar(max) NULL,
|
|
||||||
FirstName nvarchar(max) NULL,
|
|
||||||
MiddleName nvarchar(max) NULL,
|
|
||||||
LastName nvarchar(max) NULL,
|
|
||||||
ContactType nvarchar(max) NULL,
|
|
||||||
PhoneNumber nvarchar(max) NULL,
|
|
||||||
Email nvarchar(max) NULL,
|
|
||||||
Address1 nvarchar(max) NULL,
|
|
||||||
Address2 nvarchar(max) NULL,
|
|
||||||
City nvarchar(max) NULL,
|
|
||||||
State nvarchar(max) NULL,
|
|
||||||
Zip nvarchar(max) NULL,
|
|
||||||
FacebookUrl nvarchar(max) NULL,
|
|
||||||
LinkedInUrl nvarchar(max) NULL,
|
|
||||||
TwitterUrl nvarchar(max) NULL,
|
|
||||||
IsDeleted bit NULL,
|
|
||||||
createdby nvarchar(max) NULL,
|
|
||||||
DeleterUserId nvarchar(max) NULL,
|
|
||||||
DeletionTime datetime2 NULL,
|
|
||||||
CreatedDate datetime2 NULL,
|
|
||||||
LastModificationTime datetime2 NULL,
|
|
||||||
LastModifierUserId int NULL,
|
|
||||||
SiteContactOrder int NULL,
|
|
||||||
CONSTRAINT FK_Contacts_Locations_LocationId FOREIGN KEY (LocationId) REFERENCES Locations (Id) ON DELETE NO ACTION
|
|
||||||
);
|
|
||||||
SET IDENTITY_INSERT Locations ON;
|
|
||||||
INSERT INTO Locations (Id, Name) VALUES (11, 'Depot');
|
|
||||||
SET IDENTITY_INSERT Locations OFF;
|
|
||||||
INSERT INTO Contacts (LocationId, FirstName, PhoneNumber, SiteContactOrder) VALUES (11, 'Alice Cooper', '555-0100', 0);
|
|
||||||
INSERT INTO Contacts (LocationId, FirstName, PhoneNumber, IsDeleted, DeleterUserId, DeletionTime) VALUES (11, 'Bob Dillon', '555-0200', 1, 'actor-1', '2026-01-01T00:00:00');
|
|
||||||
""";
|
|
||||||
await createTables.ExecuteNonQueryAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
|
||||||
.UseSqlServer(connection)
|
|
||||||
.Options;
|
|
||||||
|
|
||||||
await using var context = new ApplicationDbContext(options);
|
|
||||||
var service = new LocationDataService(context);
|
|
||||||
var deleted = await service.DeleteByIdAsync(11, CancellationToken.None);
|
|
||||||
|
|
||||||
Assert.True(deleted);
|
|
||||||
|
|
||||||
await using (var locationCmd = connection.CreateCommand())
|
|
||||||
{
|
|
||||||
locationCmd.CommandText = "SELECT COUNT(*) FROM Locations;";
|
|
||||||
var locationCount = await locationCmd.ExecuteScalarAsync();
|
|
||||||
Assert.Equal(0, Convert.ToInt32(locationCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
await using (var activeCmd = connection.CreateCommand())
|
|
||||||
{
|
|
||||||
activeCmd.CommandText =
|
|
||||||
"""
|
|
||||||
SELECT FirstName, PhoneNumber, LocationId, IsDeleted, DeleterUserId, DeletionTime
|
|
||||||
FROM Contacts
|
|
||||||
WHERE FirstName = N'Alice Cooper';
|
|
||||||
""";
|
|
||||||
await using var reader = await activeCmd.ExecuteReaderAsync();
|
|
||||||
Assert.True(await reader.ReadAsync());
|
|
||||||
Assert.Equal("Alice Cooper", reader.GetString(0));
|
|
||||||
Assert.Equal("555-0100", reader.GetString(1));
|
|
||||||
Assert.True(reader.IsDBNull(2));
|
|
||||||
Assert.True(reader.GetBoolean(3));
|
|
||||||
Assert.True(reader.IsDBNull(4), "delete carries no audit actor");
|
|
||||||
Assert.False(reader.IsDBNull(5));
|
|
||||||
Assert.False(await reader.ReadAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
await using (var previouslyDeletedCmd = connection.CreateCommand())
|
|
||||||
{
|
|
||||||
previouslyDeletedCmd.CommandText =
|
|
||||||
"""
|
|
||||||
SELECT LocationId, IsDeleted, DeleterUserId, DeletionTime
|
|
||||||
FROM Contacts
|
|
||||||
WHERE FirstName = N'Bob Dillon';
|
|
||||||
""";
|
|
||||||
await using var reader = await previouslyDeletedCmd.ExecuteReaderAsync();
|
|
||||||
Assert.True(await reader.ReadAsync());
|
|
||||||
Assert.True(reader.IsDBNull(0), "already soft-deleted rows detach so the restrict FK cannot block the delete");
|
|
||||||
Assert.True(reader.GetBoolean(1));
|
|
||||||
Assert.Equal("actor-1", reader.GetString(2));
|
|
||||||
Assert.Equal(new DateTime(2026, 1, 1), reader.GetDateTime(3));
|
|
||||||
Assert.False(await reader.ReadAsync());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await DropDatabaseAsync(masterConnectionString, dbName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task ApplyMigrationUpAsync(ApplicationDbContext context, Migration migration)
|
private static async Task ApplyMigrationUpAsync(ApplicationDbContext context, Migration migration)
|
||||||
{
|
{
|
||||||
var builder = new MigrationBuilder(context.Database.ProviderName!);
|
var builder = new MigrationBuilder(context.Database.ProviderName!);
|
||||||
|
|
|
||||||
|
|
@ -849,7 +849,19 @@ public class WorkOrderAccountScopeTests
|
||||||
=> _inner.AddAsync(location, cancellationToken);
|
=> _inner.AddAsync(location, cancellationToken);
|
||||||
public Task UpdateAsync(Locations location, CancellationToken cancellationToken)
|
public Task UpdateAsync(Locations location, CancellationToken cancellationToken)
|
||||||
=> _inner.UpdateAsync(location, cancellationToken);
|
=> _inner.UpdateAsync(location, cancellationToken);
|
||||||
public Task<bool> DeleteByIdAsync(int id, CancellationToken cancellationToken)
|
public Task<bool> SiteCodeExistsAsync(
|
||||||
=> _inner.DeleteByIdAsync(id, cancellationToken);
|
string siteCode,
|
||||||
|
int? accountId,
|
||||||
|
int? excludeLocationId,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
=> _inner.SiteCodeExistsAsync(siteCode, accountId, excludeLocationId, cancellationToken);
|
||||||
|
public Task<(int Count, IReadOnlyList<int> Ids)> GetOpenWorkOrderIdsAsync(
|
||||||
|
int locationId,
|
||||||
|
string? siteCode,
|
||||||
|
int? siteAccountId,
|
||||||
|
int? callerAccountId,
|
||||||
|
int maxIds,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
=> _inner.GetOpenWorkOrderIdsAsync(locationId, siteCode, siteAccountId, callerAccountId, maxIds, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue