Testing without side effects.
Unit tests that never touch the network, integration tests in a workspace of their own, and test addresses that never reach a real inbox.
There is no test mode yet
Every ProductCraft API key is live: there are no test keys and no sandbox host. A request made with a key does what it says, whether it comes from production or from your test suite. The three patterns below keep tests from sending real email, spending Trawl credits or changing data your product uses.
1
Unit tests: the SDK's mock fetch
Every generated SDK package has a testing entry point. createMockFetch() is a fetch that answers each operation with the example from the OpenAPI spec, so code under test runs against realistic responses with no network at all.
import { Trawl } from "@productcraft/trawl";
import { createMockFetch } from "@productcraft/trawl/testing";
// Answers every operation with its example from the OpenAPI spec.
// No network, no credits, no side effects.
const fetch = createMockFetch({
// Override one operation by its resource.method name
"jobs.retrieve": { id: "job_1", status: "succeeded", result: { title: "Mug" } },
});
const trawl = new Trawl({
apiKey: "test",
workspaceId: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b",
fetch,
});
const job = await trawl.jobs.retrieve("job_1");
console.log(job.status, fetch.calls.length); // "succeeded" 1- Import it from
@productcraft/<product>/testing(today@productcraft/trawl/testing; the other packages gain it as they move to the generated runtime, see SDKs). - Override an operation by its
resource.methodname with a value (sent as the JSON body with the operation's success status) or a handler that returns aResponse, for example a 429 to test your retry path. fetch.callsrecords every request (operation, method, URL, headers, parsed body), so you can assert what your code sent, including theIdempotency-Key.
2
Integration tests: a workspace of their own
Run tests that call the real API against a separate workspace, never the one your product uses.
- Create a second workspace in the console for tests only, and enable just the products the tests call.
- Mint its API key with a policy that grants only the actions the tests need (for example
trawl.createandtrawl.read), so a test that goes wrong cannot touch anything else. - Create throwaway resources with a recognisable prefix and delete them in your teardown, even when a test fails.
- Keep traffic modest. Rate limits apply per workspace and per client IP exactly as in production; the SDK retries 429s for you, but a load test belongs on your own infrastructure.
3
Mail: mailbox simulator addresses
Mail delivers through Amazon SES, so its mailbox simulator works for every send. Mail to these addresses never reaches a person and never counts against your sending reputation.
| Send to | What happens |
|---|---|
| success@simulator.amazonses.com | Accepted and delivered. The message timeline shows it delivered. |
| bounce@simulator.amazonses.com | A hard bounce: the message is bounced and the address goes on the workspace suppression list, as a real bounce would. |
| complaint@simulator.amazonses.com | Delivered, then reported as spam: the message is complained and the address goes on the suppression list. |
| suppressionlist@simulator.amazonses.com | A hard bounce, as if the address were on the global suppression list. |
| ooto@simulator.amazonses.com | Delivered, then an out-of-office auto-reply. |
Never test with made-up addresses at real domains: each one bounces, and bounces count against the workspace's sending reputation. The bounce and complaint addresses are the way to exercise your Mail webhook handlers for message.bounced and message.complained. Afterwards the address is on your suppression list, so later sends to it are suppressed until you remove it there.
Webhooks and Trawl
- Webhooks: every product's endpoints have Send test (
webhooks.endpoints.test()in the SDK), which delivers a signed sample event with"test": true. Your handler verifies it like any other. See the Webhooks guide. - Trawl: every agent step uses a credit, test workspace or not. Unit-test with the mock fetch, and keep integration jobs small: one seed URL, a narrow schema and a low
max_steps.