52 lines
3.4 KiB
TypeScript
52 lines
3.4 KiB
TypeScript
// Config validation of the growth extras: web, revenuecat, prices.ppp, apple.teamId / adhoc.
|
|
import { describe, expect, test } from 'bun:test';
|
|
import { type ShipConfig, validateConfig, validateExtras } from '../cli/lib/config';
|
|
import { TOOLS, toArgv } from '../cli/mcp-tools';
|
|
|
|
const base: ShipConfig = {
|
|
app: { name: 'Example', privacyPolicyUrl: 'https://example.com/privacy', supportUrl: 'https://example.com/support', website: 'https://example.com', contactEmail: 'hi@example.com' },
|
|
languages: [{ id: 'en', play: 'en-US', apple: 'en-US' }],
|
|
apple: { bundleId: 'com.example.app' },
|
|
play: { packageName: 'com.example.app' },
|
|
};
|
|
const fp = Array.from({ length: 32 }, (_, i) => (i * 7 % 256).toString(16).toUpperCase().padStart(2, '0')).join(':');
|
|
|
|
describe('growth extras config', () => {
|
|
test('a full, valid set passes', () => {
|
|
const c: ShipConfig = { ...base, apple: { ...base.apple!, teamId: 'ABCDEFGHIJ', adhoc: { method: 'ad-hoc' } },
|
|
prices: { ppp: { source: 'worldbank', floor: 0.25, ceiling: 1 } },
|
|
web: { domain: 'links.example.com', paths: ['/invite/*'], exclude: ['/admin/*'], androidSha256: [fp], landing: true },
|
|
revenuecat: { projectId: 'proj0example', entitlements: { pro: ['com.example.app.pro'] }, offering: 'default' } };
|
|
expect(validateExtras(c)).toEqual([]);
|
|
expect(validateConfig(c)).toEqual([]);
|
|
});
|
|
test('each mistake is named', () => {
|
|
const c = { ...base, apple: { ...base.apple!, teamId: 'abc', adhoc: { method: 'enterprise' } },
|
|
prices: { ppp: { source: 'file', floor: 2, ceiling: 1 } },
|
|
web: { domain: 'https://example.com/x', paths: ['invite'], androidSha256: ['ab:cd'] },
|
|
revenuecat: { projectId: '', entitlements: { pro: [] }, offering: 'a b' } } as unknown as ShipConfig;
|
|
const e = validateExtras(c).join('\n');
|
|
for (const s of ['apple.teamId', 'apple.adhoc.method', 'prices.ppp.file', 'floor is above ceiling', 'web.domain', 'must start with /', 'web.androidSha256', 'revenuecat.projectId', 'revenuecat.entitlements.pro', 'revenuecat.offering'])
|
|
expect(e).toContain(s);
|
|
});
|
|
test('no extras, no problems', () => expect(validateExtras(base)).toEqual([]));
|
|
});
|
|
|
|
describe('growth extras over MCP', () => {
|
|
test('no console tool exists in MCP', () => {
|
|
expect(TOOLS.filter(t => t.command[0] === 'console' || /console/.test(t.name))).toEqual([]);
|
|
});
|
|
test('Apple Ads, RevenueCat, device registration and internal sharing are read-only in MCP', () => {
|
|
const risky = TOOLS.filter(t => (t.command[0] === 'ads' && t.command[1] === 'apple') || t.command[0] === 'revenuecat' || (t.command[0] === 'play' && t.command[1] === 'share')
|
|
|| (t.command[0] === 'apple' && t.command[1] === 'adhoc' && !['devices', 'options'].includes(t.command[2]!)));
|
|
expect(risky.filter(t => t.write)).toEqual([]);
|
|
for (const t of TOOLS.filter(x => (x.command[0] === 'ads' && x.command[1] === 'apple') || x.command[0] === 'revenuecat')) expect(['status', 'campaigns', 'report', 'diff']).toContain(t.command.at(-1)!);
|
|
});
|
|
test('local writes default to a dry run', () => {
|
|
for (const n of ['prices_ppp', 'web_init', 'apple_adhoc_options']) {
|
|
const t = TOOLS.find(x => x.name === n)!;
|
|
expect(toArgv(t, {}).argv).toContain('--dry');
|
|
}
|
|
expect(toArgv(TOOLS.find(x => x.name === 'prices_ppp')!, { base: 4.99, source: 'worldbank' }).argv).toEqual(['prices', 'ppp', '--base', '4.99', '--source', 'worldbank', '--dry']);
|
|
});
|
|
});
|