forked from Crockan/MercuryToolbox
chore(release): prepare public source release
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
//! 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"));
|
||||
}
|
||||
Reference in New Issue
Block a user