apple testflight distribute: a build's groups read per group (GET /v1/betaGroups/{id}/relationships/builds) instead of GET /v1/betaGroups?filter[builds]=, which App Store Connect refuses with 400 although the spec declares it; internal all-builds groups reported as already available and never sent the build; a re-run adds nothing; configured groups missing on App Store Connect point at groups sync, unknown ones list what exists; APPLE_LIVE_REJECTED (refused by storebound api too), tests/asc-query-guard.test.ts checks every App Store Connect GET with a query in cli/ against the spec's declared parameters, and the fake App Store Connect answers 400 to undeclared or live-refused parameters (pitfall 42)
This commit is contained in:
parent
f741c15829
commit
bba46b5c7f
13 changed files with 354 additions and 14 deletions
|
|
@ -98,6 +98,53 @@ describe('apple testflight groups: bare lists, sync creates', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('apple testflight distribute (real use 2026-10-13)', () => {
|
||||
const TF: ShipConfig = { ...CONFIG, apple: { ...CONFIG.apple!, testflight: { groups: [{ name: 'Team', internal: true, allBuilds: true }, { name: 'Friends' }, { name: 'Devs', internal: true }] } as never } };
|
||||
const groupsAndBuild = () => {
|
||||
s.asc.put({ type: 'betaGroups', id: 'grp-team', attributes: { name: 'Team', isInternalGroup: true, hasAccessToAllBuilds: true }, rels: { app: APP } });
|
||||
s.asc.put({ type: 'betaGroups', id: 'grp-friends', attributes: { name: 'Friends', isInternalGroup: false }, rels: { app: APP } });
|
||||
s.asc.put({ type: 'betaGroups', id: 'grp-devs', attributes: { name: 'Devs', isInternalGroup: true, hasAccessToAllBuilds: false }, rels: { app: APP } });
|
||||
s.asc.put({ type: 'builds', id: 'b-1', attributes: { version: '1', processingState: 'VALID', usesNonExemptEncryption: false, expired: false }, rels: { app: APP } });
|
||||
};
|
||||
|
||||
test('live shape: GET /v1/betaGroups?filter[builds]= is a 400 (the old lookup fails here as it did live)', async () => {
|
||||
const r = s.asc.handle('GET', `/v1/betaGroups?filter[app]=${APP}&filter[builds]=b-1&limit=200`, undefined);
|
||||
expect(r.status).toBe(400);
|
||||
expect(JSON.stringify(await r.json())).toContain('PARAMETER_ERROR.ILLEGAL');
|
||||
});
|
||||
|
||||
test('an internal all-builds group: reported as already available, nothing added', async () => {
|
||||
groupsAndBuild();
|
||||
const p = project(TF);
|
||||
const r = await twice(() => appleTestflight(p.ctx(['distribute', '--build', '1', '--group', 'Team', '--yes'])));
|
||||
expect(r.first).toEqual([]);
|
||||
expect(r.second).toEqual([]);
|
||||
const out = await capture(() => appleTestflight(p.ctx(['distribute', '--build', '1', '--group', 'Team', '--yes'])));
|
||||
expect(out.out).toContain('already available to Team (internal, all builds)');
|
||||
expect(s.calls.some(c => /filter\[builds\]/.test(decodeURIComponent(c.path)))).toBe(false);
|
||||
});
|
||||
|
||||
test('an internal group without all builds and an external group: added once, the re-run adds nothing', async () => {
|
||||
groupsAndBuild();
|
||||
const p = project(TF);
|
||||
const r = await twice(() => appleTestflight(p.ctx(['distribute', '--build', '1', '--group', 'Team,Devs,Friends', '--yes'])));
|
||||
expect(r.first).toEqual(['POST /v1/betaGroups/grp-devs/relationships/builds', 'POST /v1/betaGroups/grp-friends/relationships/builds']);
|
||||
expect(r.second).toEqual([]);
|
||||
const out = await capture(() => appleTestflight(p.ctx(['distribute', '--build', '1', '--group', 'Devs', '--yes'])));
|
||||
expect(out.out).toContain('already in Devs (internal)');
|
||||
});
|
||||
|
||||
test('a group in the config but not on App Store Connect points at groups sync; an unknown one lists what exists', async () => {
|
||||
const p = project(TF);
|
||||
s.asc.put({ type: 'builds', id: 'b-1', attributes: { version: '1', processingState: 'VALID' }, rels: { app: APP } });
|
||||
await expect(capture(() => appleTestflight(p.ctx(['distribute', '--build', '1', '--group', 'Team', '--yes']))))
|
||||
.rejects.toThrow('group "Team" is in the config but not on App Store Connect yet: create it with `storebound apple testflight groups sync`');
|
||||
s.asc.put({ type: 'betaGroups', id: 'grp-friends', attributes: { name: 'Friends', isInternalGroup: false }, rels: { app: APP } });
|
||||
await expect(capture(() => appleTestflight(p.ctx(['distribute', '--build', '1', '--group', 'Nope', '--yes']))))
|
||||
.rejects.toThrow('unknown group "Nope": App Store Connect has "Friends"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('App Store Connect: a second run writes nothing', () => {
|
||||
test('apple iap: product, localizations, price schedule, availability once', async () => {
|
||||
const p = project(CONFIG);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue