Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/main/github/client-issue-source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,63 @@ describe('GitHub issue source split', () => {
)
})

it("preference='auto' + upstream exists → PRs query upstream too", async () => {
// Why: fork-contribution PRs live on the upstream repo — the fork's own
// PR list is almost always empty. 'auto' must resolve PRs upstream-first
// like issues, or the PRs tab renders "No matching GitHub work" on forks.
resolveIssueSourceMock.mockResolvedValueOnce({
source: { owner: 'stablyai', repo: 'orca' },
fellBack: false
})
getOwnerRepoMock.mockResolvedValueOnce({ owner: 'fork', repo: 'orca' })
getOwnerRepoForRemoteMock.mockResolvedValueOnce({ owner: 'stablyai', repo: 'orca' })
ghExecFileAsyncMock.mockResolvedValueOnce({ stdout: '[]' }).mockResolvedValueOnce({
stdout: '[]'
})

const result = await listWorkItems('/repo-root', 10, undefined, undefined, 'auto')

expect(ghExecFileAsyncMock).toHaveBeenNthCalledWith(
2,
[
'api',
'--cache',
'120s',
'repos/stablyai/orca/pulls?per_page=10&state=open&sort=updated&direction=desc'
],
{ cwd: '/repo-root' }
)
expect(result.sources).toEqual({
issues: { owner: 'stablyai', repo: 'orca' },
prs: { owner: 'stablyai', repo: 'orca' },
originCandidate: { owner: 'fork', repo: 'orca' },
upstreamCandidate: { owner: 'stablyai', repo: 'orca' }
})
})

it('collapses the default count to one query when auto resolves both sides to upstream', async () => {
getIssueOwnerRepoMock.mockResolvedValueOnce({ owner: 'stablyai', repo: 'orca' })
getOwnerRepoMock.mockResolvedValueOnce({ owner: 'fork', repo: 'orca' })
getOwnerRepoForRemoteMock.mockResolvedValueOnce({ owner: 'stablyai', repo: 'orca' })
ghExecFileAsyncMock.mockResolvedValueOnce({ stdout: '11\n' })

const count = await countWorkItems('/repo-root')

expect(count).toBe(11)
expect(ghExecFileAsyncMock).toHaveBeenCalledTimes(1)
expect(ghExecFileAsyncMock).toHaveBeenCalledWith(
[
'api',
'--cache',
'120s',
`search/issues?q=${encodeURIComponent('repo:stablyai/orca is:open')}&per_page=1`,
'--jq',
'.total_count'
],
{ cwd: '/repo-root' }
)
})

it("preference='upstream' + upstream exists → queries upstream", async () => {
resolveIssueSourceMock.mockResolvedValueOnce({
source: { owner: 'stablyai', repo: 'orca' },
Expand Down
6 changes: 4 additions & 2 deletions src/main/github/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1069,8 +1069,10 @@ async function resolvePrWorkItemSource(
getOwnerRepo(repoPath, connectionId, localGitOptions),
getOwnerRepoForRemote(repoPath, 'upstream', connectionId, localGitOptions)
])
const source =
preference === 'upstream' ? (upstreamCandidate ?? originCandidate) : originCandidate
// Why: fork-contribution PRs live on the upstream repo (the fork's own PR
// list is almost always empty), so 'auto' resolves upstream-first exactly
// like the issue side. Only an explicit 'origin' pick pins PRs to the fork.
const source = preference === 'origin' ? originCandidate : (upstreamCandidate ?? originCandidate)
return { source, originCandidate, upstreamCandidate }
}

Expand Down
Loading