Your mission
Build an in-memory order program that validates records, calculates quotes, searches and filters orders, and produces a summary. It is a small program with realistic decisions, not a SaaS application. No DOM, database, payment system or real API is required.
Work independently first. A complete reference implementation and test suite is available for comparison after your attempt. It contains .mjs modules and runs with Node without external packages.
Start with a precise brief
An order has a nonempty string id, a customer record and at least one item. The customer has a nonempty name, a type of standard or premium, and a Boolean active field. An inactive customer cannot receive a quote under this exercise's policy.
Each item has a nonempty name, a nonnegative integer unitPriceCents and a positive integer quantity. Amounts and products must stay within JavaScript's safe integer range. A missing array, missing property, numeric string or fractional quantity is invalid. Do not silently convert or drop it.
Represent money in integer cents to make units explicit. We assume a currency with 100 cents per unit. Real systems may need other monetary conventions; those are outside this exercise.
Confirm the pricing policy
- Sum unitPriceCents multiplied by quantity for every item.
- An active premium customer receives a 10% discount when the subtotal is strictly greater than 10,000 cents.
- Round the discount to the nearest whole cent using Math.round; for nonnegative half-cent values this rounds upward.
- Apply 20% exercise tax to the discounted subtotal and round that tax to the nearest cent using the same rule.
- Add discounted subtotal and tax. There is no delivery charge or promotion stacking in this project.
These fictional policies are fully specified so you can test them. They do not describe actual tax obligations.
Predict before implementing
| Case | Subtotal | Discount | Tax | Total |
|---|---|---|---|---|
| Standard customer, one 10,000-cent item | 10000 | 0 | 2000 | 12000 |
| Premium customer, exactly 10,000 cents | 10000 | 0 | 2000 | 12000 |
| Premium customer, 15,000 cents | 15000 | 1500 | 2700 | 16200 |
| Standard customer, a 103-cent item | 103 | 0 | 21 | 124 |
| Inactive customer | ? | ? | ? | Reject |
Compute the table by hand. A successful normal quote is insufficient: the exact threshold and a fractional tax case reveal different decisions.
Design the data and responsibilities
validation.mjs: validate the order, customer, items and collection ids
pricing.mjs: calculate subtotal, eligibility, discount, tax and total
orders.mjs: search, filter and summarize valid and invalid orders
main.mjs: prepare sample input and display results
tests.mjs: execute behavioral checks
The reference keeps calculation helpers together rather than creating one file per formula. A quote returns its component amounts so the result can be explained. Query functions return matching source records without modifying them.
Define reporting rules too
Order ids must be unique within a collection. Reject a duplicate-id collection because a search would otherwise be ambiguous. Structural errors in a record should be identified, not omitted from a report silently.
A summary contains accepted quotes and invalid records with reasons. Its total counts only valid quotes and must be labeled as such. For an empty collection, accepted and invalid lists are empty, total is zero and average is null. A non-array input is invalid.
Searching a valid unique collection by id returns the matching record or null. Filtering by customer type returns an array. These operations do not themselves approve an order: quote validation makes that decision.
Implement in deliberate stages
- Write requirements and open questions in your own words.
- Create your expected-result table, including missing data and boundaries.
- Write validation before arithmetic can consume invalid inputs.
- Implement one quote and test its component amounts.
- Add collection queries and the summary.
- Separate modules once responsibilities are clear.
- Run the complete suite after each refactor.
Do not add an interface to conceal an uncertain rule. A console report is enough to demonstrate correctness at this stage.
Required tests
Cover standard and premium customers, the exact threshold, above-threshold eligibility, inactive customers, missing names, unsupported customer types, no items, zero quantity, fractional quantity, text prices, nonfinite amounts, unsafe totals, rounding, duplicate ids, empty collections, absent searches and input immutability.
Include a mixed collection with one valid and one invalid order. Verify that the invalid record is reported and excluded from the financial total. Test the returned details, not only whether the function completed.
Run the reference after your attempt
Extract the download. In its folder run node tests.mjs, then node main.mjs. Read README.md for module responsibilities and expected output. The reference's error strings are implementation choices; your solution can use other clear wording if its failure behavior remains explicit and tested.
Change request: demonstrate your mindset
The owner changes eligibility to "10,000 cents or more." Before changing code, identify the expected row that changes. Then add a second request: "special discounts for some customers." Explain why the second request cannot yet be implemented faithfully.
Review and correction
At exactly 10,000 cents, a premium quote now has discount 1000, discounted subtotal 9000, tax 1800 and total 10800. The standard quote remains 12000. The 15,000-cent premium case stays 16200. Special discounts still require eligibility, rate, priority and combination rules. List those questions rather than inventing answers.
Submission rubric
| Area | Weight | Evidence |
|---|---|---|
| Understanding and policy | 25% | Clear rules, units, assumptions and examples |
| Correct behavior | 25% | Valid quotes, explicit rejection and honest reports |
| Tests | 20% | Boundaries, failure cases and unchanged input |
| Organization | 15% | Responsibilities and readable function contracts |
| Reasoning and revision | 15% | Counterexample, alternatives and change analysis |
Submit your source, test command, brief and decision journal. Explain what remains outside scope, such as persistence and concurrency. A maintainable program makes both its behavior and its limits understandable.