Skip to content
Astera Technology

Turn discount fields into an explicit tagged choice

Distinguish a percentage, a fixed amount, zero and no discount.

Download complete case notes and answer keys ↓

Source contract and expected records

This is an original, fully synthetic mapping example. Inputs are plain, acyclic JSON-shaped records with the fields shown; the canonical shape and rejection codes belong to this example. Adapt the contract before using a different source.

The original input has at most one own field: ratePercent or amountUnits. Presence of both is a conflict even when one is zero. A rate is a whole percentage 0–100; fixed amount is a nonnegative safe integer in the source accounting unit. Neither field means kind none. A present null is invalid.

Editable synthetic mapping cases
CaseInput record (JSON)Expected output (JSON)
Tag a rate representation{"ratePercent":10}{"ok":true,"value":{"kind":"rate","percent":10}}
Reject two representations{"ratePercent":10,"amountUnits":20}{"ok":false,"error":"DISCOUNT_CONFLICT"}
Preserve explicit fixed zero{"amountUnits":0}{"ok":true,"value":{"kind":"fixed","amountUnits":0}}
Download editable CSV ↓

Run the local transformation

Run the complete example as a JavaScript module in Node.js. It contains the pure mapping function, all three inputs and literal expected outputs; assertions stop on a mismatch. A rejected case returns ok=false with an error code, rather than a partial canonical record.

Complete function and executable cases
import assert from 'node:assert/strict';

function discountTag(i){
  const fail=error=>({ok:false,error});if(!i||typeof i!=='object'||Array.isArray(i))return fail('DISCOUNT_OBJECT');
  const rate=Object.hasOwn(i,'ratePercent'),fixed=Object.hasOwn(i,'amountUnits');
  if(rate&&fixed)return fail('DISCOUNT_CONFLICT');
  if(rate){if(!Number.isSafeInteger(i.ratePercent)||i.ratePercent<0||i.ratePercent>100)return fail('DISCOUNT_RATE');return {ok:true,value:{kind:'rate',percent:i.ratePercent}};}
  if(fixed){if(!Number.isSafeInteger(i.amountUnits)||i.amountUnits<0)return fail('DISCOUNT_AMOUNT');return {ok:true,value:{kind:'fixed',amountUnits:i.amountUnits}};}
  return {ok:true,value:{kind:'none'}};
}

// Original synthetic cases; expected outputs are literal records.
const cases = [
  {
    "label": "Tag a rate representation",
    "input": {
      "ratePercent": 10
    },
    "expected": {
      "ok": true,
      "value": {
        "kind": "rate",
        "percent": 10
      }
    }
  },
  {
    "label": "Reject two representations",
    "input": {
      "ratePercent": 10,
      "amountUnits": 20
    },
    "expected": {
      "ok": false,
      "error": "DISCOUNT_CONFLICT"
    }
  },
  {
    "label": "Preserve explicit fixed zero",
    "input": {
      "amountUnits": 0
    },
    "expected": {
      "ok": true,
      "value": {
        "kind": "fixed",
        "amountUnits": 0
      }
    }
  }
];
for (const sample of cases) {
  const actual = discountTag(sample.input);
  assert.deepEqual(actual, sample.expected);
  console.log(sample.label, JSON.stringify(actual));
}
Download runnable example ↓

Work through the boundary

Why is amountUnits=0 not the same record as an absent discount field?

Show the worked answer

An explicit zero amount produces {kind:"fixed",amountUnits:0}; absence produces {kind:"none"}. The tag preserves the declared representation without computing a final price.

Related resources