Define what an empty result means.
Check filters, time zones, account scope and permissions against the expected records. An empty filtered dataset may be correct. Record whether a valid zero-row report is allowed before labelling it a failure.
Inspect the actual generated file.
Locate the artifact for the exact export ID. Compare the intended format, row count and content with the stored output. A CSV containing only headers has bytes but may have no records; a size check alone is not enough.
Validate what matters for that report. Mark completion after the artifact is durably available and validated, not immediately after the job is queued.
Check what the customer downloaded.
Use the same account permissions and download route. Inspect the status, content type, redirects and response bytes. A login page or JSON error saved with a .csv filename is not a usable CSV.
Check upload completion, object identifiers, expiring links and cache behaviour. Ensure the download request references the same artifact the worker produced.
Check the actual file, not its extension.
Open a small malformed export in the CSV validator. It reports record 3 on physical line 4: three columns where the header defines two. The previous record spans two lines because its note is quoted.
The note pending,review needs quotes so its comma stays inside one field. Compare the corrected file: three data records, two columns and no structure errors. A compliant CSV writer should quote embedded delimiters and double inner quotes; joining values with commas is not enough.
Passing this check establishes CSV structure. It does not prove the export contains every expected customer record. Compare record IDs, filters and totals against a known development dataset too.
Why does the CSV contain headers but no rows?
Compare the export query with a known record using the same account, filters and time zone. If the query returns rows, check pagination, stream completion and whether the worker uploaded the final file rather than an early buffer.
Test with a CSV parser and an expected record, not just file size or line count. Quoted CSV fields can contain line breaks. A report with no matching records can correctly contain only headers.
Why does the download contain a login page?
Inspect the redirect chain and final response. An expired session can redirect a download to sign-in and end with HTTP 200 containing HTML. Verify both authorisation and the actual file format before showing “Downloaded”.
Keep the file private. Restore the session or return a clear authentication error; do not remove the permission check to make the download succeed.
Why does a completed export return 403?
Check the object key, permissions, signed-link expiry and the credentials used to sign it. With S3, temporary credentials can expire before the link’s stated expiry. See AWS’s presigned URL guidance.
After checking the customer still has access and the artifact still exists, issue a fresh link. Do not regenerate a large export just because its download link expired. Treat an expected expired-link response separately from a failure to provide the promised file.
Test the successful customer result.
Include a known dataset, a valid empty dataset, a failed generation and a denied download in your tests. Assert the output and the protected download, not just the worker’s completed status.
Download the runnable export example and run node export-result.mjs. It checks four authored cases, including a valid empty result. It uses no customer data, packages or network calls, and does not claim to prove client-side delivery.
Use an explicit failure event when output violates the configured contract. Do not infer failure merely because a customer never clicked download. Get test cases for the rest of your customer flow.
Keep the later failure attached to the export.
- Export requested
- Export completed
- Download failed
A shared export attempt ID lets you relate a download failure to the earlier completion message. See how Braith follows exports and downloads.
