/ tests / config / pricing-supplement.test.js
pricing-supplement.test.js
  1  /**
  2   * Supplement tests for src/config/pricing.js
  3   *
  4   * Covers uncovered exported functions:
  5   *   - getLocalPrice: returns formatted price object
  6   *   - getPricingTierDistribution: groups countries by tier
  7   *   - getPriceStatistics: min/max/average/median
  8   *   - overrideCountryPrice: mutates COUNTRY_PRICES; throws for unknown code
  9   */
 10  
 11  import { test, describe } from 'node:test';
 12  import assert from 'node:assert/strict';
 13  import {
 14    getCountryPrice,
 15    getLocalPrice,
 16    getPricingTierDistribution,
 17    getPriceStatistics,
 18    overrideCountryPrice,
 19    COUNTRY_PRICES,
 20    PRICING_CONFIG,
 21  } from '../../src/config/pricing.js';
 22  
 23  describe('getCountryPrice', () => {
 24    test('returns known country price for AU', () => {
 25      const result = getCountryPrice('AU');
 26      assert.equal(result.usdPrice, 247);
 27      assert.equal(result.tier, 'Standard');
 28    });
 29  
 30    test('case-insensitive: au === AU', () => {
 31      const upper = getCountryPrice('AU');
 32      const lower = getCountryPrice('au');
 33      assert.equal(upper.usdPrice, lower.usdPrice);
 34    });
 35  
 36    test('returns base price for unknown country code', () => {
 37      const result = getCountryPrice('ZZ');
 38      assert.equal(result.usdPrice, PRICING_CONFIG.BASE_PRICE_USD);
 39      assert.equal(result.tier, 'Standard');
 40    });
 41  
 42    test('returns Premium+ for SG (capped at ceiling)', () => {
 43      const result = getCountryPrice('SG');
 44      assert.equal(result.tier, 'Premium+');
 45      assert.ok(result.cappedAtCeiling);
 46    });
 47  });
 48  
 49  describe('getLocalPrice', () => {
 50    test('returns amount, currency, currencySymbol, formatted, usdEquivalent for AU', () => {
 51      const { usdPrice } = getCountryPrice('AU');
 52      const countryConfig = { currency: 'AUD', currencySymbol: 'A$' };
 53  
 54      const result = getLocalPrice('AU', countryConfig);
 55  
 56      assert.equal(result.amount, usdPrice);
 57      assert.equal(result.currency, 'AUD');
 58      assert.equal(result.currencySymbol, 'A$');
 59      assert.ok(result.formatted.includes('A$'));
 60      assert.ok(result.formatted.includes(String(usdPrice)));
 61      assert.equal(result.usdEquivalent, usdPrice);
 62    });
 63  
 64    test('works for US (baseline market)', () => {
 65      const result = getLocalPrice('US', { currency: 'USD', currencySymbol: '$' });
 66      assert.equal(result.amount, 297);
 67      assert.equal(result.formatted, '$297');
 68    });
 69  
 70    test('works for unknown country (uses base price)', () => {
 71      const result = getLocalPrice('ZZ', { currency: 'USD', currencySymbol: '$' });
 72      assert.equal(result.amount, PRICING_CONFIG.BASE_PRICE_USD);
 73    });
 74  });
 75  
 76  describe('getPricingTierDistribution', () => {
 77    test('returns object with tier keys', () => {
 78      const dist = getPricingTierDistribution();
 79      assert.ok(typeof dist === 'object');
 80      assert.ok(Array.isArray(dist['Premium+']));
 81      assert.ok(Array.isArray(dist['Premium']));
 82      assert.ok(Array.isArray(dist['Standard']));
 83      assert.ok(Array.isArray(dist['Moderate']));
 84      assert.ok(Array.isArray(dist['Emerging']));
 85      assert.ok(Array.isArray(dist['Developing']));
 86    });
 87  
 88    test('Premium+ tier contains SG and IE', () => {
 89      const dist = getPricingTierDistribution();
 90      const codes = dist['Premium+'].map(e => e.code);
 91      assert.ok(codes.includes('SG'), 'SG should be Premium+');
 92      assert.ok(codes.includes('IE'), 'IE should be Premium+');
 93    });
 94  
 95    test('all countries are distributed (total count matches COUNTRY_PRICES length)', () => {
 96      const dist = getPricingTierDistribution();
 97      const total = Object.values(dist).reduce((sum, arr) => sum + arr.length, 0);
 98      assert.equal(total, Object.keys(COUNTRY_PRICES).length);
 99    });
100  
101    test('each entry has code and price fields', () => {
102      const dist = getPricingTierDistribution();
103      for (const arr of Object.values(dist)) {
104        for (const entry of arr) {
105          assert.ok('code' in entry, 'entry should have code');
106          assert.ok('price' in entry, 'entry should have price');
107        }
108      }
109    });
110  });
111  
112  describe('getPriceStatistics', () => {
113    test('returns min, max, average, median, basePrice, floor, ceiling', () => {
114      const stats = getPriceStatistics();
115      assert.ok(typeof stats.min === 'number');
116      assert.ok(typeof stats.max === 'number');
117      assert.ok(typeof stats.average === 'number');
118      assert.ok(typeof stats.median === 'number');
119      assert.ok(typeof stats.basePrice === 'number');
120      assert.ok(typeof stats.floor === 'number');
121      assert.ok(typeof stats.ceiling === 'number');
122    });
123  
124    test('min <= average <= max', () => {
125      const stats = getPriceStatistics();
126      assert.ok(stats.min <= stats.average, 'min should be <= average');
127      assert.ok(stats.average <= stats.max, 'average should be <= max');
128    });
129  
130    test('max <= ceiling', () => {
131      const stats = getPriceStatistics();
132      assert.ok(stats.max <= stats.ceiling, 'max should be <= ceiling');
133    });
134  
135    test('basePrice matches PRICING_CONFIG.BASE_PRICE_USD', () => {
136      const stats = getPriceStatistics();
137      assert.equal(stats.basePrice, PRICING_CONFIG.BASE_PRICE_USD);
138    });
139  });
140  
141  describe('overrideCountryPrice', () => {
142    test('throws for unknown country code', () => {
143      assert.throws(() => overrideCountryPrice('ZZ', 199, 'test override'), /Unknown country code/);
144    });
145  
146    test('mutates price for known country', () => {
147      const originalPrice = COUNTRY_PRICES['IN'].usdPrice;
148      const originalNotes = COUNTRY_PRICES['IN'].notes;
149  
150      overrideCountryPrice('IN', 99, 'price test override');
151  
152      assert.equal(COUNTRY_PRICES['IN'].usdPrice, 99);
153      assert.ok(COUNTRY_PRICES['IN'].notes.includes('OVERRIDE'));
154      assert.ok(COUNTRY_PRICES['IN'].overridden);
155  
156      // Restore original value
157      COUNTRY_PRICES['IN'].usdPrice = originalPrice;
158      COUNTRY_PRICES['IN'].notes = originalNotes;
159      COUNTRY_PRICES['IN'].overridden = false;
160    });
161  
162    test('accepts lowercase country code', () => {
163      const originalPrice = COUNTRY_PRICES['MX'].usdPrice;
164      const originalNotes = COUNTRY_PRICES['MX'].notes;
165  
166      overrideCountryPrice('mx', 79, 'lowercase test');
167  
168      assert.equal(COUNTRY_PRICES['MX'].usdPrice, 79);
169  
170      // Restore
171      COUNTRY_PRICES['MX'].usdPrice = originalPrice;
172      COUNTRY_PRICES['MX'].notes = originalNotes;
173      COUNTRY_PRICES['MX'].overridden = false;
174    });
175  });