Public/TestCaseManagement/Resolve-TcmTestCaseConflict.ps1
function Resolve-TcmTestCaseConflict { <# .SYNOPSIS Resolves synchronization conflicts for test cases. .DESCRIPTION Resolves conflicts that occur when both local and remote versions of a test case have changes. Provides multiple resolution strategies and supports interactive conflict resolution. Conflicts occur when content has changed in both the local YAML file and the Azure DevOps work item since the last synchronization. This function helps choose which version to keep or merge changes. .PARAMETER InputObject The local test case to resolve conflict for. Accepts: - Test case ID (string) - e.g., "TC001" Accepts pipeline input by value or property name. .PARAMETER Strategy The conflict resolution strategy to use: - Manual: Interactive resolution allowing user to choose (default) - LocalWins: Use the local version, overwrite remote changes - RemoteWins: Use the remote version, overwrite local changes - LatestWins: Use the version with the most recent modification timestamp .PARAMETER TestCasesRoot Root directory containing test case YAML files. If not specified, uses the current directory or searches parent directories for .tcm-config.yaml. .EXAMPLE PS C:\> Resolve-TcmTestCaseConflict -Id "TC001" -Strategy LocalWins Resolves the conflict for TC001 by keeping the local version. .EXAMPLE PS C:\> Resolve-TcmTestCaseConflict -Id "TC001" -Strategy RemoteWins Resolves the conflict for TC001 by using the Azure DevOps version. .EXAMPLE PS C:\> Resolve-TcmTestCaseConflict -Id "TC001" -Strategy LatestWins Resolves the conflict by choosing the version that was modified most recently. .EXAMPLE PS C:\> "TC001", "TC002" | Resolve-TcmTestCaseConflict -Strategy Manual Interactively resolves conflicts for multiple test cases. .INPUTS System.String Accepts test case IDs from the pipeline. .OUTPUTS None. Displays resolution results to the console. .NOTES - Manual strategy will prompt for user input to choose resolution approach. - LatestWins compares file modification timestamps vs. work item changed dates. - Resolution is atomic per test case to prevent inconsistent states. - After resolution, the test case will be marked as synced. .LINK Sync-TcmTestCase .LINK Resolve-TcmTestCaseSyncStatus #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias("Id", "TestCaseId", "WorkItemId")] $InputObject, [Parameter(Mandatory)] [ValidateSet('Manual', 'LocalWins', 'RemoteWins', 'LatestWins')] [string] $Strategy, [string] $TestCasesRoot ) begin { # Get configuration $config = Get-TcmTestCaseConfig -TestCasesRoot $TestCasesRoot $inputItems = @() } process { $inputItems += $InputObject } end { # PS5 compatible: don't start new line with pipe $inputItems | Get-TcmTestCase -TestCasesRoot $config.TestCasesRoot | ForEach-Object { $resolved = $_ $testCaseId = $resolved.Id try { Write-Verbose "Resolving conflict for test case '$testCaseId' using strategy: $Strategy" # Check if there's actually a conflict (already determined by Get-TcmTestCase) $syncStatus = $resolved.SyncStatus if ($syncStatus -ne 'conflict') { Write-Warning "Test case '$testCaseId' does not have a conflict (status: $syncStatus)" return } # Get local and remote data from resolved object $localData = $resolved.LocalData $localPath = $resolved.FilePath $remoteData = $resolved.RemoteData $remoteWorkItem = $resolved.RemoteWorkItem switch ($Strategy) { 'LocalWins' { Sync-TcmTestCaseToRemote ` -InputObject $resolved ` -TestCasesRoot $config.TestCasesRoot ` -Force ` -Message "Resolving conflict for '$testCaseId': Local version wins" ` -MessageColor 'Yellow' ` -ShouldProcessOperation "Resolve conflict - keep local changes" Write-Host "[OK] Conflict resolved: Local changes pushed to Azure DevOps" -ForegroundColor Green } 'RemoteWins' { Sync-TcmTestCaseFromRemote ` -InputObject $resolved ` -TestCasesRoot $config.TestCasesRoot ` -Force ` -Message "Resolving conflict for '$testCaseId': Remote version wins" ` -MessageColor 'Yellow' ` -ShouldProcessOperation "Resolve conflict - keep remote changes" Write-Host "[OK] Conflict resolved: Remote changes pulled from Azure DevOps" -ForegroundColor Green } 'LatestWins' { # Compare timestamps to determine which is newer $localTimestamp = [DateTime]::Parse($localData.history.lastModifiedAt) $remoteTimestamp = [DateTime]::Parse($remoteWorkItem.fields.'System.ChangedDate') if ($localTimestamp -gt $remoteTimestamp) { Sync-TcmTestCaseToRemote ` -InputObject $resolved ` -TestCasesRoot $config.TestCasesRoot ` -Force ` -Message "Resolving conflict for '$testCaseId': Local version is newer" ` -MessageColor 'Yellow' ` -ShouldProcessOperation "Resolve conflict - local is newer" Write-Host "[OK] Conflict resolved: Newer local changes pushed to Azure DevOps" -ForegroundColor Green } else { Sync-TcmTestCaseFromRemote ` -InputObject $resolved ` -TestCasesRoot $config.TestCasesRoot ` -Force ` -Message "Resolving conflict for '$testCaseId': Remote version is newer" ` -MessageColor 'Yellow' ` -ShouldProcessOperation "Resolve conflict - remote is newer" Write-Host "[OK] Conflict resolved: Newer remote changes pulled from Azure DevOps" -ForegroundColor Green } } 'Manual' { # Display conflict information for manual resolution Write-Host "`nConflict Details for Test Case '$testCaseId':" -ForegroundColor Cyan Write-Host "="*60 -ForegroundColor Cyan Write-Host "`nLocal Version:" -ForegroundColor Yellow Write-Host " Title: $($localData.testCase.title)" Write-Host " State: $($localData.testCase.state)" Write-Host " Steps Count: $($localData.testCase.steps.Count)" Write-Host "`nRemote Version:" -ForegroundColor Yellow Write-Host " Title: $($remoteWorkItem.fields.'System.Title')" Write-Host " Last Modified: $($remoteWorkItem.fields.'System.ChangedDate')" Write-Host " Modified By: $($remoteWorkItem.fields.'System.ChangedBy'.displayName)" Write-Host " State: $($remoteData.state)" Write-Host " Steps Count: $($remoteData.steps.Count)" Write-Host "`n" -ForegroundColor Cyan Write-Host "To resolve this conflict, run one of the following commands:" -ForegroundColor White Write-Host " Resolve-TcmTestCaseConflict -InputObject '$testCaseId' -Strategy LocalWins" -ForegroundColor Gray Write-Host " Resolve-TcmTestCaseConflict -InputObject '$testCaseId' -Strategy RemoteWins" -ForegroundColor Gray Write-Host " Resolve-TcmTestCaseConflict -InputObject '$testCaseId' -Strategy LatestWins" -ForegroundColor Gray Write-Host "`nOr manually edit the local file and sync:" -ForegroundColor White Write-Host " $localPath" -ForegroundColor Gray Write-Host " Sync-TcmTestCase -InputObject '$testCaseId'" -ForegroundColor Gray } } } catch { Write-Error "Failed to resolve conflict for test case '$testCaseId': $($_.Exception.Message)" throw } } } } |