Files
ai-tool-pricing-transparenc…/tests/pricing.test.mjs
T

134 lines
6.2 KiB
JavaScript

// 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);