storebound/tests/appinfo-drift.test.ts

68 lines
3.9 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { availableTerritories, blockedItems, currentBasePrice, planAvailability } from '../cli/apple/appinfo';
import { reviewDetailDiff } from '../cli/apple/review-details';
const price = (id: string, startDate: string | null, point: string) => ({ type: 'appPrices', id, attributes: { startDate }, relationships: { appPricePoint: { data: { type: 'appPricePoints', id: point } } } });
const point = (id: string, customerPrice: string) => ({ type: 'appPricePoints', id, attributes: { customerPrice } });
describe('currentBasePrice', () => {
test('paid price is shown as a number', () => {
expect(currentBasePrice({ data: [price('a', null, 'p1')], included: [point('p1', '4.99')] })).toBe('4.99');
});
test('zero is Free', () => {
expect(currentBasePrice({ data: [price('a', null, 'p1')], included: [point('p1', '0.0')] })).toBe('Free');
});
test('latest started price wins, future ones are ignored', () => {
const page = { data: [price('a', null, 'p1'), price('b', '2026-01-01', 'p2'), price('c', '2099-01-01', 'p3')], included: [point('p1', '0'), point('p2', '1.99'), point('p3', '9.99')] };
expect(currentBasePrice(page, '2026-06-01')).toBe('1.99');
});
test('unreadable gives null', () => {
expect(currentBasePrice(null)).toBeNull();
expect(currentBasePrice({ data: [price('a', null, 'zz')], included: [] })).toBeNull();
});
});
describe('availability', () => {
const ta = (t: string, available: boolean) => ({ attributes: { available }, relationships: { territory: { data: { id: t } } } });
test('availableTerritories lists only available ones across pages', () => {
expect(availableTerritories([{ data: [ta('AAA', true), ta('BBB', false)] }, { data: [ta('CCC', true)] }])).toEqual(['AAA', 'CCC']);
});
test('same set and flag: no difference', () => {
expect(planAvailability({ availableInNewTerritories: true, available: ['A', 'B'] }, ['A', 'B']).differs).toBe(false);
});
test('missing territories are additions', () => {
const p = planAvailability({ availableInNewTerritories: true, available: ['A'] }, ['A', 'B', 'C']);
expect(p).toMatchObject({ add: ['B', 'C'], remove: [], differs: true, from: '1 territories', to: '3 territories' });
});
test('extra territories are removals; flag off is a change', () => {
const p = planAvailability({ availableInNewTerritories: false, available: ['A', 'X'] }, ['A']);
expect(p.remove).toEqual(['X']);
expect(p.newFlagChange).toBe(true);
});
test('blockedItems', () => {
expect(blockedItems({ priceDiffers: true, removes: true, allowPrice: false, allowAvailability: false })).toEqual(['price', 'availability removal']);
expect(blockedItems({ priceDiffers: true, removes: true, allowPrice: true, allowAvailability: true })).toEqual([]);
expect(blockedItems({ priceDiffers: false, removes: false, allowPrice: false, allowAvailability: false })).toEqual([]);
});
});
describe('reviewDetailDiff', () => {
const want = { contactPhone: '+15550001111', contactEmail: 'someone@example.com', demoAccountRequired: true, demoAccountName: 'demo-user', demoAccountPassword: 'hunter2', notes: 'Hello' };
test('lists only changed keys and masks secrets', () => {
const lines = reviewDetailDiff({ ...want, demoAccountPassword: 'old-secret', notes: 'Hi' }, want);
expect(lines).toHaveLength(2);
expect(lines.join('\n')).not.toContain('hunter2');
expect(lines.join('\n')).not.toContain('old-secret');
expect(lines[0]).toBe('demoAccountPassword: ******** -> ********');
expect(lines[1]).toBe('notes: 2 bytes -> 5 bytes');
});
test('nothing changed gives no lines', () => {
expect(reviewDetailDiff({ ...want }, want)).toEqual([]);
});
test('new detail lists everything, phone and email masked', () => {
const lines = reviewDetailDiff(null, want);
expect(lines).toHaveLength(6);
expect(lines.join('\n')).not.toContain('5550001111');
expect(lines.join('\n')).not.toContain('someone@example.com');
});
});