cross-store text: other platforms named in App Store text (2.3.10: Android, Google Play, ...) or Play text (Metadata: iOS, App Store, Game Center, ...) are refused by every text write (apple / play listing, play release / upload notes, listing set, whats-new, storebound edit, desktop editor), blockers in check / validate, scan other-platforms / play-other-platforms; listing.allowPlatformMentions for the owner's functional mentions; whats-new splits [apple] / [play] lines per store; storebound api --yes without a TTY is refused (exit 1, stderr) instead of a silent dry run; pitfalls 77, 78

This commit is contained in:
CRTkafa 2026-10-20 02:57:25 +07:00
parent 3e4015b686
commit edabbec874
32 changed files with 613 additions and 53 deletions

View file

@ -167,11 +167,11 @@ describe('write gating', () => {
const w = { ...base, kind: 'write' as const, method: 'PATCH' };
expect(decide(w)).toMatchObject({ action: 'dry', why: expect.stringContaining('--yes') });
expect(decide({ ...w, yes: true, dry: true }).action).toBe('dry');
expect(decide({ ...w, yes: true, tty: false })).toMatchObject({ action: 'dry', why: expect.stringContaining('not an interactive terminal') });
expect(decide({ ...w, yes: true, tty: false })).toMatchObject({ action: 'refuse', why: expect.stringContaining('need an interactive terminal') });
expect(decide({ ...w, yes: true })).toEqual({ action: 'send' });
const o = { ...w, kind: 'owner' as const };
expect(decide({ ...o, yes: true })).toEqual({ action: 'confirm', typed: true });
expect(decide({ ...o, yes: true, tty: false }).action).toBe('dry');
expect(decide({ ...o, yes: true, tty: false }).action).toBe('refuse');
// MCP / desktop: never a write, whatever the flags
expect(decide({ ...w, yes: true, agent: true }).action).toBe('refuse');
expect(decide({ ...o, yes: true, agent: true })).toMatchObject({ action: 'refuse', why: expect.stringContaining('never through MCP') });
@ -222,10 +222,16 @@ describe('the CLI never sends a write it should not', () => {
expect(r.code).toBe(0);
expect(JSON.parse(r.out)).toMatchObject({ dryRun: true, gate: 'write', request: { method: 'PUT', url: 'https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.example.app/edits/1/tracks/beta' } });
});
test('--yes outside a terminal still only prints', () => {
test('--yes outside a terminal (piped / redirected) is refused loudly: exit 1, stderr, nothing sent', () => {
const r = cli(['apple', 'DELETE', '/v1/appScreenshots/1', '--yes']);
expect(r.code).not.toBe(0);
expect(r.err).toContain('refused: writes through `storebound api` need an interactive terminal');
expect(r.err).toContain('Nothing was sent');
expect(r.out).not.toContain('dry run');
});
test('without --yes, a piped write is still a plain dry run (exit 0)', () => {
const r = cli(['apple', 'DELETE', '/v1/appScreenshots/1']);
expect(r.code).toBe(0);
expect(r.out).toContain('not an interactive terminal');
expect(r.out).toContain('owner only: this deletes');
});
test('under MCP and in api get, writes are refused', () => {