Data and reporting

28 CSV import test cases, including the ones that corrupt data silently

Twenty-eight test cases for CSV import and bulk operations — encoding, date ambiguity, leading zeros, formula injection, partial failure and the re-import that duplicates everything.

  • 28 cases
  • 7 coverage types
  • Last verified Sep 16, 2026

A CSV import either fails loudly or corrupts your data quietly. The loud failures are easy — a wrong file type, a missing column, a file that is not a CSV at all. Every team tests those.

The quiet ones are the reason this set exists. A phone number that loses its leading zero. A date that reads 03/04 as March the fourth in one country and April the third in another. A re-import that creates a second copy of every row instead of updating the first. None of these produce an error. They produce a database that is wrong in a way nobody notices for a month.

Twenty-eight cases below, weighted towards the quiet half.

Type
Priority

Showing 28 of 28 cases

28 test cases for Test cases for CSV import and bulk operations
IDTest caseTypePriorityPreconditionsStepsTest dataExpected result
CSV-01A valid file imports every rowFunctionalHighAn import-capable account
  1. Upload a well-formed file
  2. Confirm the import
100 valid rows100 records created, and the reported count matches what is in the database.
CSV-02Columns can be mapped to fieldsFunctionalHighThe file's headers differ from the field names
  1. Upload
  2. Map each column
  3. Confirm
headers in a different orderValues land in the mapped fields. Column order in the file does not matter.
CSV-03A preview is shown before anything is writtenFunctionalHigh
  1. Upload a file
  2. Stop at the preview
10 rowsThe first rows and the detected mapping are shown, and nothing is written until the import is confirmed.
CSV-04Cancelling at the preview writes nothingFunctionalHighA file at the preview stage
  1. Cancel
No records are created and no partial state is left behind.
CSV-05Re-importing the same file updates rather than duplicatesData integrityHighThe file was already imported once
  1. Import the identical file again
the same 100 rowsStill 100 records. Matching is on the documented key, not on row position.
CSV-06A partial failure reports every bad rowFunctionalHighA file with some invalid rows
  1. Import a file of 100 rows where 5 are invalid
95 valid, 5 invalidThe outcome is stated per the documented contract, and all five failures are named with their row numbers and reasons.
CSV-07The failed rows can be downloaded and re-importedFunctionalMediumAn import with failures
  1. Download the error file
  2. Fix the rows
  3. Import it
the error fileThe file has the original columns plus a reason, and the corrected version imports cleanly.
CSV-08Progress is visible on a long importUsabilityMediumA large file
  1. Import 50000 rows
  2. Watch the interface
50000 rowsProgress is reported and the page is usable. The request does not simply hang until it is done.
CSV-09A non-CSV file is refusedNegativeHigh
  1. Upload an executable renamed with a csv extension
a renamed binaryRefused on content, not on extension alone.
CSV-10An empty file is refusedNegativeHigh
  1. Upload a zero-byte file
Refused with a clear message. Not an unhandled error.
CSV-11A header-only file is refused or reports zeroNegativeMedium
  1. Upload a file with headers and no rows
headers onlyHandled per the documented contract, with a message. Never a success claiming rows were imported.
CSV-12A missing required column is refusedNegativeHigh
  1. Upload a file without a required column
no email columnRefused before any row is written, naming the missing column.
CSV-13A duplicate header is refusedNegativeMedium
  1. Upload a file with the same header twice
two email columnsRefused with an explanation, rather than silently keeping one of them.
CSV-14Broken quoting is reported with a row numberNegativeHigh
  1. Upload a file with an unclosed quote
an unterminated quoted fieldThe error names the row, and no partial import runs.
CSV-15A row with too many columns is reportedNegativeMedium
  1. Upload a file where one row has an extra comma
one ragged rowThat row is reported by number. The extra value is never shifted into the next field.
CSV-16A file over the size limit is refusedBoundaryMediumThe documented limit is known
  1. Upload a file above the limit
