Repository navigation
feat(review): post --comment reviews to Aone Code via the a1 CLI #9491
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
1326270
feat(review): post --comment reviews to Aone Code via the a1 CLI
wenshao 6aa4880
fix(review): count an accepted-but-unreadable Aone answer as posted
wenshao f837d92
docs(review): record the Aone write-path trade-offs in the Phase 3 note
wenshao cb5ebe5
fix(review): close five write-safety holes in the Aone submit path
wenshao f982815
fix(review): harden the Aone submit path per the verify-lane review
wenshao 366cdfb
test(review): pin hostsEquivalent's alias equivalence across spelling…
wenshao 188dd2d
fix(review): close the third-layer holes in the Aone submit path
wenshao ddc3cb2
fix(review): close the residual holes the sandboxed verify report found
wenshao a66d7e9
Merge remote-tracking branch 'origin/main' into review-aone/comment-p…
wenshao 79f0494
fix(review): close the round-5 platform-binding holes in the Aone wri…
wenshao File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
packages/cli/src/commands/review/lib/platform/aone-client.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| /** | ||
| * @license | ||
| * Copyright 2026 Qwen Team | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
|
|
||
| // Mock execFileSync before aone-client.ts is loaded — same shape as | ||
| // gh.test.ts: vi.mock is hoisted above all imports. | ||
| const mockExecFileSync = vi.hoisted(() => vi.fn()); | ||
| vi.mock('node:child_process', () => ({ | ||
| default: { execFileSync: mockExecFileSync }, | ||
| execFileSync: mockExecFileSync, | ||
| })); | ||
|
|
||
| import { a1, a1JsonOnce, a1Once } from './aone-client.js'; | ||
|
|
||
| function transientError(): Error { | ||
| // The message shape execFileSync produces, carrying a transient marker | ||
| // the retry policy recognises. | ||
| return new Error( | ||
| 'Command failed: a1 repo mr comment create\nHTTP 502 Bad Gateway\n', | ||
| ); | ||
| } | ||
|
|
||
| describe('aone-client write discipline', () => { | ||
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
| }); | ||
|
|
||
| it('a1Once NEVER retries — a transient failure after an accepted write must not double-post', () => { | ||
| // The read path retries this exact error class; a write must surface | ||
| // the first failure instead, or a retry behind a swallowed 502 posts | ||
| // the same comment twice. | ||
| mockExecFileSync.mockImplementation(() => { | ||
| throw transientError(); | ||
| }); | ||
| expect(() => | ||
| a1Once('repo', 'mr', 'comment', 'create', '--mr', '7'), | ||
| ).toThrow(); | ||
| expect(mockExecFileSync).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('a1JsonOnce parses the write result and appends --format json', () => { | ||
| mockExecFileSync.mockReturnValue('{"id": 42}\n'); | ||
| const out = a1JsonOnce<{ id: number }>( | ||
| 'repo', | ||
| 'mr', | ||
| 'comment', | ||
| 'create', | ||
| '--mr', | ||
| '7', | ||
| ); | ||
| expect(out).toEqual({ id: 42 }); | ||
| const args = mockExecFileSync.mock.calls[0][1] as string[]; | ||
| expect(args.slice(-2)).toEqual(['--format', 'json']); | ||
|
wenshao marked this conversation as resolved.
Resolved
|
||
| }); | ||
|
|
||
| it('a1JsonOnce returns undefined (not a throw) when an ACCEPTED write answers unparseably', () => { | ||
| // The exec SUCCEEDED, so the write is accepted. A result that fails to | ||
| // parse is a platform anomaly, not a failed post — throwing would let a | ||
| // caller count the accepted comment as unposted and re-run it into a | ||
| // duplicate. undefined = "landed, result unreadable". | ||
| mockExecFileSync.mockReturnValue('this is not json\n'); | ||
| const out = a1JsonOnce<{ id: number }>( | ||
| 'repo', | ||
| 'mr', | ||
| 'comment', | ||
| 'create', | ||
| '--mr', | ||
| '7', | ||
| ); | ||
| expect(out).toBeUndefined(); | ||
| expect(mockExecFileSync).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('a1JsonOnce still PROPAGATES an exec failure (the write genuinely failed)', () => { | ||
|
wenshao marked this conversation as resolved.
Resolved
|
||
| mockExecFileSync.mockImplementation(() => { | ||
| throw new Error('Command failed: a1 repo mr comment create\nboom\n'); | ||
| }); | ||
| expect(() => | ||
| a1JsonOnce('repo', 'mr', 'comment', 'create', '--mr', '7'), | ||
| ).toThrow(); | ||
| expect(mockExecFileSync).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('a1 (the read path) surfaces a NON-transient error at once', () => { | ||
| // Only the transient class retries; anything else must not pay the | ||
| // delay (and this exercises the shared exec path without its sleep). | ||
| mockExecFileSync.mockImplementation(() => { | ||
| throw new Error('Command failed: a1 repo mr view 7\nnot found\n'); | ||
| }); | ||
| expect(() => a1('repo', 'mr', 'view', '7')).toThrow(); | ||
| expect(mockExecFileSync).toHaveBeenCalledTimes(1); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.