// One app record, an iOS and a macOS App Store version (pitfall 79): CFBundleVersion must be unique across ALL the app's // builds, so `apple next-build`, `apple build` and `apple build --platform macos` (also --dry) share one computation. // Seen live: the macOS dry run proposed a build number an uploaded macOS build already had (it asked only for macOS // builds), while next-build said the right one. Against the fake App Store Connect. import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from 'bun:test'; import type { ShipConfig } from '../cli/lib/config'; import { appNextBuild, run as nextBuild } from '../cli/apple/next-build'; import { identityLine, run as buildMas } from '../cli/apple/build-mas'; import { ascEnv, BASE, capture, project, restore } from './helpers/store-mock'; import { installStores, type Stores, uninstallStores } from './helpers/fake-stores'; const APP = '6400000101'; const CONFIG: ShipConfig = { ...BASE }; const TAURI = JSON.stringify({ productName: 'Example Notes', identifier: 'com.example.notes', version: '1.2.0' }); let s: Stores; beforeAll(ascEnv); afterAll(restore); beforeEach(() => { s = installStores(); }); afterEach(uninstallStores); const build = (id: string, version: string, state = 'VALID') => s.asc.put({ type: 'builds', id, attributes: { version, processingState: state }, rels: { app: APP } }); /** iOS builds 202610261700 / ...1730, a macOS build ...1731 and an expired one ...1734 in another state. */ function seed(): void { build('b-ios-1', '202610261700'); build('b-ios-2', '202610261730'); build('b-mac-1', '202610261731'); build('b-mac-2', '202610261734', 'PROCESSING'); } describe('next build number over every platform', () => { test('apple next-build and the macOS dry run agree and sit above the highest build', async () => { seed(); const p = project(CONFIG, { 'src-tauri/tauri.conf.json': TAURI }); const nb = await capture(() => nextBuild(p.ctx(['--json']))); expect(JSON.parse(nb.out)).toEqual({ next: '202610261735', builds: 4 }); const dry = await capture(() => buildMas(p.ctx(['--tauri', 'src-tauri', '--unsigned', '--dry', '--json']), 'linux')); expect(JSON.parse(dry.out.slice(dry.out.indexOf('{')))).toMatchObject({ platform: 'MAC_OS', build: '202610261735' }); }); test('no request narrows the build list to one platform', async () => { seed(); const p = project(CONFIG, { 'src-tauri/tauri.conf.json': TAURI }); await capture(() => buildMas(p.ctx(['--tauri', 'src-tauri', '--unsigned', '--dry', '--json']), 'linux')); const reads = s.calls.filter(c => /\/v1\/builds/.test(c.path)); expect(reads.length).toBeGreaterThan(0); for (const r of reads) expect(decodeURIComponent(r.path)).not.toMatch(/preReleaseVersion|platform|processingState/); }); test('every page counts', async () => { const pages: Record = { '/p1': { data: [{ attributes: { version: '5' } }], links: { next: '/p2' } }, '/p2': { data: [{ attributes: { version: '41' } }], links: { next: '/p3' } }, '/p3': { data: [{ attributes: { version: '9' } }], links: {} }, }; const seen: string[] = []; const client = { app: async () => APP, all: async (path: string) => { seen.push(path); const out: unknown[] = []; for (let u: string | undefined = '/p1'; u; u = (pages[u] as { links: { next?: string } }).links.next) out.push(...(pages[u] as { data: unknown[] }).data); return out; } }; expect(await appNextBuild(client as never)).toEqual({ next: '42', builds: 3 }); expect(seen[0]).toContain(`filter[app]=${APP}`); }); test('an app without builds starts at 1', async () => { expect((await appNextBuild({ app: async () => APP, all: async () => [] } as never)).next).toBe('1'); }); }); describe('identity line off a Mac', () => { test('a non-macOS machine is called a non-build machine, not "none found"', () => { const line = identityLine({ platform: 'win32', appIdentity: null, installerIdentity: null, profile: null }); expect(line).toContain('not a build machine'); expect(line).toContain('Mac'); expect(line).not.toContain('none found'); }); test('on a Mac a missing identity is still reported as missing', () => { expect(identityLine({ platform: 'darwin', appIdentity: null, installerIdentity: 'Mac Installer', profile: null })).toContain('app identity: (none found); installer identity: Mac Installer'); }); });