Browse Source

fix: detect destination folders and name a missing destination folder in Copy-Item2 and Move-Item2

The check for an existing destination looked for a file only. For a
folder whose name existed at the destination, the cmdlets failed in the
middle with a CopyError or a MoveError, and Copy-Item2 could copy a part
of the folder first. They now write DestinationFileAlreadyExists, as
for a file.

When the folder that is to contain the new item didn't exist, AlphaFS
reported a DirectoryNotFoundException that named the source item, which
reproduces the symptom of #21. The cmdlets now write an error that names
the missing folder, with the destination as the target. Copy-Item2 no
longer creates the missing folders for a folder: the workaround that
creates the destination folder for AlphaFS created its parents as well,
which only the prereleases of 5.0.0 did.

The pages also say that -Force merges a folder into an existing folder
of the same name, and that a folder can't move to another volume.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
ai/release-5.0.0-rc6
Raimund Andree 4 days ago
parent
commit
f977fec525
  1. 8
      CHANGELOG.md
  2. 6
      Docs/Cmdlets/Copy-Item2.md
  3. 8
      Docs/Cmdlets/Move-Item2.md
  4. 23
      NTFSSecurity/BaseCmdlets.cs
  5. 8
      NTFSSecurity/ItemCmdlets/CopyItem2.cs
  6. 8
      NTFSSecurity/ItemCmdlets/MoveItem2.cs
  7. 18
      NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml
  8. 83
      Tests/ItemCmdlets.Tests.ps1

8
CHANGELOG.md

