// Tests for pricing calculation engine import { readFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import { createRequire } from 'module'; const __dirname = dirname(fileURLToPath(import.meta.url)); const require = createRequire(import.meta.url); // Load the pricing library (CommonJS-style) const pricingPath = join(__dirname, '..', 'src', 'lib', 'pricing.js'); const pricingCode = readFileSync(pricingPath, 'utf-8'); // Simulate browser global global.window = {}; global.module = undefined; // Execute in a context that exposes PricingCalculator eval(pricingCode); const { PricingCalculator } = global.window; let passed = 0; let failed = 0; function assert(condition, message) { if (condition) { passed++; console.log(' ✅ ' + message); } else { failed++; console.error(' ❌ FAIL: ' + message); } } function approxEqual(a, b, tolerance) { return Math.abs(a - b) < (tolerance || 0.01); } console.log('\n=== Pricing Calculator Tests ===\n'); // Test 1: calcApiCost console.log('1. calcApiCost'); const usage = { messagesPerMonth: 500, avgInputTokens: 800, avgOutputTokens: 400 }; // GPT-4o: input $2.50/1M, output $10.00/1M // 500 msgs * 800 tokens = 400,000 input tokens = 0.4M → 0.4 * 2.50 = $1.00 // 500 msgs * 400 tokens = 200,000 output tokens = 0.2M → 0.2 * 10.00 = $2.00 // Total: $3.00 var apiCost = PricingCalculator.calcApiCost(2.50, 10.00, usage); assert(approxEqual(apiCost, 3.00, 0.01), 'GPT-4o API cost at 500 msgs = $3.00 (got $' + apiCost + ')'); // Test 2: calcApiCost for DeepSeek (extremely cheap) console.log('\n2. calcApiCost - DeepSeek'); // DeepSeek V3: input $0.27/1M, output $1.10/1M // 500 * 800 / 1M * 0.27 = 0.108, 500 * 400 / 1M * 1.10 = 0.22 → $0.328 var dsCost = PricingCalculator.calcApiCost(0.27, 1.10, usage); assert(approxEqual(dsCost, 0.33, 0.01), 'DeepSeek API cost at 500 msgs ≈ $0.33 (got $' + dsCost + ')'); // Test 3: calcSubscriptionCostPerMessage console.log('\n3. calcSubscriptionCostPerMessage'); var subPerMsg = PricingCalculator.calcSubscriptionCostPerMessage(20, 500); assert(approxEqual(subPerMsg, 0.04, 0.001), '$20/mo at 500 msgs = $0.04/msg (got $' + subPerMsg + ')'); // Test 4: findBreakEven console.log('\n4. findBreakEven'); var breakEven = PricingCalculator.findBreakEven(20, 2.50, 10.00, 800, 400); // cost per msg API = 800/1M * 2.50 + 400/1M * 10.00 = 0.002 + 0.004 = 0.006 // break even = 20 / 0.006 = 3333.33 → 3333 assert(breakEven === 3333, 'GPT-4o break-even at 3333 msgs (got ' + breakEven + ')'); // Test 5: compare — full tool comparison console.log('\n5. compare - full comparison'); var chatgptPlus = { id: 'chatgpt-plus', name: 'ChatGPT Plus', priceMonthly: 20, apiEquivalent: { model: 'gpt-4o', inputPer1M: 2.50, outputPer1M: 10.00 } }; var result = PricingCalculator.compare(chatgptPlus, usage); assert(result.cheaperOption === 'api', 'ChatGPT Plus: API should be cheaper'); assert(result.savingsMonthly > 0, 'Should show positive savings: $' + result.savingsMonthly); assert(result.apiMonthlyCost < 20, 'API cost should be < $20 (got $' + result.apiMonthlyCost + ')'); // Test 6: compareAll console.log('\n6. compareAll - batch comparison'); var tools = [ { id: 'a', name: 'A', priceMonthly: 20, apiEquivalent: { inputPer1M: 2.50, outputPer1M: 10.00 } }, { id: 'b', name: 'B', priceMonthly: 20, apiEquivalent: { inputPer1M: 0.27, outputPer1M: 1.10 } }, { id: 'c', name: 'C', priceMonthly: 0, apiEquivalent: { inputPer1M: 0, outputPer1M: 0 } } // no API ]; var results = PricingCalculator.compareAll(tools, usage); assert(results.length === 2, 'Should skip tools with no API pricing (got ' + results.length + ')'); assert(results[0].toolId === 'b', 'Should sort by savings descending (B first — DeepSeek-like saves more)'); // Test 7: classifyFreeTier console.log('\n7. classifyFreeTier'); var demoTool = { freeTier: { exists: true, type: 'demo', realValue: 'test' } }; var generousTool = { freeTier: { exists: true, type: 'generous', realValue: 'good' } }; var noFreeTool = { freeTier: null }; assert(PricingCalculator.classifyFreeTier(demoTool).type === 'demo', 'Should classify demo tier'); assert(PricingCalculator.classifyFreeTier(generousTool).type === 'generous', 'Should classify generous tier'); assert(PricingCalculator.classifyFreeTier(noFreeTool).type === 'none', 'Should classify no free tier'); // Test 8: formatMoney console.log('\n8. formatMoney'); assert(PricingCalculator.formatMoney(0) === 'Free', 'formatMoney(0) = "Free"'); assert(PricingCalculator.formatMoney(20) === '$20.00', 'formatMoney(20) = "$20.00"'); assert(PricingCalculator.formatMoney(3.456) === '$3.46', 'formatMoney(3.456) = "$3.46"'); // Test 9: Edge case — zero messages console.log('\n9. Edge cases'); var zeroUsage = { messagesPerMonth: 0, avgInputTokens: 800, avgOutputTokens: 400 }; var zeroResult = PricingCalculator.compare(chatgptPlus, zeroUsage); assert(zeroResult.apiMonthlyCost === 0, 'Zero usage → $0 API cost'); // Test 10: Edge case — very high usage (subscription cheaper) console.log('\n10. High usage scenario'); var highUsage = { messagesPerMonth: 10000, avgInputTokens: 2000, avgOutputTokens: 1000 }; // API: 10000 * 2000 / 1M * 2.50 = 50, 10000 * 1000 / 1M * 10 = 100 → $150 var highResult = PricingCalculator.compare(chatgptPlus, highUsage); assert(highResult.cheaperOption === 'subscription', 'At 10K msgs, subscription should be cheaper'); assert(approxEqual(highResult.apiMonthlyCost, 150, 1), 'API cost at 10K msgs ≈ $150 (got $' + highResult.apiMonthlyCost + ')'); // Test 11: Pricing database loads correctly console.log('\n11. Pricing database'); var dbPath = join(__dirname, '..', 'src', 'data', 'pricing-database.js'); var dbCode = readFileSync(dbPath, 'utf-8'); global.window = {}; eval(dbCode); assert(global.window.AptDB !== undefined, 'AptDB should be defined'); assert(global.window.AptDB.tools.length >= 10, 'Should have at least 10 tools (got ' + global.window.AptDB.tools.length + ')'); assert(global.window.AptDB.pricingPatterns !== undefined, 'pricingPatterns should be defined'); assert(global.window.AptDB.version !== undefined, 'version should be defined'); console.log('\n=== Results: ' + passed + ' passed, ' + failed + ' failed ===\n'); process.exit(failed > 0 ? 1 : 0);