play: old testing builds below production listed with their replace command (status, validate); 'Not live yet' instead of 'In review'; commit redone without changesNotSentForReview on the auto-send 400; pitfalls #129

This commit is contained in:
CRTkafa 2026-11-08 02:18:24 +07:00
parent 83d351cf42
commit 9c001d1ca9
13 changed files with 130 additions and 15 deletions

View file

@ -0,0 +1,31 @@
import { describe, expect, test } from 'bun:test';
import { oldBuildLines, oldTrackBuilds } from '../cli/play/old-builds';
import { isAutoSendOnly, isReviewRefused } from '../cli/play/review-pending';
const tracks = [
{ track: 'production', releases: [{ name: '2.0.0 (10)', versionCodes: ['10'], status: 'completed' }] },
{ track: 'alpha', releases: [{ name: '1.0 (2)', versionCodes: ['2'], status: 'completed' }] },
{ track: 'beta', releases: [{ versionCodes: ['10'], status: 'completed' }] },
{ track: 'internal', releases: [{ versionCodes: ['3'], status: 'draft' }] },
{ track: 'qa-closed', releases: [{ versionCodes: ['4'], status: 'completed' }] },
];
describe('old builds on testing tracks (pitfall 129)', () => {
test('active builds below production are listed with the command that replaces them; drafts and current ones are not', () => {
const old = oldTrackBuilds(tracks);
expect(old.map(o => `${o.track}:${o.versionCodes.join(',')}`)).toEqual(['alpha:2', 'qa-closed:4']);
const text = oldBuildLines(old).join('\n');
expect(text).toContain('storebound play release --version-code 10 --alpha completed');
expect(text).toContain('--track qa-closed completed');
});
test('no production release: nothing to compare', () => {
expect(oldTrackBuilds(tracks.filter(t => t.track !== 'production'))).toEqual([]);
});
test('the "are sent for review automatically" 400 is told apart from the "cannot be sent" one', () => {
const auto = new Error('POST commit -> 400 {"error":{"message":"Changes are sent for review automatically.","errors":[{"reason":"badRequest"}]}}');
const refused = new Error('POST commit -> 400 {"error":{"message":"Changes cannot be sent for review automatically.","errors":[{"reason":"badRequest"}]}}');
expect(isAutoSendOnly(auto)).toBe(true);
expect(isAutoSendOnly(refused)).toBe(false);
expect(isReviewRefused(refused)).toBe(true);
});
});

View file

@ -7,7 +7,7 @@ import { trainsOf } from '../cli/release';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { clearPlayPublicCache, pageVersion, pageVersionInfo, playPublicPage } from '../cli/lib/play-public';
import { appleReleases, playReleaseLabel, playReleases, releaseTrains, sameVersion, versionKey } from '../cli/lib/portfolio';
import { appleReleases, PLAY_NOT_LIVE, playReleaseLabel, playReleases, releaseTrains, sameVersion, versionKey } from '../cli/lib/portfolio';
import { laneOf, laneText, releasePlan, storeNow, versionHistory } from '../cli/lib/train';
const realFetch = globalThis.fetch;
@ -45,7 +45,7 @@ describe('version matching across the stores', () => {
expect(p.ios?.release?.version).toBe('1.0');
expect(p.android?.release?.version).toBe('1.0.0');
expect(p.done).toBe(false);
expect(p.android!.steps.map(x => x.text).join(' ')).toContain('1.0: In review');
expect(p.android!.steps.map(x => x.text).join(' ')).toContain('1.0: Not live yet');
});
});
@ -53,7 +53,7 @@ describe('Google Play "Live" needs the public store page', () => {
test('completed production + page 404 = in review, never live', () => {
const [prod] = playReleases(PLAY_STATUS_FIRST_404);
expect(prod).toMatchObject({ phase: 'review', state: 'completed', publicPage: 'notPublic', fraction: 1 });
expect(playReleaseLabel(prod!)).toBe('In review');
expect(playReleaseLabel(prod!)).toBe(PLAY_NOT_LIVE);
const lane = laneOf('android', prod!);
expect(lane.at).toBe(2); // the review stage, not live / rollout
expect(lane.share).toBeNull();
@ -77,14 +77,14 @@ describe('Google Play "Live" needs the public store page', () => {
expect(playReleaseLabel(r)).toBe('Live');
});
test('rule: page 404, managed publishing off: In review; on: In review / ready to publish', () => {
expect(playReleaseLabel(prodRel({ state: 'notPublic', versionName: null }))).toBe('In review');
expect(playReleaseLabel(prodRel({ state: 'notPublic', versionName: null }))).toBe(PLAY_NOT_LIVE);
expect(playReleaseLabel(prodRel({ state: 'notPublic', versionName: null }, '1.1.0', 'completed', { managedPublishing: true }))).toBe('In review / ready to publish (managed publishing)');
});
test('rule: page live with an older version: In review (live: <older>)', () => {
const r = prodRel({ state: 'public', versionName: '1.0.0' });
expect(r).toMatchObject({ phase: 'review', liveVersion: '1.0.0' });
expect(playReleaseLabel(r)).toBe('In review (live: 1.0.0)');
expect(laneText(laneOf('android', r))).toBe('in review (live: 1.0.0)');
expect(playReleaseLabel(r)).toBe(`${PLAY_NOT_LIVE} (live: 1.0.0)`);
expect(laneText(laneOf('android', r))).toBe(`not live yet: in review or blocked before review (the API cannot tell; see Publishing overview) (live: 1.0.0)`);
expect(storeNow([r], 'android').now).toBeNull();
});
test('rule: rollout, halted, draft', () => {