The bug
An online furniture shop imports prices from its supplier’s file. The file uses Romanian number format, a dot for thousands and a comma for decimals, so a sofa is listed as 1.299,00, meaning 1,299 lei. The import script turns that text into a number with JavaScript’s parseFloat, which reads from the left and stops at the first character it can’t use. It takes the dot as a decimal point, stops at the comma and returns 1.299, a little over one leu. Rounded to two decimals, the sofa costs 1.30 lei.
How it shows up
The import finishes without an error, and every product at 1,000 lei or more now sells for a thousandth of its price. Cheaper products break too, less visibly: 999,99 comes out as 999, because parseFloat stops at the comma and drops the bani. Number("1.299,00") would have returned NaN and failed loudly; parseFloat fails quietly. The first people to notice are customers who find a sofa at 1.30 lei and order it.
How a test finds it
Feed the import the prices that trip a lenient parser and assert the exact values that come out. A value a thousand times too small, or one that lost its bani, fails. Anyone can check this in a browser console: parseFloat("1.299,00") returns 1.299.
The fix
Parse prices with explicit rules for the supplier’s format: remove the thousands dots, turn the decimal comma into a point, then convert. Reject any value that doesn’t match the pattern, so a bad row stops the import. Hold any import that moves a price by more than a set percentage until someone approves it.
The test that catches it
Import tests with the fixtures 1.299,00, 999,99 and 12.345,50, asserting 1299.00, 999.99 and 12345.50 exactly, plus a test that a price change above the threshold waits for approval. They run on every change to the import script.
import { test, expect } from "@playwright/test";
import { importPrices, parsePrice } from "../src/import";
const fixtures: [text: string, lei: number][] = [
["1.299,00", 1299.0],
["999,99", 999.99],
["12.345,50", 12345.5],
];
for (const [text, lei] of fixtures) {
test(`reads ${text} as ${lei} lei`, () => {
expect(parsePrice(text)).toBe(lei);
});
}
test("a value that doesn't match the format stops the import", () => {
expect(() => parsePrice("1.299")).toThrow();
});
test("a large price change waits for approval", async () => {
const result = await importPrices([{ sku: "SOFA-3", price: "1,30" }], {
current: { "SOFA-3": 1299 },
});
expect(result.held).toEqual(["SOFA-3"]);
});
What it puts at risk
The most expensive stock, sold for almost nothing until someone notices. Then come the cancellations, the refunds and the customers who feel cheated.
