diff --git a/SeaHavenIndustries.Tests/SH138SiteContactsSqlServerTests.cs b/SeaHavenIndustries.Tests/SH138SiteContactsSqlServerTests.cs index e93807f..5e1a301 100644 --- a/SeaHavenIndustries.Tests/SH138SiteContactsSqlServerTests.cs +++ b/SeaHavenIndustries.Tests/SH138SiteContactsSqlServerTests.cs @@ -5,7 +5,6 @@ using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; -using SeaHaven.DataServices.Implementation; 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() - .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) { var builder = new MigrationBuilder(context.Database.ProviderName!); diff --git a/SeaHavenIndustries.Tests/WorkOrderAccountScopeTests.cs b/SeaHavenIndustries.Tests/WorkOrderAccountScopeTests.cs index 59bc227..9a2ed53 100644 --- a/SeaHavenIndustries.Tests/WorkOrderAccountScopeTests.cs +++ b/SeaHavenIndustries.Tests/WorkOrderAccountScopeTests.cs @@ -849,7 +849,19 @@ public class WorkOrderAccountScopeTests => _inner.AddAsync(location, cancellationToken); public Task UpdateAsync(Locations location, CancellationToken cancellationToken) => _inner.UpdateAsync(location, cancellationToken); - public Task DeleteByIdAsync(int id, CancellationToken cancellationToken) - => _inner.DeleteByIdAsync(id, cancellationToken); + public Task SiteCodeExistsAsync( + string siteCode, + int? accountId, + int? excludeLocationId, + CancellationToken cancellationToken) + => _inner.SiteCodeExistsAsync(siteCode, accountId, excludeLocationId, cancellationToken); + public Task<(int Count, IReadOnlyList Ids)> GetOpenWorkOrderIdsAsync( + int locationId, + string? siteCode, + int? siteAccountId, + int? callerAccountId, + int maxIds, + CancellationToken cancellationToken) + => _inner.GetOpenWorkOrderIdsAsync(locationId, siteCode, siteAccountId, callerAccountId, maxIds, cancellationToken); } }