@ -287,5 +287,13 @@ The format is based on
grants only `ReadData`, and which left out the parent folder of a
relative path with a single folder name, although `-IncludeRootFolder` is
on by default
- Fix `Copy-Item2` and `Move-Item2`, which didn't detect a folder at the
destination, so that a copy failed in the middle after it had copied a
part of the folder, and which reported a missing destination folder with
an error that named the source item
([#21](https://github.com/raandree/NTFSSecurity/issues/21)); they now
write `DestinationFileAlreadyExists` and an error that names the missing
folder. `Copy-Item2` no longer creates the missing folders of the
destination when it copies a folder, which the prereleases of 5.0.0 did
[Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD

6
Docs/Cmdlets/Copy-Item2.md

@ -24,7 +24,7 @@ The `Copy-Item2` cmdlet copies the items in `-Path` to the location in `-Destina
How `-Destination` is interpreted depends on what is already there. If the value names an existing folder, the cmdlet keeps the name of the source item and copies it into that folder. In every other case the value is the full path of the new item, which lets you copy and rename in one step. `-Destination` is resolved against the current location once, when the cmdlet starts.
Without `-Force`, the cmdlet checks whether the destination file already exists and writes a `DestinationFileAlreadyExists` error instead of overwriting it. With `-WhatIf`, it names an existing destination file in a verbose message instead; before 5.0.0, it wrote the error also with `-WhatIf`. With `-Force`, an existing file is replaced. Relative paths and the `.` and `..` notations in `-Path` are resolved against the current location, and wildcard characters are not supported.
Without `-Force`, the cmdlet checks whether a file or folder already exists at the destination and writes a `DestinationFileAlreadyExists` error instead of overwriting it or merging into it. With `-WhatIf`, it names an existing destination in a verbose message instead; before 5.0.0, it wrote the error also with `-WhatIf`. With `-Force`, an existing file is replaced, and a folder is copied into an existing folder of the same name, replacing the files that exist in both. The folder that is to contain the new item must exist; otherwise the cmdlet writes an error that names that folder. Relative paths and the `.` and `..` notations in `-Path` are resolved against the current location, and wildcard characters are not supported.
The cmdlet supports `-WhatIf` and `-Confirm`, and it writes nothing to the pipeline unless you specify `-PassThru $true`.
@ -98,7 +98,7 @@ Accept wildcard characters: False
### -Force
Indicates that the cmdlet overwrites an existing destination file. Without `-Force`, an existing file causes the error `DestinationFileAlreadyExists` and the item is not copied.
Indicates that the cmdlet overwrites an existing destination file, and copies a folder into an existing folder of the same name. Without `-Force`, an existing file or folder at the destination causes the error `DestinationFileAlreadyExists` and the item is not copied.
```yaml
Type: SwitchParameter
@ -192,7 +192,7 @@ Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confi
Before 5.0.0, copying a folder that contained files failed with a `CopyError` that reported a `DirectoryNotFoundException` for the first file in the folder.
If a path in `-Path` does not exist or the destination file exists and `-Force` is missing, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call.
If a path in `-Path` does not exist, a file or folder exists at the destination and `-Force` is missing, or the folder that is to contain the copy does not exist, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call. Before 5.0.0, it also didn't detect an existing destination folder, so that the copy failed in the middle with a `CopyError` after it had copied a part of the folder; it reported a missing destination folder as a `DirectoryNotFoundException` that named the source item; and, in the prereleases of 5.0.0, it created the missing folders of the destination for a folder.
## RELATED LINKS

8
Docs/Cmdlets/Move-Item2.md

@ -24,7 +24,7 @@ The `Move-Item2` cmdlet moves the items in `-Path` to the location in `-Destinat
How `-Destination` is interpreted depends on what is already there. If the value names an existing folder, the cmdlet keeps the name of the source item and moves it into that folder. In every other case the value is the full path of the new item, which lets you move and rename in one step, or rename an item in place. `-Destination` is resolved against the current location once, when the cmdlet starts.
Without `-Force`, the cmdlet checks whether the destination file already exists and writes a `DestinationFileAlreadyExists` error instead of overwriting it; the move itself then runs with the `CopyAllowed` option, which allows a file to move to a different volume. With `-WhatIf`, the cmdlet names an existing destination file in a verbose message instead; before 5.0.0, it wrote the error also with `-WhatIf`. With `-Force`, the move runs with the `ReplaceExisting` option and overwrites an existing destination item.
Without `-Force`, the cmdlet checks whether a file or folder already exists at the destination and writes a `DestinationFileAlreadyExists` error instead of overwriting it; the move itself then runs with the `CopyAllowed` option, which allows a file to move to a different volume. With `-WhatIf`, the cmdlet names an existing destination in a verbose message instead; before 5.0.0, it wrote the error also with `-WhatIf`. With `-Force`, the move runs with the `ReplaceExisting` option and overwrites an existing destination item. The folder that is to contain the moved item must exist; otherwise the cmdlet writes an error that names that folder.
The cmdlet supports `-WhatIf` and `-Confirm`, and it writes nothing to the pipeline unless you specify `-PassThru $true`.
@ -98,7 +98,7 @@ Accept wildcard characters: False
### -Force
Indicates that the cmdlet replaces an existing destination item. Without `-Force`, an existing destination file causes the error `DestinationFileAlreadyExists` and the item is not moved.
Indicates that the cmdlet replaces an existing destination item. Without `-Force`, an existing file or folder at the destination causes the error `DestinationFileAlreadyExists` and the item is not moved.
```yaml
Type: SwitchParameter
@ -190,9 +190,9 @@ With `-PassThru $true` the cmdlet returns a folder object for each folder that i
Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confirmation skipped the operation.
The cmdlet chooses between two mutually exclusive move options. Without `-Force` it moves with `CopyAllowed`, which permits a file to cross volume boundaries because Windows then copies and deletes it. With `-Force` it moves with `ReplaceExisting`, which overwrites the destination but does not request `CopyAllowed`, so a move across volumes can fail when `-Force` is specified.
The cmdlet chooses between two mutually exclusive move options. Without `-Force` it moves with `CopyAllowed`, which permits a file to cross volume boundaries because Windows then copies and deletes it. With `-Force` it moves with `ReplaceExisting`, which overwrites the destination but does not request `CopyAllowed`, so a move across volumes can fail when `-Force` is specified. A folder can't move to another volume: the cmdlet writes a `MoveError` and leaves the folder in place, so copy it with `Copy-Item2` and remove it with `Remove-Item2` instead.
If a path in `-Path` does not exist or the destination file exists and `-Force` is missing, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call.
If a path in `-Path` does not exist, a file or folder exists at the destination and `-Force` is missing, or the folder that is to contain the moved item does not exist, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call. Before 5.0.0, it also reported an existing destination folder as a `MoveError`, and a missing destination folder as a `DirectoryNotFoundException` that named the source item ([#21](https://github.com/raandree/NTFSSecurity/issues/21)).
## RELATED LINKS

23
NTFSSecurity/BaseCmdlets.cs

@ -149,6 +149,29 @@ namespace NTFSSecurity
}
#endregion
#region WriteMissingDestinationFolderError
/// <summary>
/// Writes an error that names the folder of a destination path when that folder doesn't exist. Before
/// 5.0.0-rc6, AlphaFS reported such a destination as the source path that could not be found (#21), and
/// Copy-Item2 created the missing folders for a folder.
/// </summary>
/// <param name="destinationPath">The full path of the item that the operation would create.</param>
/// <param name="errorId">The error ID of the cmdlet for a failed operation.</param>
/// <returns>Whether the folder is missing and the error was written.</returns>
protected bool WriteMissingDestinationFolderError(string destinationPath, string errorId)
{
var folder = Alphaleonis.Win32.Filesystem.Path.GetDirectoryName(destinationPath.TrimEnd('\\'));
if (string.IsNullOrEmpty(folder) || Alphaleonis.Win32.Filesystem.Directory.Exists(folder))
{
return false;
}
var exception = new System.IO.DirectoryNotFoundException(string.Format("The destination folder '{0}' does not exist.", folder));
WriteError(new ErrorRecord(exception, errorId, ErrorCategory.ObjectNotFound, destinationPath));
return true;
}
#endregion
#region InvokeAsOwner
/// <summary>
/// Takes ownership of the item, runs the action, and restores the previous owner on every exit path.

8
NTFSSecurity/ItemCmdlets/CopyItem2.cs

@ -88,7 +88,8 @@ namespace NTFSSecurity
actualDestination = destination;
}
var destinationExists = !force && File.Exists(actualDestination);
// A folder at the destination counts as well; before 5.0.0-rc6, only a file did.
var destinationExists = !force && (File.Exists(actualDestination) || Directory.Exists(actualDestination));
// Report a conflict only for an operation that runs; -WhatIf names it in a verbose message (#108).
if (!ShouldProcess(resolvedPath, item is FileInfo ? "Copy File" : "Copy Directory"))
@ -107,6 +108,11 @@ namespace NTFSSecurity
continue;
}
if (WriteMissingDestinationFolderError(actualDestination, "CopyError"))
{
continue;
}
try
{
FileSystemInfo copy = null;

8
NTFSSecurity/ItemCmdlets/MoveItem2.cs

@ -88,7 +88,8 @@ namespace NTFSSecurity
actualDestination = destination;
}
var destinationExists = !force && File.Exists(actualDestination);
// A folder at the destination counts as well; before 5.0.0-rc6, only a file did.
var destinationExists = !force && (File.Exists(actualDestination) || Directory.Exists(actualDestination));
// Report a conflict only for an operation that runs; -WhatIf names it in a verbose message (#108).
if (!ShouldProcess(resolvedPath, item is FileInfo ? "Move File" : "Move Directory"))
@ -107,6 +108,11 @@ namespace NTFSSecurity
continue;
}
if (WriteMissingDestinationFolderError(actualDestination, "MoveError"))
{
continue;
}
try
{
if (item is FileInfo)

18
NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml

@ -1997,7 +1997,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<maml:description>
<maml:para>The `Copy-Item2` cmdlet copies the items in `-Path` to the location in `-Destination`. It is the long-path counterpart of the built-in `Copy-Item` cmdlet: it works through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), so source and destination may be longer than the 260-character `MAX_PATH` limit.</maml:para>
<maml:para>How `-Destination` is interpreted depends on what is already there. If the value names an existing folder, the cmdlet keeps the name of the source item and copies it into that folder. In every other case the value is the full path of the new item, which lets you copy and rename in one step. `-Destination` is resolved against the current location once, when the cmdlet starts.</maml:para>
<maml:para>Without `-Force`, the cmdlet checks whether the destination file already exists and writes a `DestinationFileAlreadyExists` error instead of overwriting it. With `-WhatIf`, it names an existing destination file in a verbose message instead; before 5.0.0, it wrote the error also with `-WhatIf`. With `-Force`, an existing file is replaced. Relative paths and the `.` and `..` notations in `-Path` are resolved against the current location, and wildcard characters are not supported.</maml:para>
<maml:para>Without `-Force`, the cmdlet checks whether a file or folder already exists at the destination and writes a `DestinationFileAlreadyExists` error instead of overwriting it or merging into it. With `-WhatIf`, it names an existing destination in a verbose message instead; before 5.0.0, it wrote the error also with `-WhatIf`. With `-Force`, an existing file is replaced, and a folder is copied into an existing folder of the same name, replacing the files that exist in both. The folder that is to contain the new item must exist; otherwise the cmdlet writes an error that names that folder. Relative paths and the `.` and `..` notations in `-Path` are resolved against the current location, and wildcard characters are not supported.</maml:para>
<maml:para>The cmdlet supports `-WhatIf` and `-Confirm`, and it writes nothing to the pipeline unless you specify `-PassThru $true`.</maml:para>
</maml:description>
<command:syntax>
@ -2041,7 +2041,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>Force</maml:name>
<maml:description>
<maml:para>Indicates that the cmdlet overwrites an existing destination file. Without `-Force`, an existing file causes the error `DestinationFileAlreadyExists` and the item is not copied.</maml:para>
<maml:para>Indicates that the cmdlet overwrites an existing destination file, and copies a folder into an existing folder of the same name. Without `-Force`, an existing file or folder at the destination causes the error `DestinationFileAlreadyExists` and the item is not copied.</maml:para>
</maml:description>
<dev:type>
<maml:name>SwitchParameter</maml:name>
@ -2102,7 +2102,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>Force</maml:name>
<maml:description>
<maml:para>Indicates that the cmdlet overwrites an existing destination file. Without `-Force`, an existing file causes the error `DestinationFileAlreadyExists` and the item is not copied.</maml:para>
<maml:para>Indicates that the cmdlet overwrites an existing destination file, and copies a folder into an existing folder of the same name. Without `-Force`, an existing file or folder at the destination causes the error `DestinationFileAlreadyExists` and the item is not copied.</maml:para>
</maml:description>
<command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
<dev:type>
@ -2189,7 +2189,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<maml:para>`Copy-Item2` copies through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), which is why it handles source and destination paths that exceed the 260-character `MAX_PATH` limit of the built-in `Copy-Item` cmdlet.</maml:para>
<maml:para>Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confirmation skipped the operation.</maml:para>
<maml:para>Before 5.0.0, copying a folder that contained files failed with a `CopyError` that reported a `DirectoryNotFoundException` for the first file in the folder.</maml:para>
<maml:para>If a path in `-Path` does not exist or the destination file exists and `-Force` is missing, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call.</maml:para>
<maml:para>If a path in `-Path` does not exist, a file or folder exists at the destination and `-Force` is missing, or the folder that is to contain the copy does not exist, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call. Before 5.0.0, it also didn't detect an existing destination folder, so that the copy failed in the middle with a `CopyError` after it had copied a part of the folder; it reported a missing destination folder as a `DirectoryNotFoundException` that named the source item; and, in the prereleases of 5.0.0, it created the missing folders of the destination for a folder.</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
@ -6784,7 +6784,7 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<maml:description>
<maml:para>The `Move-Item2` cmdlet moves the items in `-Path` to the location in `-Destination`. It is the long-path counterpart of the built-in `Move-Item` cmdlet: it works through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), so source and destination may be longer than the 260-character `MAX_PATH` limit. Files and folders can both be moved, and a folder is moved with everything it contains.</maml:para>
<maml:para>How `-Destination` is interpreted depends on what is already there. If the value names an existing folder, the cmdlet keeps the name of the source item and moves it into that folder. In every other case the value is the full path of the new item, which lets you move and rename in one step, or rename an item in place. `-Destination` is resolved against the current location once, when the cmdlet starts.</maml:para>
<maml:para>Without `-Force`, the cmdlet checks whether the destination file already exists and writes a `DestinationFileAlreadyExists` error instead of overwriting it; the move itself then runs with the `CopyAllowed` option, which allows a file to move to a different volume. With `-WhatIf`, the cmdlet names an existing destination file in a verbose message instead; before 5.0.0, it wrote the error also with `-WhatIf`. With `-Force`, the move runs with the `ReplaceExisting` option and overwrites an existing destination item.</maml:para>
<maml:para>Without `-Force`, the cmdlet checks whether a file or folder already exists at the destination and writes a `DestinationFileAlreadyExists` error instead of overwriting it; the move itself then runs with the `CopyAllowed` option, which allows a file to move to a different volume. With `-WhatIf`, the cmdlet names an existing destination in a verbose message instead; before 5.0.0, it wrote the error also with `-WhatIf`. With `-Force`, the move runs with the `ReplaceExisting` option and overwrites an existing destination item. The folder that is to contain the moved item must exist; otherwise the cmdlet writes an error that names that folder.</maml:para>
<maml:para>The cmdlet supports `-WhatIf` and `-Confirm`, and it writes nothing to the pipeline unless you specify `-PassThru $true`.</maml:para>
</maml:description>
<command:syntax>
@ -6828,7 +6828,7 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>Force</maml:name>
<maml:description>
<maml:para>Indicates that the cmdlet replaces an existing destination item. Without `-Force`, an existing destination file causes the error `DestinationFileAlreadyExists` and the item is not moved.</maml:para>
<maml:para>Indicates that the cmdlet replaces an existing destination item. Without `-Force`, an existing file or folder at the destination causes the error `DestinationFileAlreadyExists` and the item is not moved.</maml:para>
</maml:description>
<dev:type>
<maml:name>SwitchParameter</maml:name>
@ -6889,7 +6889,7 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>Force</maml:name>
<maml:description>
<maml:para>Indicates that the cmdlet replaces an existing destination item. Without `-Force`, an existing destination file causes the error `DestinationFileAlreadyExists` and the item is not moved.</maml:para>
<maml:para>Indicates that the cmdlet replaces an existing destination item. Without `-Force`, an existing file or folder at the destination causes the error `DestinationFileAlreadyExists` and the item is not moved.</maml:para>
</maml:description>
<command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
<dev:type>
@ -6975,8 +6975,8 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<maml:alert>
<maml:para>`Move-Item2` moves through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), which is why it handles source and destination paths that exceed the 260-character `MAX_PATH` limit of the built-in `Move-Item` cmdlet.</maml:para>
<maml:para>Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confirmation skipped the operation.</maml:para>
<maml:para>The cmdlet chooses between two mutually exclusive move options. Without `-Force` it moves with `CopyAllowed`, which permits a file to cross volume boundaries because Windows then copies and deletes it. With `-Force` it moves with `ReplaceExisting`, which overwrites the destination but does not request `CopyAllowed`, so a move across volumes can fail when `-Force` is specified.</maml:para>
<maml:para>If a path in `-Path` does not exist or the destination file exists and `-Force` is missing, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call.</maml:para>
<maml:para>The cmdlet chooses between two mutually exclusive move options. Without `-Force` it moves with `CopyAllowed`, which permits a file to cross volume boundaries because Windows then copies and deletes it. With `-Force` it moves with `ReplaceExisting`, which overwrites the destination but does not request `CopyAllowed`, so a move across volumes can fail when `-Force` is specified. A folder can't move to another volume: the cmdlet writes a `MoveError` and leaves the folder in place, so copy it with `Copy-Item2` and remove it with `Remove-Item2` instead.</maml:para>
<maml:para>If a path in `-Path` does not exist, a file or folder exists at the destination and `-Force` is missing, or the folder that is to contain the moved item does not exist, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call. Before 5.0.0, it also reported an existing destination folder as a `MoveError`, and a missing destination folder as a `DirectoryNotFoundException` that named the source item ( #21 (https://github.com/raandree/NTFSSecurity/issues/21)).</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>

83
Tests/ItemCmdlets.Tests.ps1

@ -185,6 +185,89 @@ Describe 'Copy-Item2, Move-Item2, and Remove-Item2 with several paths' {
$result.FullName | Should -Be (Join-Path -Path $destination -ChildPath 'First.txt')
}
It 'Move-Item2 -PassThru should return a folder at its new location' {
$sourceFolder = Join-Path -Path $folder -ChildPath 'MovedFolder'
Assert-TestSandboxPath -Sandbox $sandbox -Path $sourceFolder
New-Item -ItemType Directory -Path $sourceFolder | Out-Null
$result = Move-Item2 -Path $sourceFolder -Destination $destination -PassThru $true
$result | Should -BeOfType [Alphaleonis.Win32.Filesystem.DirectoryInfo]
$result.FullName | Should -Be (Join-Path -Path $destination -ChildPath 'MovedFolder')
$sourceFolder | Should -Not -Exist
}
# Before 5.0.0-rc6, the check for an existing destination looked for a file only. For a folder whose name existed
# in the destination, the cmdlets failed in the middle with a CopyError or a MoveError, and Copy-Item2 could copy
# a part of the folder.
It '<_> should write DestinationFileAlreadyExists for a folder that exists at the destination and change nothing' -ForEach @('Copy-Item2', 'Move-Item2') {
$sourceFolder = Join-Path -Path $folder -ChildPath 'Conflict'
$existingFolder = Join-Path -Path $destination -ChildPath 'Conflict'
Assert-TestSandboxPath -Sandbox $sandbox -Path $sourceFolder, $existingFolder
New-Item -ItemType Directory -Path $sourceFolder, $existingFolder | Out-Null
Set-Content -LiteralPath (Join-Path -Path $sourceFolder -ChildPath 'A.txt') -Value 'New'
Set-Content -LiteralPath (Join-Path -Path $sourceFolder -ChildPath 'B.txt') -Value 'New'
Set-Content -LiteralPath (Join-Path -Path $existingFolder -ChildPath 'A.txt') -Value 'Existing'
& $_ -Path $sourceFolder -Destination $destination -ErrorVariable itemErrors -ErrorAction SilentlyContinue
$itemErrors | Should -HaveCount 1
$itemErrors[0].FullyQualifiedErrorId | Should -BeLike 'DestinationFileAlreadyExists,*'
$itemErrors[0].TargetObject | Should -Be $existingFolder
Get-Content -LiteralPath (Join-Path -Path $existingFolder -ChildPath 'A.txt') | Should -Be 'Existing'
Join-Path -Path $existingFolder -ChildPath 'B.txt' | Should -Not -Exist
Join-Path -Path $sourceFolder -ChildPath 'A.txt' | Should -Exist
}
It 'Copy-Item2 -Force should copy a folder into an existing folder of the same name and replace the files in both' {
$sourceFolder = Join-Path -Path $folder -ChildPath 'Merge'
$existingFolder = Join-Path -Path $destination -ChildPath 'Merge'
Assert-TestSandboxPath -Sandbox $sandbox -Path $sourceFolder, $existingFolder
New-Item -ItemType Directory -Path $sourceFolder, $existingFolder | Out-Null
Set-Content -LiteralPath (Join-Path -Path $sourceFolder -ChildPath 'A.txt') -Value 'New'
Set-Content -LiteralPath (Join-Path -Path $sourceFolder -ChildPath 'B.txt') -Value 'New'
Set-Content -LiteralPath (Join-Path -Path $existingFolder -ChildPath 'A.txt') -Value 'Existing'
Set-Content -LiteralPath (Join-Path -Path $existingFolder -ChildPath 'C.txt') -Value 'Existing'
Copy-Item2 -Path $sourceFolder -Destination $destination -Force -ErrorVariable itemErrors -ErrorAction SilentlyContinue
$itemErrors | Should -BeNullOrEmpty
Get-Content -LiteralPath (Join-Path -Path $existingFolder -ChildPath 'A.txt') | Should -Be 'New'
Get-Content -LiteralPath (Join-Path -Path $existingFolder -ChildPath 'B.txt') | Should -Be 'New'
Get-Content -LiteralPath (Join-Path -Path $existingFolder -ChildPath 'C.txt') | Should -Be 'Existing'
}
# Before 5.0.0-rc6, the error named the source item as the path that wasn't found, also when the folder of the
# destination was missing (#21).
It '<Command> should name the missing folder of the destination for a <Kind> and change nothing' -ForEach @(
@{ Command = 'Copy-Item2'; Kind = 'file'; ErrorId = 'CopyError' }
@{ Command = 'Copy-Item2'; Kind = 'folder'; ErrorId = 'CopyError' }
@{ Command = 'Move-Item2'; Kind = 'file'; ErrorId = 'MoveError' }
@{ Command = 'Move-Item2'; Kind = 'folder'; ErrorId = 'MoveError' }
) {
$source = $first
if ($Kind -eq 'folder') {
$source = Join-Path -Path $folder -ChildPath 'SourceFolder'
Assert-TestSandboxPath -Sandbox $sandbox -Path $source
New-Item -ItemType Directory -Path $source | Out-Null
Set-Content -LiteralPath (Join-Path -Path $source -ChildPath 'Inner.txt') -Value 'Inner'
}
$missingFolder = Join-Path -Path $folder -ChildPath 'MissingFolder'
$target = Join-Path -Path $missingFolder -ChildPath 'Item'
Assert-TestSandboxPath -Sandbox $sandbox -Path $missingFolder, $target
& $Command -Path $source -Destination $target -ErrorVariable itemErrors -ErrorAction SilentlyContinue
$itemErrors | Should -HaveCount 1
$itemErrors[0].FullyQualifiedErrorId | Should -BeLike "$ErrorId,*"
$itemErrors[0].Exception.Message | Should -BeLike "*'$missingFolder'*"
$itemErrors[0].TargetObject | Should -Be $target
$source | Should -Exist
$missingFolder | Should -Not -Exist
}
# Before 5.0.0, -PassThru wrote the item also when -WhatIf skipped the operation.
It '<_> should write nothing with -PassThru and -WhatIf' -ForEach @('Copy-Item2', 'Move-Item2', 'Remove-Item2') {
$parameters = @{ Path = $first; PassThru = $true; WhatIf = $true }

Loading…
Cancel
Save