Skip to content
Draft
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
9 changes: 9 additions & 0 deletions src/@types/vscode.proposed.chatSessionsProvider.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,8 +594,15 @@ declare module 'vscode' {
/**
* The initial option selections for the session, provided with the first request.
* Contains the options the user selected (or defaults) before the session was created.
*
* @deprecated Use `inputState` instead
*/
readonly initialSessionOptions?: ReadonlyArray<{ optionId: string; value: string | ChatSessionProviderOptionItem }>;

/**
* The current input state of the chat session.
*/
readonly inputState: ChatSessionInputState;
}

export interface ChatSessionCapabilities {
Expand Down Expand Up @@ -692,6 +699,8 @@ declare module 'vscode' {
*
* These commands will be displayed at the bottom of the group.
*
* For extensions using the legacy `commands` API, these commands are passed the sessionResource as the first argument.
*
* For extensions that use the new `provideChatSessionInputState` API, these commands are passed a context object
* `{ inputState: ChatSessionInputState; sessionResource: Uri | undefined }` that they can use to determine which session and options they are being invoked for.
*/
Expand Down
12 changes: 8 additions & 4 deletions src/github/githubRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1916,9 +1916,13 @@ export class GitHubRepository extends Disposable {
const statusByContext = new Map<string, PullRequestCheckStatus>();

for (const status of statuses) {
const existing = statusByContext.get(status.context);
// Include event and workflowName in the key so that checks from different
// workflow events (e.g. "push" vs "pull_request") or different workflows
// are not incorrectly merged during deduplication.
const key = `${status.context}\0${status.event ?? ''}\0${status.workflowName ?? ''}`;
const existing = statusByContext.get(key);
if (!existing) {
statusByContext.set(status.context, status);
statusByContext.set(key, status);
continue;
}

Expand All @@ -1928,15 +1932,15 @@ export class GitHubRepository extends Disposable {

if (currentIsPending && !existingIsPending) {
// Current is pending, existing is completed - prefer current
statusByContext.set(status.context, status);
statusByContext.set(key, status);
} else if (!currentIsPending && existingIsPending) {
// Current is completed, existing is pending - keep existing
continue;
} else {
// Both are same type (both pending or both completed)
// Prefer the one with a higher ID (more recent), as GitHub IDs are monotonically increasing
if (status.id > existing.id) {
statusByContext.set(status.context, status);
statusByContext.set(key, status);
}
}
}
Expand Down
79 changes: 79 additions & 0 deletions src/test/github/githubRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Uri } from 'vscode';
import { MockExtensionContext } from '../mocks/mockExtensionContext';
import { GitHubManager } from '../../authentication/githubServer';
import { GitHubServerType } from '../../common/authentication';
import { CheckState, PullRequestCheckStatus } from '../../github/interface';

describe('GitHubRepository', function () {
let sinon: SinonSandbox;
Expand Down Expand Up @@ -52,4 +53,82 @@ describe('GitHubRepository', function () {
// assert(! dotcomRepository.isGitHubDotCom);
});
});

describe('deduplicateStatusChecks', function () {
function createStatus(overrides: Partial<PullRequestCheckStatus> & { id: string; context: string }): PullRequestCheckStatus {
return {
databaseId: undefined,
url: undefined,
avatarUrl: undefined,
state: CheckState.Success,
description: null,
targetUrl: null,
workflowName: undefined,
event: undefined,
isRequired: false,
isCheckRun: true,
...overrides,
};
}

function callDeduplicateStatusChecks(repo: GitHubRepository, statuses: PullRequestCheckStatus[]): PullRequestCheckStatus[] {
return (repo as any).deduplicateStatusChecks(statuses);
}

let repo: GitHubRepository;

beforeEach(function () {
const url = 'https://github.com/some/repo';
const remote = new GitHubRemote('origin', url, new Protocol(url), GitHubServerType.GitHubDotCom);
const rootUri = Uri.file('C:\\users\\test\\repo');
repo = new GitHubRepository(1, remote, rootUri, credentialStore, telemetry);
});

it('keeps checks with different events as separate entries', function () {
const statuses = [
createStatus({ id: '1', context: 'Build Linux / x86-64', event: 'push', workflowName: 'Build Linux' }),
createStatus({ id: '2', context: 'Build Linux / x86-64', event: 'pull_request', workflowName: 'Build Linux' }),
];
const result = callDeduplicateStatusChecks(repo, statuses);
assert.strictEqual(result.length, 2);
});

it('deduplicates checks with the same name, event, and workflow', function () {
const statuses = [
createStatus({ id: '1', context: 'Build Linux / x86-64', event: 'push', workflowName: 'Build Linux', state: CheckState.Success }),
createStatus({ id: '2', context: 'Build Linux / x86-64', event: 'push', workflowName: 'Build Linux', state: CheckState.Success }),
];
const result = callDeduplicateStatusChecks(repo, statuses);
assert.strictEqual(result.length, 1);
assert.strictEqual(result[0].id, '2'); // higher ID preferred
});

it('keeps checks from different workflows as separate entries', function () {
const statuses = [
createStatus({ id: '1', context: 'build', event: 'push', workflowName: 'CI' }),
createStatus({ id: '2', context: 'build', event: 'push', workflowName: 'Nightly' }),
];
const result = callDeduplicateStatusChecks(repo, statuses);
assert.strictEqual(result.length, 2);
});

it('prefers pending checks over completed ones during deduplication', function () {
const statuses = [
createStatus({ id: '1', context: 'test', event: 'push', workflowName: 'CI', state: CheckState.Success }),
createStatus({ id: '2', context: 'test', event: 'push', workflowName: 'CI', state: CheckState.Pending }),
];
const result = callDeduplicateStatusChecks(repo, statuses);
assert.strictEqual(result.length, 1);
assert.strictEqual(result[0].state, CheckState.Pending);
});

it('handles status contexts without event or workflowName', function () {
const statuses = [
createStatus({ id: '1', context: 'ci/jenkins', isCheckRun: false }),
createStatus({ id: '2', context: 'ci/travis', isCheckRun: false }),
];
const result = callDeduplicateStatusChecks(repo, statuses);
assert.strictEqual(result.length, 2);
});
});
});
Loading