Flow
What each step does with the link now
1Address lookup
One place finds the address and throws an error naming the missing setting.
2Link builder
Gives back a link or an error. The PDF code won't draw without a link.
3Weight orders
Skip the link by a written rule, not by accident.
4Sending step
A saved PDF without the link is made again instead of sent.
5Build test
Fails if new code calls the PDF code directly.
6Repair command
Previews first, rebuilds only unsent orders, reads the file back, sends nothing.
A batch of purchase orders came out of the ordering system without the one button suppliers use, and no part of the system minded.
Each order PDF that goes to a supplier has a link they click to ask for changes to that order. A set of orders made by my command-line tool had no link. Nobody flagged anything else on them. No error, no warning. You had to open one and look.
How an empty address became a finished PDF
The link is the web app's public address plus a signed code for the order. The web app gets its address from its own settings. The command-line tool, which I and my agents both use, runs the same code but never loaded that address.
So the tool asked for a link with no address. The link builder was allowed to hand back nothing, so it did. The PDF code was allowed to skip the link, so it drew the order without a button. Every step treated "no link" as a normal answer, and the broken file got saved like any other.
The test that guarded the bug
There was a test for this in my code. It said the link should be left out when there's no address, and it passed every time. I don't know if I wrote it or an agent did. It's my code either way, and the test kept the bug in place.
One resolver, and a builder that throws
The agent wrote the change, and it closed the gap at every step instead of fixing one command. One place now works out the address, and if it can't, it throws an error that names the setting to set. The link builder returns a link or throws:
It uses the passed address or the configured one, and throws if there's neither.
The PDF code now needs the link and won't draw the order without it. Orders sold by weight are the one exception, by a written rule, since suppliers change those by replying to the email. Before a stored PDF is sent, the sending step checks that the link is in it, and makes the PDF again if it isn't. A test fails the build if new code calls the PDF code directly. And the command-line tool now reads the address from the same settings as the web app.
My own test suite tripped over it
The new rule broke my database tests too, because they had no address either. Annoying, but it was the right error in the right place. They now get a test address when none is set.
Repairing what was already stored
A second command lists every order's link and whether its saved PDF has it. Repair shows a preview first. When you apply it, it locks the order, rebuilds the PDF only if it hasn't been sent, and reads the saved file back to check. It never touches a sent order and never sends anything.
What the check still can't see
The check only proves a link is there, not that it goes to the right place. Orders that were already sent stay as they are. And if the tool was missing one setting, it may be missing others. That audit is still on my list.