Files
vscode/src/vs/platform/agentHost/test/node/agentHostPullRequestOperationProvider.test.ts
T
fe9115c2c3 Agent host: track pull requests per branch (#328425)
* agent host: track pull requests per branch

A pull request always relates to a branch, but the host latched onto the
first pull request it found for a session and never looked again. When the
agent switched the branch of the working directory, the session kept
reporting the previous branch's pull request forever.

Record the branch a pull request was found (or created) for and resume
looking whenever a git state refresh observes a different branch. The known
pull request keeps being reported until one is found for the branch that is
currently checked out.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* only look for a pull request on GitHub-backed working copies

The branch-change lookup ran for every working copy, including those
without a GitHub remote where `attachSessionGitHubPullRequest` can only
bail out. Besides the pointless async work, awaiting it delayed the
refresh event by a few microtasks and broke `subscribe lazily attaches
git state when an existing session has no _meta.git`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-31 21:12:38 +00:00

75 lines
4.1 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import assert from 'assert';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
import { Event } from '../../../../base/common/event.js';
import { InstantiationService } from '../../../instantiation/common/instantiationService.js';
import { NullLogService } from '../../../log/common/log.js';
import { AgentHostStateManager } from '../../node/agentHostStateManager.js';
import { AgentHostPullRequestOperationContribution } from '../../node/agentHostPullRequestOperationProvider.js';
import type { ISessionGitHubState, ISessionGitState } from '../../common/state/sessionState.js';
import type { IAgentHostGitStateService } from '../../common/agentHostGitStateService.js';
import { ChangesetKind } from '../../common/changesetUri.js';
const nullGitStateService = new class implements IAgentHostGitStateService {
declare readonly _serviceBrand: undefined;
readonly onDidRefreshSessionGitState = Event.None;
async refreshSessionGitState(): Promise<void> { }
async getSessionGitHubState(): Promise<ISessionGitHubState | undefined> { return undefined; }
async setSessionGitHubState(): Promise<void> { }
async attachSessionGitHubPullRequest(): Promise<void> { }
async attachSessionGitHubIssues(): Promise<void> { }
};
const githubBranchWithUncommittedChanges: ISessionGitState = {
hasGitHubRemote: true,
branchName: 'feature/test',
uncommittedChanges: 1,
outgoingChanges: 0,
};
suite('AgentHostPullRequestOperationContribution', () => {
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
function createContribution(): AgentHostPullRequestOperationContribution {
return disposables.add(new AgentHostPullRequestOperationContribution(
disposables.add(new AgentHostStateManager(new NullLogService())),
disposables.add(new InstantiationService()),
nullGitStateService,
));
}
test('advertises PR operations for GitHub branches with uncommitted changes', () => {
const provider = createContribution();
const operations = provider.getOperations({ sessionKey: 'agent:/session', gitState: githubBranchWithUncommittedChanges, changesetKind: ChangesetKind.Session, changesetUri: '' });
assert.deepStrictEqual(operations?.map(op => op.id), ['create-pr', 'create-pr-auto-merge', 'create-pr-auto-squash', 'create-pr-auto-rebase', 'create-draft-pr']);
});
test('does not advertise PR operations without GitHub branch changes', () => {
const provider = createContribution();
const actual = [
provider.getOperations({ sessionKey: 'agent:/session', gitState: { ...githubBranchWithUncommittedChanges, hasGitHubRemote: false }, changesetKind: ChangesetKind.Session, changesetUri: '' }),
provider.getOperations({ sessionKey: 'agent:/session', gitState: { ...githubBranchWithUncommittedChanges, uncommittedChanges: 0, outgoingChanges: 0 }, changesetKind: ChangesetKind.Session, changesetUri: '' }),
];
assert.deepStrictEqual(actual, [undefined, undefined]);
});
test('advertises PR operations again for a branch whose pull request is unknown', () => {
const provider = createContribution();
const actual = [
provider.getOperations({ sessionKey: 'agent:/session', gitState: githubBranchWithUncommittedChanges, gitHubState: { pullRequestUrl: 'https://github.com/microsoft/vscode/pull/1', pullRequestBranchName: 'feature/test' }, changesetKind: ChangesetKind.Session, changesetUri: '' }),
provider.getOperations({ sessionKey: 'agent:/session', gitState: githubBranchWithUncommittedChanges, gitHubState: { pullRequestUrl: 'https://github.com/microsoft/vscode/pull/1', pullRequestBranchName: 'feature/other' }, changesetKind: ChangesetKind.Session, changesetUri: '' }),
];
assert.deepStrictEqual(actual.map(operations => operations?.map(op => op.id)), [undefined, ['create-pr', 'create-pr-auto-merge', 'create-pr-auto-squash', 'create-pr-auto-rebase', 'create-draft-pr']]);
});
});