forked from Crockan/MercuryToolbox
186 lines
9.1 KiB
Rust
186 lines
9.1 KiB
Rust
//! Integration tests for the `unlock` command.
|
|
|
|
use assert_cmd::Command;
|
|
use predicates::prelude::*;
|
|
|
|
fn cargo_command() -> Command {
|
|
Command::cargo_bin("unlock").expect("binary")
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "slow Windows file-lock force-delete integration; covered by just stable-test"]
|
|
fn reports_file_lockers_and_can_force_delete() {
|
|
let binary = assert_cmd::cargo::cargo_bin("unlock");
|
|
let script = format!(
|
|
"$temp = Join-Path $env:TEMP ('mercury-unlock-test-' + [guid]::NewGuid()); \
|
|
New-Item -ItemType Directory -Path $temp | Out-Null; \
|
|
$path = Join-Path $temp 'locked.txt'; \
|
|
$locker = Join-Path $temp 'locker.ps1'; \
|
|
Set-Content -Path $path -Value 'locked'; \
|
|
Set-Content -Path $locker -Value \"param([string]`$Path)`n`$stream = [System.IO.File]::Open(`$Path, 'Open', 'ReadWrite', 'None')`nStart-Sleep -Seconds 10\"; \
|
|
$child = Start-Process pwsh -ArgumentList '-NoProfile','-File',$locker,$path -PassThru; \
|
|
Start-Sleep -Milliseconds 500; \
|
|
$delete = & '{}' delete --json --force --wait 0ms $path | ConvertFrom-Json; \
|
|
$exists = Test-Path $path; \
|
|
Stop-Process -Id $child.Id -Force -ErrorAction SilentlyContinue; \
|
|
Remove-Item -LiteralPath $temp -Recurse -Force -ErrorAction SilentlyContinue; \
|
|
[pscustomobject]@{{ delete = $delete; exists = $exists }} | ConvertTo-Json -Depth 8 -Compress",
|
|
binary.display()
|
|
);
|
|
|
|
let mut command = Command::new("pwsh");
|
|
command
|
|
.arg("-NoProfile")
|
|
.arg("-Command")
|
|
.arg(script)
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("\"results\":[{"))
|
|
.stdout(predicate::str::contains("\"summary\":{\"results\":1"))
|
|
.stdout(predicate::str::contains("\"initial_blockers\""))
|
|
.stdout(predicate::str::contains("\"command_line\""))
|
|
.stdout(predicate::str::contains("\"action\":\"delete\""))
|
|
.stdout(predicate::str::contains("\"ok\":true"))
|
|
.stdout(predicate::str::contains("\"exists\":false"));
|
|
}
|
|
|
|
#[test]
|
|
fn help_includes_unlock_examples() {
|
|
let mut command = cargo_command();
|
|
command
|
|
.arg("--help")
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("who"))
|
|
.stdout(predicate::str::contains("--deep"))
|
|
.stdout(predicate::str::contains("delete"))
|
|
.stdout(predicate::str::contains("ConvertFrom-Json"));
|
|
}
|
|
|
|
#[test]
|
|
fn who_returns_success_for_unlocked_files() {
|
|
let temp = std::env::temp_dir().join(format!(
|
|
"unlock-who-{}",
|
|
std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.expect("clock")
|
|
.as_nanos()
|
|
));
|
|
std::fs::create_dir_all(&temp).expect("tempdir");
|
|
let path = temp.join("plain.txt");
|
|
std::fs::write(&path, "ready").expect("fixture");
|
|
|
|
let mut command = cargo_command();
|
|
command
|
|
.arg("who")
|
|
.arg("--json")
|
|
.arg(&path)
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("\"summary\":{\"results\":1"))
|
|
.stdout(predicate::str::contains("\"ok\":true"))
|
|
.stdout(predicate::str::contains("\"final_blockers\":[]"));
|
|
|
|
let _ = std::fs::remove_file(&path);
|
|
let _ = std::fs::remove_dir_all(temp);
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
#[ignore = "slow Windows junction integration; covered by just stable-test"]
|
|
fn delete_refuses_junction_directory_without_touching_target() {
|
|
let binary = assert_cmd::cargo::cargo_bin("unlock");
|
|
let script = format!(
|
|
"$temp = Join-Path $env:TEMP ('mercury-unlock-junction-' + [guid]::NewGuid()); \
|
|
$target = Join-Path $temp 'target'; \
|
|
$junction = Join-Path $temp 'junction'; \
|
|
$sentinel = Join-Path $target 'sentinel.txt'; \
|
|
try {{ \
|
|
New-Item -ItemType Directory -Path $target | Out-Null; \
|
|
Set-Content -LiteralPath $sentinel -Value 'keep'; \
|
|
New-Item -ItemType Junction -Path $junction -Target $target | Out-Null; \
|
|
$json = & '{}' delete --json $junction; \
|
|
$exit = $LASTEXITCODE; \
|
|
$report = $json | ConvertFrom-Json; \
|
|
if ($exit -eq 0) {{ throw 'unlock delete unexpectedly succeeded for a junction directory' }}; \
|
|
if (-not (Test-Path -LiteralPath $sentinel)) {{ throw 'junction target sentinel was deleted' }}; \
|
|
if (-not (Test-Path -LiteralPath $junction)) {{ throw 'junction itself was removed instead of refused' }}; \
|
|
if ($report.results[0].ok -ne $false) {{ throw 'unlock report should mark the junction delete as not ok' }}; \
|
|
if ($report.results[0].error -notmatch 'reparse') {{ throw ('expected reparse error, got: ' + $report.results[0].error) }}; \
|
|
[pscustomobject]@{{ exit = $exit; sentinel = (Test-Path -LiteralPath $sentinel); junction = (Test-Path -LiteralPath $junction); error = $report.results[0].error }} | ConvertTo-Json -Compress \
|
|
}} finally {{ \
|
|
if (Test-Path -LiteralPath $junction) {{ Remove-Item -LiteralPath $junction -Force -ErrorAction SilentlyContinue }}; \
|
|
if (Test-Path -LiteralPath $temp) {{ Remove-Item -LiteralPath $temp -Recurse -Force -ErrorAction SilentlyContinue }} \
|
|
}}",
|
|
binary.display()
|
|
);
|
|
|
|
let mut command = Command::new("pwsh");
|
|
command
|
|
.arg("-NoProfile")
|
|
.arg("-Command")
|
|
.arg(script)
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("\"sentinel\":true"))
|
|
.stdout(predicate::str::contains("\"junction\":true"))
|
|
.stdout(predicate::str::contains("reparse"));
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
#[ignore = "slow Windows junction integration; covered by just stable-test"]
|
|
fn copy_move_and_rename_refuse_junction_sources_with_reparse_errors() {
|
|
let binary = assert_cmd::cargo::cargo_bin("unlock");
|
|
let script = format!(
|
|
"$temp = Join-Path $env:TEMP ('mercury-unlock-junction-actions-' + [guid]::NewGuid()); \
|
|
$target = Join-Path $temp 'target'; \
|
|
$copyJunction = Join-Path $temp 'copy-junction'; \
|
|
$moveJunction = Join-Path $temp 'move-junction'; \
|
|
$renameJunction = Join-Path $temp 'rename-junction'; \
|
|
$copyDest = Join-Path $temp 'copy-dest'; \
|
|
$moveDest = Join-Path $temp 'move-dest'; \
|
|
$sentinel = Join-Path $target 'sentinel.txt'; \
|
|
function Invoke-Refused([string[]]$ArgumentList) {{ \
|
|
$json = & '{}' @ArgumentList; \
|
|
$exit = $LASTEXITCODE; \
|
|
$report = $json | ConvertFrom-Json; \
|
|
if ($exit -eq 0) {{ throw ('expected nonzero exit for ' + ($ArgumentList -join ' ')) }}; \
|
|
if ($report.results[0].ok -ne $false) {{ throw ('expected ok=false for ' + ($ArgumentList -join ' ')) }}; \
|
|
if ($report.results[0].error -notmatch 'reparse') {{ throw ('expected reparse error for ' + ($ArgumentList -join ' ') + ', got: ' + $report.results[0].error) }}; \
|
|
return $report.results[0].error \
|
|
}}; \
|
|
try {{ \
|
|
New-Item -ItemType Directory -Path $target | Out-Null; \
|
|
Set-Content -LiteralPath $sentinel -Value 'keep'; \
|
|
New-Item -ItemType Junction -Path $copyJunction -Target $target | Out-Null; \
|
|
New-Item -ItemType Junction -Path $moveJunction -Target $target | Out-Null; \
|
|
New-Item -ItemType Junction -Path $renameJunction -Target $target | Out-Null; \
|
|
$copyError = Invoke-Refused @('copy','--json',$copyJunction,$copyDest); \
|
|
$moveError = Invoke-Refused @('move','--json',$moveJunction,$moveDest); \
|
|
$renameError = Invoke-Refused @('rename','--json',$renameJunction,'renamed-junction'); \
|
|
if (-not (Test-Path -LiteralPath $sentinel)) {{ throw 'junction target sentinel was deleted' }}; \
|
|
if (-not (Test-Path -LiteralPath $copyJunction)) {{ throw 'copy junction was removed' }}; \
|
|
if (-not (Test-Path -LiteralPath $moveJunction)) {{ throw 'move junction was removed' }}; \
|
|
if (-not (Test-Path -LiteralPath $renameJunction)) {{ throw 'rename junction was removed' }}; \
|
|
if (Test-Path -LiteralPath $copyDest) {{ throw 'copy destination was created' }}; \
|
|
if (Test-Path -LiteralPath $moveDest) {{ throw 'move destination was created' }}; \
|
|
[pscustomobject]@{{ sentinel = (Test-Path -LiteralPath $sentinel); copy = $copyError; move = $moveError; rename = $renameError }} | ConvertTo-Json -Compress \
|
|
}} finally {{ \
|
|
foreach ($path in @($copyJunction,$moveJunction,$renameJunction)) {{ if (Test-Path -LiteralPath $path) {{ Remove-Item -LiteralPath $path -Force -ErrorAction SilentlyContinue }} }}; \
|
|
if (Test-Path -LiteralPath $temp) {{ Remove-Item -LiteralPath $temp -Recurse -Force -ErrorAction SilentlyContinue }} \
|
|
}}",
|
|
binary.display()
|
|
);
|
|
|
|
let mut command = Command::new("pwsh");
|
|
command
|
|
.arg("-NoProfile")
|
|
.arg("-Command")
|
|
.arg(script)
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("\"sentinel\":true"))
|
|
.stdout(predicate::str::contains("reparse"));
|
|
}
|