Pick a failure policy
Write down whether the import is all-or-nothing or accepts valid rows and reports rejects. Test duplicate rows within one file and the same file sent twice. A retry after a timeout must not repeat payouts or enroll the same account twice. Check error files for private data and spreadsheet formulas. Give each job an owner and expiry. The person who uploads a file should not gain access to rows outside their tenant just because the parser runs with a broad service account.
Test cases and proof
| Case | Expected result | Proof to keep |
|---|---|---|
| File includes another tenant ID | Reject foreign row | Row report and database |
| Duplicate row is retried | No duplicate action | Business records |
| Other user downloads error file | Deny | Job result response |
Synthetic example
An import contains nine valid rows and one row for another tenant. Decide whether the whole file fails or valid rows commit with a precise error report. Either way, the foreign row must not change data, and the importer must not hide partial work.
Evidence to keep
Use a synthetic import file with row numbers and unique test IDs. Keep a before snapshot of every affected record, the job report, and an after snapshot. A rejected file may still have written earlier rows if the parser commits as it goes. Test the error download as another user because it may repeat private fields from the input file.
Keep a row-level outcome ledger
A file-level success message can hide partial work. Give every row a source line, business key, validation result, and commit result. That makes a retry after timeout auditable. If the product chooses atomic import, validate before commit and roll back the whole job on a foreign row. If it chooses partial import, never commit the foreign row and return a precise report. Error files need the same tenant scope as the original upload; they often repeat names, account IDs, or rejection reasons.
Related reading
Why these checks matter
OWASP’s file-upload guide covers size, type, naming, and storage controls. Its input-validation guide says external data needs syntax and business checks. An import file can pass file-type checks but contain a row for another tenant. The test separates upload acceptance from row authorization and final commit.
Use a valid CSV row and a changed retry
Synthetic CSV row A-101 has the name “Acme, Ltd” and an embedded quote in its note. Encode both using the parser’s documented CSV quoting rules. It should parse into the correct columns and commit under the allowed policy. An unclosed quote is a separate malformed-input case. Names containing punctuation are not invalid just because a naive split-on-comma parser cannot read them.
Upload file F1 under import key IMP-21, then resend the exact bytes after a forced timeout. Expect the saved result or safe continuation. Send different amounts under the same key. The importer must reject the body mismatch rather than return success for the old job or apply a second file. Save parsed row numbers, business IDs, file hash, and final state.
Separate database import from money dispatch
All-or-nothing applies to the transaction the importer controls. If it sends payments to a provider while parsing, rolling back local rows cannot unsend those payments. Validate and authorize the entire batch first; commit allowed rows and durable work under the chosen policy. Dispatch external actions using stable per-item IDs and reconcile uncertain responses.
Pause after the first work item is created, cancel the job, and check the stated cut-off rule. A partial-import report must name committed, rejected, queued, dispatched, and uncertain items where those states exist. Test error-file cells beginning with a spreadsheet formula marker using harmless text, and protect private result downloads. OWASP upload guidance covers intake; payment retry and commit rules require the separate business design.