limit plus 1MBRefused before the whole file is read, with the limit stated.
CSV-17A row count over the limit is refusedBoundaryMediumThe documented row limit is known
  1. Upload a file above the row limit
limit plus 1 rowRefused with the limit stated and a suggestion to split the file.
CSV-18A cell at the maximum field length imports wholeBoundaryMedium
  1. Import a row with a cell at the field maximum
255 charactersStored whole, not truncated without warning.
CSV-19A one-row file importsBoundaryLow
  1. Upload a file with a header and one row
1 rowOne record created.
CSV-20Leading zeros surviveData integrityHigh
  1. Import a column of values with leading zeros
00123 and 0044 7700 900123The values are stored as written. They are not read as numbers and shortened.
CSV-21Long numbers are not turned into scientific notationData integrityHigh
  1. Import an 18-digit identifier
123456789012345678Stored exactly. Not rounded and not rendered as an exponent.
CSV-22Dates are read in one documented orderData integrityHigh
  1. Import rows with ambiguous dates
03/04/2026 and 04/03/2026Both are read per the stated format, and an unparseable date is an error rather than a guess.
CSV-23UTF-8 with a byte order mark imports cleanlyData integrityHighA file exported from a spreadsheet on Windows
  1. Import a UTF-8 file with a BOM and non-Latin characters
a BOM and accented charactersThe first header is not prefixed with a stray character, and the accents survive.
CSV-24Both line endings are acceptedData integrityMedium
  1. Import the same file with CRLF and with LF endings
the same file twiceIdentical results.
CSV-25A formula in a cell is neutralised on exportSecurityHighThe data can be exported again
  1. Import a cell beginning with an equals sign
  2. Export the data
  3. Open the export in a spreadsheet
=1+1 and =cmd |'/c calc'!A0The cell is escaped on export so the spreadsheet shows text rather than evaluating it.
CSV-26A row cannot be imported into another accountSecurityHighTwo accounts
  1. Import a file containing an id belonging to another account
another account's record idRefused or ignored. The import respects the same ownership rules as the interface.
CSV-27An import that fails partway leaves no half-written stateData integrityHighA file that fails at the last row
  1. Import it
  2. Inspect the data afterwards
99 good rows and a fatal oneThe result matches the documented contract — all or nothing, or a named partial set — and there are no orphaned child records either way.
CSV-28The upload control is keyboard operable and errors are announcedAccessibilityHighScreen reader running
  1. Reach the file control and choose a file with the keyboard
  2. Trigger a validation error
The control is reachable and labelled, and the error is announced rather than only shown.

Case IDs are positional within this set, not stable identifiers. Import the set, then let your own tool assign its IDs.

How to use this set

Build one deliberately nasty fixture file and keep it in the repository. Most of the quiet cases here — the leading zeros, the long number, the ambiguous date, the BOM, the formula — can live as five columns in a single file, so one import exercises all of them.

The re-import case (CSV-05) decides the shape of the whole feature. Import-only means every correction is a manual edit afterwards, which is why people re-upload the file and end up with two of everything.

What we deliberately left out

Spreadsheet formats with multiple sheets, formulas and merged cells are a different parsing problem and get their own set. Scheduled and API-driven imports share most of these cases but add retry and partial-batch behaviour worth covering separately.

Questions about this set

Our import runs in a background job. Does this still apply?

Yes, and add the job cases — what a user sees while it runs, what happens if the worker restarts halfway, and whether the same file can be queued twice.

Why treat leading zeros as a high priority?

Because postcodes, phone numbers and account references all have them, the failure is invisible at import time, and it is usually discovered when someone cannot be contacted.

Is all-or-nothing better than a partial import?

Neither is wrong. What is wrong is not documenting which one you do, because the user's next action depends entirely on it.

Somewhere to keep these once you have run them

Tesbo holds the cases, the runs and the results in one place, so the next release starts from what the last one proved.

Where this coverage comes from