Exercise: Make It Fail Well
Fix a photo mover that loses photos on a bad day, so a failed copy never deletes a photo, a photo with the same name is never replaced, its results stay apart from its messages and a summary prints whatever happens, then answer four questions on reading errors and debugging while a Pester check tries it for real.
Journeyman · PowerShell · Updated
Every phone fills up eventually, and then the photos have to go somewhere: a laptop, a photo library, a USB drive. A script that moves them is a real time-saver, as long as it never loses one. This exercise's move-photos.ps1 works on a good day. On a bad day, it loses photos without a word: when a copy fails, it deletes the photo anyway, and when the library already has a photo with the same name, it replaces it. Your job is to make it fail well, the way a script you'd hand to a friend or a coworker should. A Pester check runs your script on pretend photos and tells you which jobs are done.
Get the exercise code
Open this lesson's GitHub repository, github.com/ask-codey/exercise-powershell-make-it-fail-well, also linked from the Exercise box beside the lesson. Click Code, then Download ZIP, and unzip it into your terminal-practice folder. Or clone it with Git.
The folder holds:
move-photos.ps1: the photo mover, for you to finish.phone\: three pretend photos standing in for your phone, andphoto-library\, which already has an olderIMG_0002.jpgof its own.reset-photos.ps1: puts the pretend photos back the way they came, whenever you want to run the mover again.answers.txt: four questions, each with a numbered line for your answer.Check.Tests.ps1: the check. You don't need to change it.
What to do
Open PowerShell in the exercise folder, the one with answers.txt in it, and open it in VS Code with code .. First, run the mover as it is, and look at the library's IMG_0002.jpg: the old photo is gone, replaced by the phone's. Phones start numbering again after a reset, so two different photos can share a name. Run .\reset-photos.ps1 to get everything back. The script starts like this:
foreach ($photo in Get-ChildItem $Source -File) {
$to = "$Library\$($photo.Name)"
Copy-Item -Path $photo.FullName -Destination $to
Remove-Item -Path $photo.FullName
Write-Host "Moved $($photo.Name)"
}
- A failed copy never loses a photo. Use
try,catchand-ErrorAction Stopso a failed copy never reachesRemove-Item. Incatch, report it withWrite-Error, naming the photo and saying it's still in the phone folder, thenexit 1. Try it with a library that isn't there:.\move-photos.ps1 -Library .\usb-library. - A photo already in the library is never replaced. Check first with
Test-Path. If the library has one with that name, leave the photo where it is, say so withWrite-Warning, and go on to the next. - Results go down the pipeline. Output only the name of each photo that moved, with
Write-Output, so.\move-photos.ps1 | Measure-Objectcounts them. Keep the heading on the screen withWrite-Host. - A summary, whatever happens. Count the photos that move, and print
Photos moved:and the count withWrite-Host, in afinallyblock, so it prints after a failed run too.
Then answer the four questions in answers.txt, using Reading Errors, and -ErrorAction and Debugging in VS Code.
Check your work
The check uses Pester. If you haven't installed it yet, install it once:
Install-PSResource Pester
Then, from the exercise folder:
Invoke-Pester
At first most tests fail. That's expected: each job you finish turns its tests green. A failing test names the job and says what to look at, for example Expected 1, because a run that failed should end with exit 1 in catch. The check makes its own pretend photos in a throwaway folder and runs your script there, so it never touches phone\ or photo-library\.
If PowerShell says the check can't be loaded because it is not digitally signed, Windows has marked the downloaded files as coming from the internet: from the exercise folder, run Get-ChildItem -Recurse | Unblock-File once, then Invoke-Pester again.
Hints
catchsees only errors that stop. Without-ErrorAction Stop, Copy-Item's error goes by in red and the script carries on to the delete (try,catchandfinally).$photois still there incatch. It holds the photo the loop was on, and$Sourceholds the phone folder's path, which is everything your message needs.continueskips to the next photo. You met it in Stopping Early:breakandcontinue.- One
Write-Outputa photo. A message such as "Moved IMG_0001.jpg" isn't a result: it would end up in a file or a count with the names (Where Output Goes:Write-Output,Write-Hostand Friends). - Something you can't explain? Put a breakpoint on the line, press F5, and step with F10, watching
$toand$moved.
When you're done
Every test is green, and Pester's last line says:
Tests Passed: 13, Failed: 0, Skipped: 0, Inconclusive: 0, NotRun: 0
Run .\reset-photos.ps1, then the mover, and it says:
Moving photos from .\phone to .\photo-library
IMG_0001.jpg
WARNING: IMG_0002.jpg is already in the library. Left it in .\phone.
IMG_0003.jpg
Photos moved: 2
Your warning's words can differ. Now run .\reset-photos.ps1 and .\move-photos.ps1 -Library .\usb-library, and read your error message as if you'd never seen the script: does it tell you what happened, what's safe and what to do next?
Submit your exercise
The exercise is how you finish this lesson. When your work is done, put it on GitHub in a repo of your own, then submit that repo's address here.
On GitHub, click +, then New repository. Name it (for example
exercise-powershell-make-it-fail-well), choose Public, and click Create repository. Leave out the README: your folder already has one.In a terminal in your exercise folder (PowerShell works), send your work to your new repo. If you downloaded the ZIP:
git init -b main git add . git commit -m "My solution" git remote add origin https://github.com/your-name/exercise-powershell-make-it-fail-well.git git push -u origin mainIf you cloned the starter instead, point your copy at your own repo, then commit and push:
git remote set-url origin https://github.com/your-name/exercise-powershell-make-it-fail-well.git git add . git commit -m "My solution" git push -u origin mainPaste your repo's address, such as
https://github.com/your-name/exercise-powershell-make-it-fail-well, into Submit your exercise on this page. The site checks that the repo is public and holds your commits, and then the lesson is complete.
Changed something after you submitted? Push it, and submit again: your latest submission is the one that counts.
So what
Any script can work on a good day. One that stops before it does harm, says what happened and keeps its results clean is one people can trust with their photos, their files and their servers.