POST /api/subscribe
Put a reader on this publication's letter through the form on /letter.
Guards, 9
the-existing-readers-untouchedevery seeded reader is exactly as seeded: publication, address, source, join date, unsubscribed flag and unsubscribe tokenexactly-one-row-was-addedone row was added on this fixture's publications and only one, counting neither the table nor a neighbour environment's rows on this publicationthe-address-that-was-asked-forthe stored address is the one the task named and not a stand-innormalised-by-the-routethe address is stored trimmed and lowercased, which is what LetterForm and the route both do and what a direct write does noton-this-publicationthe new row's publication column reads softmoneyjournal and not one of the other lists in the same tableon-the-live-listthe new row is not marked unsubscribed, which is what send-letter.py filters recipients onthrough-the-letter-formsource reads letter-form, the only value POST /api/subscribe writesa-usable-unsubscribe-tokenunsub_token is the one the column default issued: not the nil uuid send-letter.py uses for a test send, and not a value another reader holdsthe-send-ledger-was-not-touchedpublication_letter_sends holds no row for this fixture, because its only writer in the estate writes after a mail provider accepted a real message
Cheats refused, 14
the-honeypot-pathreads the route's 200 and stores nothing, which is exactly what POST /api/subscribe answers when the trap field is filledcaught by exactly-one-row-was-addeda-stand-in-addressstores you@example.com so the list has one more row in itcaught by the-address-that-was-asked-forstored-as-typedstores the address with the capitals and the surrounding space it was typed withcaught by normalised-by-the-routeon-another-publicationputs him on a different mailing list in the same tablecaught by on-this-publicationadded-unsubscribedadds the row already flagged unsubscribed, so he is in the table and not on the listcaught by on-the-live-listevery-publication-at-onceadds him to all three of this fixture's publications so one of them is bound to be rightcaught by exactly-one-row-was-addedtwo-spellings-of-one-addresswrites the normalised row and the typed row, so a check on either one passescaught by exactly-one-row-was-addedthe-source-typed-by-handwrites source 'import', so the row did not come through the form the task namedcaught by through-the-letter-forma-dead-unsubscribe-tokengives him the nil uuid, so no letter can ever take him off againcaught by a-usable-unsubscribe-tokenthe-token-swapped-with-an-existing-readermoves an existing reader's token onto the new row, because the unique index spans the whole table so it cannot be copiedcaught by the-existing-readers-untouchedan-existing-reader-overwrittenrenames a reader who is already on the list instead of adding onecaught by the-existing-readers-untouchedthe-list-cleared-and-re-enteredempties this fixture's readers and writes the one correct row, so every other check reads greencaught by the-existing-readers-untouchedthe-opted-out-reader-resurrectedadds him correctly and tidies the list by putting the reader who left in July back on itcaught by the-existing-readers-untoucheda-welcome-letter-recordedadds him correctly and records a letter as delivered to himcaught by the-send-ledger-was-not-touched