-- Run against a migrated verification/dev database with at least one project and scene. -- All fixture changes are rolled back, including on assertion failure. SET XACT_ABORT ON; BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @SceneID int; SELECT TOP (1) @ProjectID = p.ProjectID, @UserID = p.UserID, @SceneID = s.SceneID FROM dbo.ProjectUserAccess p JOIN dbo.Books b ON b.ProjectID = p.ProjectID JOIN dbo.Chapters c ON c.BookID = b.BookID JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID WHERE p.IsActive = 1; IF @SceneID IS NULL THROW 51001, 'Merge tests require a project with a scene and active user.', 1; DECLARE @Source int, @Target int, @Child int; INSERT dbo.Locations(ProjectID, LocationName) VALUES (@ProjectID, N'Merge test old observatory'); SET @Source = SCOPE_IDENTITY(); INSERT dbo.Locations(ProjectID, LocationName, Description) VALUES (@ProjectID, N'Merge test observatory', N'Keep target details'); SET @Target = SCOPE_IDENTITY(); INSERT dbo.Locations(ProjectID, LocationName, ParentLocationID) VALUES (@ProjectID, N'Merge test dome', @Source); SET @Child = SCOPE_IDENTITY(); INSERT dbo.LocationAliases(LocationID, Alias) VALUES (@Source, N'North tower'), (@Target, N'North tower'), (@Source, N'Old dome'); INSERT dbo.WordCompanionSceneLocations(SceneID, LocationID) VALUES (@SceneID, @Source), (@SceneID, @Target); INSERT dbo.SceneLocationSuggestions(SceneID, LocationID, CreatedByUserID) VALUES (@SceneID, @Source, @UserID), (@SceneID, @Target, @UserID); UPDATE dbo.Scenes SET PrimaryLocationID = @Source WHERE SceneID = @SceneID; DECLARE @RelType int = (SELECT TOP (1) LocationRelationshipTypeID FROM dbo.LocationRelationshipTypes); INSERT dbo.LocationRelationships(FromLocationID, ToLocationID, LocationRelationshipTypeID) VALUES (@Source,@Target,@RelType),(@Source,@Child,@RelType),(@Target,@Child,@RelType); EXEC dbo.Location_Merge @Source, @Target, @UserID; IF NOT EXISTS (SELECT 1 FROM dbo.Locations WHERE LocationID=@Source AND IsArchived=1) THROW 51001, 'Source location was not archived.', 1; IF NOT EXISTS (SELECT 1 FROM dbo.Locations WHERE LocationID=@Child AND ParentLocationID=@Target) THROW 51001, 'Child location was not moved.', 1; IF NOT EXISTS (SELECT 1 FROM dbo.Scenes WHERE SceneID=@SceneID AND PrimaryLocationID=@Target) THROW 51001, 'Primary scene location was not moved.', 1; IF (SELECT COUNT(*) FROM dbo.LocationAliases WHERE LocationID=@Target) <> 3 THROW 51001, 'Location names were lost or duplicated.', 1; IF (SELECT COUNT(*) FROM dbo.WordCompanionSceneLocations WHERE SceneID=@SceneID AND LocationID=@Target) <> 1 THROW 51001, 'Duplicate location scene link.', 1; IF (SELECT COUNT(*) FROM dbo.SceneLocationSuggestions WHERE SceneID=@SceneID AND LocationID=@Target AND Status=N'Pending') <> 1 THROW 51001, 'Duplicate location suggestion.', 1; IF (SELECT COUNT(*) FROM dbo.LocationRelationships WHERE FromLocationID=@Target AND ToLocationID=@Child AND IsArchived=0) <> 1 THROW 51001, 'Duplicate location relationship.', 1; IF EXISTS (SELECT 1 FROM dbo.LocationRelationships WHERE (FromLocationID=@Source OR ToLocationID=@Source) AND IsArchived=0) THROW 51001, 'Active relationship left on source.', 1; IF NOT EXISTS (SELECT 1 FROM dbo.Locations WHERE LocationID=@Target AND Description=N'Keep target details') THROW 51001, 'Target location details changed.', 1; DECLARE @Kind int = (SELECT TOP (1) AssetKindID FROM dbo.AssetKinds), @Other int; INSERT dbo.StoryAssets(ProjectID, AssetName, AssetKindID) VALUES (@ProjectID,N'Merge test brass compass',@Kind); SET @Source=SCOPE_IDENTITY(); INSERT dbo.StoryAssets(ProjectID, AssetName, AssetKindID,Description) VALUES (@ProjectID,N'Merge test compass',@Kind,N'Keep target details'); SET @Target=SCOPE_IDENTITY(); INSERT dbo.StoryAssets(ProjectID, AssetName, AssetKindID) VALUES (@ProjectID,N'Merge test map',@Kind); SET @Other=SCOPE_IDENTITY(); INSERT dbo.AssetAliases(StoryAssetID, Alias) VALUES (@Source,N'Navigator'),(@Target,N'Navigator'),(@Source,N'Brass guide'); INSERT dbo.WordCompanionSceneAssets(SceneID,StoryAssetID) VALUES (@SceneID,@Source),(@SceneID,@Target); INSERT dbo.SceneAssetSuggestions(SceneID,AssetID,CreatedByUserID) VALUES (@SceneID,@Source,@UserID),(@SceneID,@Target,@UserID); DECLARE @EventType int=(SELECT TOP (1) AssetEventTypeID FROM dbo.AssetEventTypes), @CustodyType int=(SELECT TOP (1) AssetCustodyEventTypeID FROM dbo.AssetCustodyEventTypes); INSERT dbo.AssetEvents(StoryAssetID,SceneID,AssetEventTypeID,EventTitle) VALUES (@Source,@SceneID,@EventType,N'Merge test event'); DECLARE @EventID int=SCOPE_IDENTITY(); INSERT dbo.AssetCustodyEvents(StoryAssetID,SceneID,AssetCustodyEventTypeID) VALUES (@Source,@SceneID,@CustodyType); DECLARE @CustodyID int=SCOPE_IDENTITY(), @DepType int=(SELECT TOP (1) AssetDependencyTypeID FROM dbo.AssetDependencyTypes); INSERT dbo.AssetDependencies(SourceAssetID,TargetAssetID,AssetDependencyTypeID) VALUES (@Source,@Target,@DepType),(@Source,@Other,@DepType),(@Target,@Other,@DepType); EXEC dbo.StoryAsset_Merge @Source,@Target,@UserID; IF NOT EXISTS (SELECT 1 FROM dbo.StoryAssets WHERE StoryAssetID=@Source AND IsArchived=1) THROW 51001, 'Source asset was not archived.', 1; IF (SELECT COUNT(*) FROM dbo.AssetAliases WHERE StoryAssetID=@Target) <> 3 THROW 51001, 'Asset names were lost or duplicated.', 1; IF (SELECT COUNT(*) FROM dbo.WordCompanionSceneAssets WHERE SceneID=@SceneID AND StoryAssetID=@Target) <> 1 THROW 51001, 'Duplicate asset scene link.', 1; IF (SELECT COUNT(*) FROM dbo.SceneAssetSuggestions WHERE SceneID=@SceneID AND AssetID=@Target AND Status=N'Pending') <> 1 THROW 51001, 'Duplicate asset suggestion.', 1; IF NOT EXISTS (SELECT 1 FROM dbo.AssetEvents WHERE AssetEventID=@EventID AND StoryAssetID=@Target) THROW 51001, 'Asset event was not moved.', 1; IF NOT EXISTS (SELECT 1 FROM dbo.AssetCustodyEvents WHERE AssetCustodyEventID=@CustodyID AND StoryAssetID=@Target) THROW 51001, 'Custody was not moved.', 1; IF (SELECT COUNT(*) FROM dbo.AssetDependencies WHERE SourceAssetID=@Target AND TargetAssetID=@Other AND IsArchived=0) <> 1 THROW 51001, 'Duplicate asset dependency.', 1; IF EXISTS (SELECT 1 FROM dbo.AssetDependencies WHERE (SourceAssetID=@Source OR TargetAssetID=@Source) AND IsArchived=0) THROW 51001, 'Active dependency left on source.', 1; IF NOT EXISTS (SELECT 1 FROM dbo.StoryAssets WHERE StoryAssetID=@Target AND Description=N'Keep target details') THROW 51001, 'Target asset details changed.', 1; ROLLBACK TRANSACTION; SELECT N'PASS: location and asset merges; fixtures rolled back' AS Result; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; THROW; END CATCH; GO -- Hierarchy failure must also leave the source unmodified (the procedure rolls back the fixtures). BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @Source int, @Target int; SELECT TOP (1) @ProjectID=ProjectID,@UserID=UserID FROM dbo.ProjectUserAccess WHERE IsActive=1; INSERT dbo.Locations(ProjectID,LocationName) VALUES (@ProjectID,N'Merge cycle test parent'); SET @Source=SCOPE_IDENTITY(); INSERT dbo.Locations(ProjectID,LocationName,ParentLocationID) VALUES (@ProjectID,N'Merge cycle test child',@Source); SET @Target=SCOPE_IDENTITY(); EXEC dbo.Location_Merge @Source,@Target,@UserID; THROW 51001, 'Hierarchy cycle was accepted.', 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF ERROR_NUMBER() <> 51000 OR ERROR_MESSAGE() NOT LIKE '%descendants%' THROW; SELECT N'PASS: hierarchy cycle rejected; fixtures rolled back' AS Result; END CATCH; GO -- Reject same record for Location without leaving fixture data behind. BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @Source int, @Target int; SELECT TOP (1) @ProjectID=ProjectID,@UserID=UserID FROM dbo.ProjectUserAccess WHERE IsActive=1; INSERT dbo.Locations(ProjectID,LocationName) VALUES (@ProjectID,N'Merge validation source'); SET @Source=SCOPE_IDENTITY(); INSERT dbo.Locations(ProjectID,LocationName) VALUES (@ProjectID,N'Merge validation target'); SET @Target=SCOPE_IDENTITY(); SET @Target=@Source; EXEC dbo.Location_Merge @Source,@Target,@UserID; THROW 51001, 'Invalid merge was accepted.', 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF ERROR_NUMBER() <> 51000 THROW; SELECT N'PASS: Location rejects same record; fixtures rolled back' AS Result; END CATCH; GO -- Reject archived target for Location without leaving fixture data behind. BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @Source int, @Target int; SELECT TOP (1) @ProjectID=ProjectID,@UserID=UserID FROM dbo.ProjectUserAccess WHERE IsActive=1; INSERT dbo.Locations(ProjectID,LocationName) VALUES (@ProjectID,N'Merge validation source'); SET @Source=SCOPE_IDENTITY(); INSERT dbo.Locations(ProjectID,LocationName) VALUES (@ProjectID,N'Merge validation target'); SET @Target=SCOPE_IDENTITY(); UPDATE dbo.Locations SET IsArchived=1 WHERE LocationID=@Target; EXEC dbo.Location_Merge @Source,@Target,@UserID; THROW 51001, 'Invalid merge was accepted.', 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF ERROR_NUMBER() <> 51000 THROW; SELECT N'PASS: Location rejects archived target; fixtures rolled back' AS Result; END CATCH; GO -- Reject other project for Location without leaving fixture data behind. BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @Source int, @Target int; SELECT TOP (1) @ProjectID=ProjectID,@UserID=UserID FROM dbo.ProjectUserAccess WHERE IsActive=1; INSERT dbo.Locations(ProjectID,LocationName) VALUES (@ProjectID,N'Merge validation source'); SET @Source=SCOPE_IDENTITY(); INSERT dbo.Locations(ProjectID,LocationName) VALUES (@ProjectID,N'Merge validation target'); SET @Target=SCOPE_IDENTITY(); INSERT dbo.Projects(ProjectName) VALUES (N'Merge test other project'); DECLARE @OtherProjectID int=SCOPE_IDENTITY(); UPDATE dbo.Locations SET ProjectID=@OtherProjectID WHERE LocationID=@Target; EXEC dbo.Location_Merge @Source,@Target,@UserID; THROW 51001, 'Invalid merge was accepted.', 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF ERROR_NUMBER() <> 51000 THROW; SELECT N'PASS: Location rejects other project; fixtures rolled back' AS Result; END CATCH; GO -- Reject unauthorized user for Location without leaving fixture data behind. BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @Source int, @Target int; SELECT TOP (1) @ProjectID=ProjectID,@UserID=UserID FROM dbo.ProjectUserAccess WHERE IsActive=1; INSERT dbo.Locations(ProjectID,LocationName) VALUES (@ProjectID,N'Merge validation source'); SET @Source=SCOPE_IDENTITY(); INSERT dbo.Locations(ProjectID,LocationName) VALUES (@ProjectID,N'Merge validation target'); SET @Target=SCOPE_IDENTITY(); SET @UserID=NULL; EXEC dbo.Location_Merge @Source,@Target,@UserID; THROW 51001, 'Invalid merge was accepted.', 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF ERROR_NUMBER() <> 51000 THROW; SELECT N'PASS: Location rejects unauthorized user; fixtures rolled back' AS Result; END CATCH; GO -- Reject same record for StoryAsset without leaving fixture data behind. BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @Source int, @Target int; SELECT TOP (1) @ProjectID=ProjectID,@UserID=UserID FROM dbo.ProjectUserAccess WHERE IsActive=1; INSERT dbo.StoryAssets(ProjectID,AssetName,AssetKindID) VALUES (@ProjectID,N'Merge validation source',(SELECT TOP (1) AssetKindID FROM dbo.AssetKinds)); SET @Source=SCOPE_IDENTITY(); INSERT dbo.StoryAssets(ProjectID,AssetName,AssetKindID) VALUES (@ProjectID,N'Merge validation target',(SELECT TOP (1) AssetKindID FROM dbo.AssetKinds)); SET @Target=SCOPE_IDENTITY(); SET @Target=@Source; EXEC dbo.StoryAsset_Merge @Source,@Target,@UserID; THROW 51001, 'Invalid merge was accepted.', 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF ERROR_NUMBER() <> 51000 THROW; SELECT N'PASS: StoryAsset rejects same record; fixtures rolled back' AS Result; END CATCH; GO -- Reject archived target for StoryAsset without leaving fixture data behind. BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @Source int, @Target int; SELECT TOP (1) @ProjectID=ProjectID,@UserID=UserID FROM dbo.ProjectUserAccess WHERE IsActive=1; INSERT dbo.StoryAssets(ProjectID,AssetName,AssetKindID) VALUES (@ProjectID,N'Merge validation source',(SELECT TOP (1) AssetKindID FROM dbo.AssetKinds)); SET @Source=SCOPE_IDENTITY(); INSERT dbo.StoryAssets(ProjectID,AssetName,AssetKindID) VALUES (@ProjectID,N'Merge validation target',(SELECT TOP (1) AssetKindID FROM dbo.AssetKinds)); SET @Target=SCOPE_IDENTITY(); UPDATE dbo.StoryAssets SET IsArchived=1 WHERE StoryAssetID=@Target; EXEC dbo.StoryAsset_Merge @Source,@Target,@UserID; THROW 51001, 'Invalid merge was accepted.', 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF ERROR_NUMBER() <> 51000 THROW; SELECT N'PASS: StoryAsset rejects archived target; fixtures rolled back' AS Result; END CATCH; GO -- Reject other project for StoryAsset without leaving fixture data behind. BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @Source int, @Target int; SELECT TOP (1) @ProjectID=ProjectID,@UserID=UserID FROM dbo.ProjectUserAccess WHERE IsActive=1; INSERT dbo.StoryAssets(ProjectID,AssetName,AssetKindID) VALUES (@ProjectID,N'Merge validation source',(SELECT TOP (1) AssetKindID FROM dbo.AssetKinds)); SET @Source=SCOPE_IDENTITY(); INSERT dbo.StoryAssets(ProjectID,AssetName,AssetKindID) VALUES (@ProjectID,N'Merge validation target',(SELECT TOP (1) AssetKindID FROM dbo.AssetKinds)); SET @Target=SCOPE_IDENTITY(); INSERT dbo.Projects(ProjectName) VALUES (N'Merge test other project'); DECLARE @OtherProjectID int=SCOPE_IDENTITY(); UPDATE dbo.StoryAssets SET ProjectID=@OtherProjectID WHERE StoryAssetID=@Target; EXEC dbo.StoryAsset_Merge @Source,@Target,@UserID; THROW 51001, 'Invalid merge was accepted.', 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF ERROR_NUMBER() <> 51000 THROW; SELECT N'PASS: StoryAsset rejects other project; fixtures rolled back' AS Result; END CATCH; GO -- Reject unauthorized user for StoryAsset without leaving fixture data behind. BEGIN TRY BEGIN TRANSACTION; DECLARE @ProjectID int, @UserID int, @Source int, @Target int; SELECT TOP (1) @ProjectID=ProjectID,@UserID=UserID FROM dbo.ProjectUserAccess WHERE IsActive=1; INSERT dbo.StoryAssets(ProjectID,AssetName,AssetKindID) VALUES (@ProjectID,N'Merge validation source',(SELECT TOP (1) AssetKindID FROM dbo.AssetKinds)); SET @Source=SCOPE_IDENTITY(); INSERT dbo.StoryAssets(ProjectID,AssetName,AssetKindID) VALUES (@ProjectID,N'Merge validation target',(SELECT TOP (1) AssetKindID FROM dbo.AssetKinds)); SET @Target=SCOPE_IDENTITY(); SET @UserID=NULL; EXEC dbo.StoryAsset_Merge @Source,@Target,@UserID; THROW 51001, 'Invalid merge was accepted.', 1; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF ERROR_NUMBER() <> 51000 THROW; SELECT N'PASS: StoryAsset rejects unauthorized user; fixtures rolled back' AS Result; END CATCH;