104 lines
6 KiB
TypeScript
104 lines
6 KiB
TypeScript
// Dormant apps: `ignored` per entry, whole app or one store side (cli/lib/registry.ts); storebound-side only.
|
|
import { describe, expect, test } from 'bun:test';
|
|
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import {
|
|
activeProjects, activeStoreApps, findEntry, ignoredStoreIds, ignoredStores, isIgnored, parseRegistry, readRegistry, setIgnore, withIgnore, writeRegistry,
|
|
} from '../cli/lib/registry';
|
|
import { TOOLS, toArgv } from '../cli/mcp-tools';
|
|
|
|
const reg = () => parseRegistry(JSON.stringify({
|
|
version: 1,
|
|
projects: [{ id: 'aaaaaaaaaaaaaaaa', path: '/work/star-dash', name: 'Star Dash', added: '' }, { id: 'bbbbbbbbbbbbbbbb', path: '/work/puzzle', name: 'Puzzle', added: '' }],
|
|
storeApps: [{ store: 'apple', appId: '6400000000', account: 'teamA', name: 'Demo App', bundleId: 'com.example.demo' }, { store: 'play', appId: 'com.example.demo', name: 'Demo Play' }],
|
|
}));
|
|
|
|
describe('registry ignore', () => {
|
|
test('whole app, one store side, restore; only valid sides are kept', () => {
|
|
let r = reg();
|
|
r = withIgnore(r, 'Star Dash', 'apple', true).registry;
|
|
expect(ignoredStores(r.projects[0]!)).toEqual(['apple']);
|
|
expect(isIgnored(r.projects[0]!)).toBe(false);
|
|
expect(isIgnored(r.projects[0]!, 'apple')).toBe(true);
|
|
expect(isIgnored(r.projects[0]!, 'play')).toBe(false);
|
|
r = withIgnore(r, 'star dash', undefined, true).registry;
|
|
expect(isIgnored(r.projects[0]!)).toBe(true);
|
|
expect(activeProjects(r).map(p => p.name)).toEqual(['Puzzle']);
|
|
expect(activeProjects(r, 'play').map(p => p.name)).toEqual(['Puzzle']);
|
|
r = withIgnore(r, 'aaaaaaaaaaaaaaaa', 'play', false).registry;
|
|
expect(ignoredStores(r.projects[0]!)).toEqual(['apple']);
|
|
expect(activeProjects(r, 'play').map(p => p.name)).toEqual(['Star Dash', 'Puzzle']);
|
|
r = withIgnore(r, 'Star Dash', undefined, false).registry;
|
|
expect(r.projects[0]!.ignored).toBeUndefined();
|
|
expect(parseRegistry(JSON.stringify({ projects: [{ path: '/a', ignored: ['play', 'x', 'play', 'apple'] }] })).projects[0]!.ignored).toEqual(['apple', 'play']);
|
|
});
|
|
|
|
test('a store-only app has one side: its own store; the other store is an error', () => {
|
|
const r = reg();
|
|
expect(() => withIgnore(r, 'apple:6400000000', 'play', true)).toThrow('apple app only');
|
|
const o = withIgnore(r, 'apple:6400000000', 'apple', true);
|
|
expect(o.changed).toBe(true);
|
|
expect(activeStoreApps(o.registry).map(a => a.store)).toEqual(['play']);
|
|
expect(withIgnore(o.registry, 'apple:6400000000', undefined, true).changed).toBe(false);
|
|
});
|
|
|
|
test('ambiguous names and unknown keys fail loudly', () => {
|
|
const r = reg();
|
|
expect(() => withIgnore(r, 'nope', undefined, true)).toThrow('not in the app list');
|
|
expect(findEntry(r, 'Demo App')?.kind).toBe('storeApp');
|
|
const dup = parseRegistry(JSON.stringify({ projects: [{ path: '/a/x', name: 'Same' }, { path: '/b/x', name: 'Same' }] }));
|
|
expect(() => findEntry(dup, 'Same')).toThrow('matches 2 apps');
|
|
});
|
|
|
|
test('written to the file and read back; unrelated entries and storeApps stay', () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'sb-ign-'));
|
|
const file = join(dir, 'projects.json');
|
|
writeRegistry(reg(), file);
|
|
expect(setIgnore('Puzzle', 'play', true, file).changed).toBe(true);
|
|
expect(setIgnore('Puzzle', 'play', true, file).changed).toBe(false);
|
|
setIgnore('play:com.example.demo', undefined, true, file);
|
|
const back = readRegistry(file);
|
|
expect(back.projects.find(p => p.name === 'Puzzle')!.ignored).toEqual(['play']);
|
|
expect(back.storeApps!.find(a => a.store === 'play')!.ignored).toEqual(['play']);
|
|
expect(back.storeApps!.find(a => a.store === 'apple')!.ignored).toBeUndefined();
|
|
expect(JSON.parse(readFileSync(file, 'utf8')).projects[0].ignored).toBeUndefined();
|
|
});
|
|
|
|
test('store ids of ignored sides come from the config text, never by running it', () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'sb-ign-'));
|
|
const app = join(dir, 'game'); mkdirSync(app);
|
|
writeFileSync(join(app, 'storebound.config.ts'), "export default { apple: { bundleId: 'com.example.game', appId: '6400000123' }, play: { packageName: 'com.example.game.android' } };\n");
|
|
let r = parseRegistry(JSON.stringify({ projects: [{ path: app, name: 'Game' }] }));
|
|
r = withIgnore(r, 'Game', 'play', true).registry;
|
|
const ids = ignoredStoreIds(r);
|
|
expect([...ids.play]).toEqual(['com.example.game.android']);
|
|
expect(ids.apple.size).toBe(0);
|
|
});
|
|
|
|
test('MCP tools: ignore and unignore build the argv, writes default to dry run', () => {
|
|
const t = TOOLS.find(x => x.name === 'apps_ignore')!;
|
|
expect(t.write).toBe(true);
|
|
expect(toArgv(t, { app: 'Star Dash', store: 'apple' }).argv).toEqual(['apps', 'ignore', 'Star Dash', '--store', 'apple', '--dry']);
|
|
expect(TOOLS.some(x => x.name === 'apps_unignore')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('watch and ws skip ignored apps', () => {
|
|
test('watch targets: a fully ignored app is not watched, a half-ignored one skips that store', async () => {
|
|
const { listTargets } = await import('../cli/watch');
|
|
const { parseArgs } = await import('../cli/lib/args');
|
|
const base = mkdtempSync(join(tmpdir(), 'sb-ign-watch-'));
|
|
const mk = (n: string) => { const d = join(base, n); mkdirSync(d); writeFileSync(join(d, 'storebound.config.ts'), 'export default {};\n'); return d; };
|
|
const file = join(base, 'projects.json');
|
|
writeRegistry(parseRegistry(JSON.stringify({ projects: [{ path: mk('a'), name: 'A' }, { path: mk('b'), name: 'B' }, { path: mk('c'), name: 'C' }] })), file);
|
|
setIgnore('B', undefined, true, file);
|
|
setIgnore('C', 'play', true, file);
|
|
const saved = process.env.STOREBOUND_REGISTRY;
|
|
process.env.STOREBOUND_REGISTRY = file;
|
|
try {
|
|
const t = await listTargets(parseArgs([]));
|
|
expect(t.map(x => [x.name, x.skip])).toEqual([['A', []], ['C', ['play']]]);
|
|
} finally { if (saved === undefined) delete process.env.STOREBOUND_REGISTRY; else process.env.STOREBOUND_REGISTRY = saved; }
|
|
});
|
|
});
|