# TEST REPORT: Failed Payment Dunning (for Stripe), n8n pack v1.0.0
Run from a **clean unzipped copy of the dist zip** (fresh `npm install` / `pip install`). Started 2026-10-17 06:24:25 BST (UTC+01:00), finished 2026-10-17 06:25:12 BST (UTC+01:00) (times from `date` at run time). Box: Linux, Node v24.22.0, Python 3.13.5. n8n **2.43.7** (npm) on Node v24.22.0, task runner in **internal** mode (deprecated by n8n, see SETUP.md).

**Result: 21/21 static checks + 22/22 end-to-end checks passed.**

## 1. Static validation: `python3 tests/validate_workflow.py`
```
PASS has name, nodes, connections
PASS node names unique
PASS node ids unique
PASS every node has type/typeVersion/position/parameters
PASS connections reference existing nodes
PASS every non-trigger node is connected
PASS exactly one webhook trigger, POST, responseNode
PASS no real-looking secrets shipped
PASS workflow inactive on import
PASS n8n-nodes-base.webhook v2 exists in installed n8n
PASS n8n-nodes-base.set v3.4 exists in installed n8n
PASS n8n-nodes-base.code v2 exists in installed n8n
PASS n8n-nodes-base.if v2.2 exists in installed n8n
PASS n8n-nodes-base.respondToWebhook v1.1 exists in installed n8n
PASS n8n-nodes-base.respondToWebhook v1.1 exists in installed n8n
PASS n8n-nodes-base.removeDuplicates v2 exists in installed n8n
PASS n8n-nodes-base.if v2.2 exists in installed n8n
PASS n8n-nodes-base.code v2 exists in installed n8n
PASS n8n-nodes-base.httpRequest v4.2 exists in installed n8n
PASS n8n-nodes-base.if v2.2 exists in installed n8n
PASS n8n-nodes-base.emailSend v2.1 exists in installed n8n
21/21 passed
```
## 2. End-to-end in real n8n: `tests/e2e_n8n.py`
The harness creates a throwaway n8n home and uses the n8n CLI to import the workflow and an SMTP credential and publish the workflow. It then runs `n8n start` and posts signed sample events. **Slack is a local mock HTTP server and email goes to a local SMTP sink. Both are mocked.**
The harness deletes its throwaway n8n home after a passing run, so no local paths are printed. It also checks that a "Final notice" is sent **only** when `next_payment_attempt` is null. A fixture (attempt 3, retry still scheduled) checks that this case gets the normal notice.
```
PASS n8n import:credentials
PASS n8n import:workflow
PASS n8n publish:workflow
PASS bad signature -> 400
PASS stale timestamp -> 400
PASS tampered body -> 400
PASS no alerts for rejected requests
PASS valid payment_failed -> 200
PASS slack alert sent once
PASS slack text has amount, attempt, retry
PASS customer email sent to jamie@example.com
PASS email has hosted invoice link
PASS duplicate delivery -> 200
PASS duplicate produced no new alert/email
PASS attempt 3 with retry pending -> 200
PASS attempt 3 + retry pending is NOT a final notice
PASS final failed attempt (next_payment_attempt null) -> 200
PASS final notice subject + no-more-retries text
PASS recovered payment -> 200
PASS recovery slack alert, no customer email
PASS first-try paid -> 200
PASS first-try paid is silent

22/22 checks passed (throwaway n8n home deleted)
```
## Not tested
- Real Slack, real SMTP providers, and live Stripe deliveries.
- n8n Cloud, n8n 1.x, and the external task-runner mode.
- The Stripe Dashboard steps in SETUP.md (written from Stripe's docs).
