Skip to content

Commit 6b6c06e

Browse files
authored
Weird Copilot on My Behalf notification state (#7924)
Fixes #7922
1 parent 445afa7 commit 6b6c06e

2 files changed

Lines changed: 153 additions & 6 deletions

File tree

src/github/copilotPrWatcher.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,11 @@ export class CopilotStateModel extends Disposable {
4848
return `${owner}/${repo}#${prNumber}`;
4949
}
5050

51-
delete(owner: string, repo: string, prNumber: number): void {
52-
const key = this.makeKey(owner, repo, prNumber);
53-
this.deleteKey(key);
54-
}
55-
5651
deleteKey(key: string): void {
5752
if (this._states.has(key)) {
53+
const item = this._states.get(key)!;
5854
this._states.delete(key);
5955
if (this._showNotification.has(key)) {
60-
const item = this._states.get(key)!;
6156
this._showNotification.delete(key);
6257
this._onDidChangeNotifications.fire([item.item]);
6358
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { default as assert } from 'assert';
7+
import { CopilotStateModel } from '../../github/copilotPrWatcher';
8+
import { CopilotPRStatus } from '../../common/copilot';
9+
import { PullRequestModel } from '../../github/pullRequestModel';
10+
11+
describe('Copilot PR watcher', () => {
12+
13+
describe('CopilotStateModel', () => {
14+
15+
const createPullRequest = (owner: string, repo: string, number: number): PullRequestModel => {
16+
return {
17+
number,
18+
remote: { owner, repositoryName: repo },
19+
author: { login: 'copilot' }
20+
} as unknown as PullRequestModel;
21+
};
22+
23+
it('creates consistent keys and reports refresh events', () => {
24+
const model = new CopilotStateModel();
25+
let refreshEvents = 0;
26+
model.onRefresh(() => refreshEvents++);
27+
28+
assert.strictEqual(model.makeKey('octo', 'repo'), 'octo/repo');
29+
assert.strictEqual(model.makeKey('octo', 'repo', 7), 'octo/repo#7');
30+
31+
model.clear();
32+
assert.strictEqual(refreshEvents, 1);
33+
});
34+
35+
it('stores statuses and emits notifications after initialization', () => {
36+
const model = new CopilotStateModel();
37+
let changeEvents = 0;
38+
const notifications: PullRequestModel[][] = [];
39+
model.onDidChangeStates(() => changeEvents++);
40+
model.onDidChangeNotifications(items => notifications.push(items));
41+
42+
const pr = createPullRequest('octo', 'repo', 1);
43+
model.set([{ pullRequestModel: pr, status: CopilotPRStatus.Started }]);
44+
45+
assert.strictEqual(model.get('octo', 'repo', 1), CopilotPRStatus.Started);
46+
assert.strictEqual(changeEvents, 1);
47+
assert.strictEqual(notifications.length, 0);
48+
assert.strictEqual(model.notifications.size, 0);
49+
50+
model.set([{ pullRequestModel: pr, status: CopilotPRStatus.Started }]);
51+
assert.strictEqual(changeEvents, 1);
52+
53+
model.setInitialized();
54+
const updated = createPullRequest('octo', 'repo', 1);
55+
model.set([{ pullRequestModel: updated, status: CopilotPRStatus.Completed }]);
56+
57+
assert.strictEqual(model.get('octo', 'repo', 1), CopilotPRStatus.Completed);
58+
assert.strictEqual(changeEvents, 2);
59+
assert.strictEqual(notifications.length, 1);
60+
assert.deepStrictEqual(notifications[0], [updated]);
61+
assert.ok(model.notifications.has('octo/repo#1'));
62+
});
63+
64+
it('deletes keys and clears related notifications', () => {
65+
const model = new CopilotStateModel();
66+
let changeEvents = 0;
67+
const notifications: PullRequestModel[][] = [];
68+
model.onDidChangeStates(() => changeEvents++);
69+
model.onDidChangeNotifications(items => notifications.push(items));
70+
71+
model.setInitialized();
72+
const pr = createPullRequest('octo', 'repo', 42);
73+
model.set([{ pullRequestModel: pr, status: CopilotPRStatus.Started }]);
74+
75+
assert.strictEqual(model.notifications.size, 1);
76+
assert.strictEqual(changeEvents, 1);
77+
78+
model.deleteKey('octo/repo#42');
79+
assert.strictEqual(model.get('octo', 'repo', 42), CopilotPRStatus.None);
80+
assert.strictEqual(changeEvents, 2);
81+
assert.strictEqual(model.notifications.size, 0);
82+
assert.strictEqual(notifications.length, 2);
83+
assert.deepStrictEqual(notifications[1], [pr]);
84+
assert.deepStrictEqual(model.keys(), []);
85+
});
86+
87+
it('clears individual notifications and reports changes', () => {
88+
const model = new CopilotStateModel();
89+
const notifications: PullRequestModel[][] = [];
90+
model.onDidChangeNotifications(items => notifications.push(items));
91+
92+
model.setInitialized();
93+
const pr = createPullRequest('octo', 'repo', 5);
94+
model.set([{ pullRequestModel: pr, status: CopilotPRStatus.Started }]);
95+
assert.strictEqual(model.notifications.size, 1);
96+
assert.strictEqual(notifications.length, 1);
97+
98+
model.clearNotification('octo', 'repo', 5);
99+
assert.strictEqual(model.notifications.size, 0);
100+
assert.strictEqual(notifications.length, 2);
101+
assert.deepStrictEqual(notifications[1], [pr]);
102+
103+
model.clearNotification('octo', 'repo', 5);
104+
assert.strictEqual(notifications.length, 2);
105+
});
106+
107+
it('supports clearing notifications by repository or entirely', () => {
108+
const model = new CopilotStateModel();
109+
const notifications: PullRequestModel[][] = [];
110+
model.onDidChangeNotifications(items => notifications.push(items));
111+
112+
assert.strictEqual(model.isInitialized, false);
113+
model.setInitialized();
114+
assert.strictEqual(model.isInitialized, true);
115+
116+
const prOne = createPullRequest('octo', 'repo', 1);
117+
const prTwo = createPullRequest('octo', 'repo', 2);
118+
const prThree = createPullRequest('other', 'repo', 3);
119+
model.set([
120+
{ pullRequestModel: prOne, status: CopilotPRStatus.Started },
121+
{ pullRequestModel: prTwo, status: CopilotPRStatus.Failed },
122+
{ pullRequestModel: prThree, status: CopilotPRStatus.Completed }
123+
]);
124+
125+
assert.strictEqual(model.notifications.size, 3);
126+
assert.strictEqual(notifications.length, 1);
127+
assert.deepStrictEqual(notifications[0], [prOne, prTwo, prThree]);
128+
assert.strictEqual(model.getNotificationsCount('octo', 'repo'), 2);
129+
assert.deepStrictEqual(model.keys().sort(), ['octo/repo#1', 'octo/repo#2', 'other/repo#3']);
130+
131+
model.clearAllNotifications('octo', 'repo');
132+
assert.strictEqual(model.notifications.size, 1);
133+
assert.strictEqual(model.getNotificationsCount('octo', 'repo'), 0);
134+
assert.strictEqual(notifications.length, 2);
135+
assert.deepStrictEqual(notifications[1], [prOne, prTwo]);
136+
137+
model.clearAllNotifications();
138+
assert.strictEqual(model.notifications.size, 0);
139+
assert.strictEqual(notifications.length, 3);
140+
assert.deepStrictEqual(notifications[2], [prThree]);
141+
142+
const counts = model.getCounts('octo', 'repo');
143+
assert.deepStrictEqual(counts, { total: 3, inProgress: 1, error: 1 });
144+
145+
const allStates = model.all;
146+
assert.strictEqual(allStates.length, 3);
147+
assert.deepStrictEqual(allStates.map(v => v.status).sort(), [CopilotPRStatus.Started, CopilotPRStatus.Completed, CopilotPRStatus.Failed]);
148+
});
149+
});
150+
151+
152+
});

0 commit comments

Comments
 (0)