feat: X API Cost Crisis Alert Chrome extension MVP with cost calculator, 8 alternatives, and 12 passing tests
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
import { calculateXCost, getAlternativesForUseCase, xApiAlternatives } from '../src/data/alternatives.js'
|
||||
|
||||
describe('calculateXCost', () => {
|
||||
it('returns Free tier for 0 reads and 0 writes', () => {
|
||||
const result = calculateXCost(0, 0)
|
||||
assert.strictEqual(result.tier, 'Free')
|
||||
assert.strictEqual(result.cost, 0)
|
||||
assert.strictEqual(result.overage, false)
|
||||
})
|
||||
|
||||
it('returns Free tier for 1500 reads and 0 writes', () => {
|
||||
const result = calculateXCost(1500, 0)
|
||||
assert.strictEqual(result.tier, 'Free')
|
||||
assert.strictEqual(result.cost, 0)
|
||||
})
|
||||
|
||||
it('returns Basic tier for 5000 reads and 100 writes', () => {
|
||||
const result = calculateXCost(5000, 100)
|
||||
assert.strictEqual(result.tier, 'Basic')
|
||||
assert.strictEqual(result.cost, 100)
|
||||
})
|
||||
|
||||
it('returns Pro tier for 500000 reads and 100000 writes', () => {
|
||||
const result = calculateXCost(500000, 100000)
|
||||
assert.strictEqual(result.tier, 'Pro')
|
||||
assert.strictEqual(result.cost, 5000)
|
||||
})
|
||||
|
||||
it('returns Enterprise tier for 5M reads and 500K writes', () => {
|
||||
const result = calculateXCost(5000000, 500000)
|
||||
assert.strictEqual(result.tier, 'Enterprise')
|
||||
assert.strictEqual(result.cost, 42000)
|
||||
})
|
||||
|
||||
it('returns Enterprise+ with overage for 25M reads', () => {
|
||||
const result = calculateXCost(25000000, 1000000)
|
||||
assert.strictEqual(result.tier, 'Enterprise+')
|
||||
assert.strictEqual(result.overage, true)
|
||||
assert.strictEqual(result.cost, 92000)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAlternativesForUseCase', () => {
|
||||
it('returns RSS alternatives for rss filter', () => {
|
||||
const alts = getAlternativesForUseCase('rss')
|
||||
assert.ok(alts.length > 0)
|
||||
assert.ok(alts.some(a => a.name.includes('RSSHub')))
|
||||
})
|
||||
|
||||
it('returns scraping alternatives for scraping filter', () => {
|
||||
const alts = getAlternativesForUseCase('scraping')
|
||||
assert.ok(alts.some(a => a.name.includes('TweetScraper')))
|
||||
})
|
||||
|
||||
it('returns default alternatives for unknown filter', () => {
|
||||
const alts = getAlternativesForUseCase('unknown-category')
|
||||
assert.ok(alts.length > 0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('xApiAlternatives data', () => {
|
||||
it('has at least 8 alternatives', () => {
|
||||
assert.ok(xApiAlternatives.length >= 8)
|
||||
})
|
||||
|
||||
it('every alternative has required fields', () => {
|
||||
for (const alt of xApiAlternatives) {
|
||||
assert.ok(alt.name, 'name is required')
|
||||
assert.ok(alt.website, 'website is required')
|
||||
assert.ok(alt.freeTier, 'freeTier is required')
|
||||
assert.ok(alt.paidTier, 'paidTier is required')
|
||||
assert.ok(alt.description, 'description is required')
|
||||
assert.ok(alt.features.length > 0, 'features must not be empty')
|
||||
assert.ok(alt.bestFor, 'bestFor is required')
|
||||
}
|
||||
})
|
||||
|
||||
it('all websites are valid URLs', () => {
|
||||
for (const alt of xApiAlternatives) {
|
||||
assert.ok(alt.website.startsWith('http'), `${alt.name} website must start with http`)
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user