From 81bcd9dd5918b6ab4ffa5dba0301be0d6109804c Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Mon, 13 Jul 2026 12:39:38 -0400 Subject: [PATCH] test: cover release lookup after tag replacement (#820) Signed-off-by: Rui Chen --- __tests__/github.test.ts | 55 ++++++++++++++++++++++++++++++++++++++++ __tests__/run.test.ts | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/__tests__/github.test.ts b/__tests__/github.test.ts index 963224f..1268ff1 100644 --- a/__tests__/github.test.ts +++ b/__tests__/github.test.ts @@ -1063,6 +1063,61 @@ describe('github', () => { ); }); + it('reuses a published prerelease found by listing after direct tag lookup returns 404', async () => { + const existingRelease: Release = { + id: 77, + upload_url: 'existing-upload', + html_url: 'existing-html', + tag_name: 'v1.0.0', + name: 'nightly', + body: 'previous nightly body', + target_commitish: 'old-commit', + draft: false, + prerelease: true, + assets: [{ id: 5, name: 'nightly.zip' }], + }; + const updatedRelease: Release = { + ...existingRelease, + name: 'updated nightly', + body: 'updated nightly body', + target_commitish: 'new-commit', + }; + const createRelease = unexpected('createRelease'); + const updateRelease = vi.fn().mockResolvedValue({ data: updatedRelease }); + const releaser = createReleaser({ + getReleaseByTag: vi.fn().mockRejectedValue({ status: 404 }), + allReleases: async function* () { + yield { data: [existingRelease] }; + }, + createRelease, + updateRelease, + }); + + const result = await release( + { + ...config, + input_name: 'updated nightly', + input_body: 'updated nightly body', + input_prerelease: true, + input_draft: false, + input_target_commitish: 'new-commit', + }, + releaser, + ); + + expect(result).toEqual({ release: updatedRelease, created: false }); + expect(updateRelease).toHaveBeenCalledWith( + expect.objectContaining({ + release_id: existingRelease.id, + tag_name: existingRelease.tag_name, + target_commitish: 'new-commit', + draft: false, + prerelease: true, + }), + ); + expect(createRelease).not.toHaveBeenCalled(); + }); + it.each([ ['an omitted draft input', undefined], ['a null-expression draft input', undefined], diff --git a/__tests__/run.test.ts b/__tests__/run.test.ts index 3c40090..ccedf9b 100644 --- a/__tests__/run.test.ts +++ b/__tests__/run.test.ts @@ -286,6 +286,54 @@ describe('run', () => { ); }); + it('uses the selected moving-tag release ID for upload, finalization, and outputs', async () => { + config = { + ...config, + input_tag_name: 'nightly', + input_draft: false, + input_prerelease: true, + input_files: ['nightly.zip'], + }; + const selectedRelease: Release = { + ...initialRelease, + id: 77, + upload_url: 'https://uploads.example.test/releases/77/assets{?name,label}', + html_url: 'https://example.test/releases/77', + tag_name: 'nightly', + name: 'nightly', + prerelease: true, + }; + const publishedRelease: Release = { + ...selectedRelease, + draft: false, + }; + mocks.release.mockResolvedValue({ release: selectedRelease, created: false }); + mocks.paths.mockReturnValue(['nightly.zip']); + mocks.upload.mockResolvedValue({ id: 9 }); + mocks.finalizeRelease.mockResolvedValue(publishedRelease); + mocks.listReleaseAssets.mockResolvedValue([{ id: 9, name: 'nightly.zip' }]); + + await run(); + + expect(mocks.upload).toHaveBeenCalledWith( + config, + mocks.releaser, + 'https://uploads.example.test/releases/77/assets', + 'nightly.zip', + selectedRelease.assets, + 77, + ); + expect(mocks.finalizeRelease).toHaveBeenCalledWith( + config, + mocks.releaser, + selectedRelease, + false, + ); + expect(mocks.listReleaseAssets).toHaveBeenCalledWith(config, mocks.releaser, publishedRelease); + expect(mocks.setOutput).toHaveBeenCalledWith('id', '77'); + expect(mocks.setOutput).toHaveBeenCalledWith('url', publishedRelease.html_url); + }); + it('leaves an existing draft recoverable when an upload fails', async () => { config = { ...config,