Skip to content

Commit 56eaa56

Browse files
Copilotalexr00
andcommitted
Fix splice bug in removeRepo and removeReviewManager, add tests
The splice() calls were missing the deleteCount argument, causing all elements from the target index onwards to be removed instead of just the single target element. This caused: - Removing a worktree repo could also remove the main repo's manager - PRs would stop appearing under the main repo entry - Stale entries could persist in the sidebar Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
1 parent 7a0845c commit 56eaa56

File tree

3 files changed

+144
-2
lines changed

3 files changed

+144
-2
lines changed

src/github/repositoriesManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export class RepositoriesManager extends Disposable {
127127
const folderManager = this._folderManagers[existingFolderManagerIndex];
128128
disposeAll(this._subs.get(folderManager)!);
129129
this._subs.delete(folderManager);
130-
this._folderManagers.splice(existingFolderManagerIndex);
130+
this._folderManagers.splice(existingFolderManagerIndex, 1);
131131
folderManager.dispose();
132132
this.updateActiveReviewCount();
133133
this._onDidChangeFolderRepositories.fire({});
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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 { Uri } from 'vscode';
7+
import { SinonSandbox, createSandbox } from 'sinon';
8+
import { default as assert } from 'assert';
9+
10+
import { RepositoriesManager } from '../../github/repositoriesManager';
11+
import { FolderRepositoryManager } from '../../github/folderRepositoryManager';
12+
import { GitApiImpl } from '../../api/api1';
13+
import { CredentialStore } from '../../github/credentials';
14+
15+
import { MockTelemetry } from '../mocks/mockTelemetry';
16+
import { MockExtensionContext } from '../mocks/mockExtensionContext';
17+
import { MockRepository } from '../mocks/mockRepository';
18+
import { MockCommandRegistry } from '../mocks/mockCommandRegistry';
19+
import { MockThemeWatcher } from '../mocks/mockThemeWatcher';
20+
import { CreatePullRequestHelper } from '../../view/createPullRequestHelper';
21+
22+
describe('RepositoriesManager', function () {
23+
let sinon: SinonSandbox;
24+
let context: MockExtensionContext;
25+
let telemetry: MockTelemetry;
26+
let credentialStore: CredentialStore;
27+
let reposManager: RepositoriesManager;
28+
let createPrHelper: CreatePullRequestHelper;
29+
let mockThemeWatcher: MockThemeWatcher;
30+
31+
beforeEach(function () {
32+
sinon = createSandbox();
33+
MockCommandRegistry.install(sinon);
34+
mockThemeWatcher = new MockThemeWatcher();
35+
context = new MockExtensionContext();
36+
telemetry = new MockTelemetry();
37+
reposManager = new RepositoriesManager(credentialStore, telemetry);
38+
credentialStore = new CredentialStore(telemetry, context);
39+
createPrHelper = new CreatePullRequestHelper();
40+
});
41+
42+
afterEach(function () {
43+
context.dispose();
44+
sinon.restore();
45+
});
46+
47+
describe('removeRepo', function () {
48+
it('removes only the specified repository when it is not at the last position', function () {
49+
const repo1 = new MockRepository();
50+
repo1.rootUri = Uri.file('/repo1');
51+
repo1.addRemote('origin', 'git@github.com:aaa/bbb');
52+
53+
const repo2 = new MockRepository();
54+
repo2.rootUri = Uri.file('/repo2');
55+
repo2.addRemote('origin', 'git@github.com:ccc/ddd');
56+
57+
const repo3 = new MockRepository();
58+
repo3.rootUri = Uri.file('/repo3');
59+
repo3.addRemote('origin', 'git@github.com:eee/fff');
60+
61+
reposManager.insertFolderManager(new FolderRepositoryManager(0, context, repo1, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper, mockThemeWatcher));
62+
reposManager.insertFolderManager(new FolderRepositoryManager(1, context, repo2, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper, mockThemeWatcher));
63+
reposManager.insertFolderManager(new FolderRepositoryManager(2, context, repo3, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper, mockThemeWatcher));
64+
65+
assert.strictEqual(reposManager.folderManagers.length, 3);
66+
67+
// Remove the repo at the first position
68+
reposManager.removeRepo(repo1);
69+
70+
// Only repo1 should be removed; repo2 and repo3 should remain
71+
assert.strictEqual(reposManager.folderManagers.length, 2);
72+
assert.strictEqual(reposManager.folderManagers[0].repository.rootUri.toString(), repo2.rootUri.toString());
73+
assert.strictEqual(reposManager.folderManagers[1].repository.rootUri.toString(), repo3.rootUri.toString());
74+
});
75+
76+
it('removes only the specified repository when it is at the last position', function () {
77+
const repo1 = new MockRepository();
78+
repo1.rootUri = Uri.file('/repo1');
79+
repo1.addRemote('origin', 'git@github.com:aaa/bbb');
80+
81+
const repo2 = new MockRepository();
82+
repo2.rootUri = Uri.file('/repo2');
83+
repo2.addRemote('origin', 'git@github.com:ccc/ddd');
84+
85+
reposManager.insertFolderManager(new FolderRepositoryManager(0, context, repo1, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper, mockThemeWatcher));
86+
reposManager.insertFolderManager(new FolderRepositoryManager(1, context, repo2, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper, mockThemeWatcher));
87+
88+
assert.strictEqual(reposManager.folderManagers.length, 2);
89+
90+
// Remove the repo at the last position
91+
reposManager.removeRepo(repo2);
92+
93+
assert.strictEqual(reposManager.folderManagers.length, 1);
94+
assert.strictEqual(reposManager.folderManagers[0].repository.rootUri.toString(), repo1.rootUri.toString());
95+
});
96+
97+
it('removes only the middle repository leaving others intact', function () {
98+
const repo1 = new MockRepository();
99+
repo1.rootUri = Uri.file('/repo1');
100+
repo1.addRemote('origin', 'git@github.com:aaa/bbb');
101+
102+
const repo2 = new MockRepository();
103+
repo2.rootUri = Uri.file('/repo2');
104+
repo2.addRemote('origin', 'git@github.com:ccc/ddd');
105+
106+
const repo3 = new MockRepository();
107+
repo3.rootUri = Uri.file('/repo3');
108+
repo3.addRemote('origin', 'git@github.com:eee/fff');
109+
110+
reposManager.insertFolderManager(new FolderRepositoryManager(0, context, repo1, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper, mockThemeWatcher));
111+
reposManager.insertFolderManager(new FolderRepositoryManager(1, context, repo2, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper, mockThemeWatcher));
112+
reposManager.insertFolderManager(new FolderRepositoryManager(2, context, repo3, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper, mockThemeWatcher));
113+
114+
assert.strictEqual(reposManager.folderManagers.length, 3);
115+
116+
// Remove the middle repo
117+
reposManager.removeRepo(repo2);
118+
119+
assert.strictEqual(reposManager.folderManagers.length, 2);
120+
assert.strictEqual(reposManager.folderManagers[0].repository.rootUri.toString(), repo1.rootUri.toString());
121+
assert.strictEqual(reposManager.folderManagers[1].repository.rootUri.toString(), repo3.rootUri.toString());
122+
});
123+
124+
it('does nothing when removing a repo that is not tracked', function () {
125+
const repo1 = new MockRepository();
126+
repo1.rootUri = Uri.file('/repo1');
127+
repo1.addRemote('origin', 'git@github.com:aaa/bbb');
128+
129+
const unknownRepo = new MockRepository();
130+
unknownRepo.rootUri = Uri.file('/unknown');
131+
132+
reposManager.insertFolderManager(new FolderRepositoryManager(0, context, repo1, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper, mockThemeWatcher));
133+
134+
assert.strictEqual(reposManager.folderManagers.length, 1);
135+
136+
reposManager.removeRepo(unknownRepo);
137+
138+
assert.strictEqual(reposManager.folderManagers.length, 1);
139+
assert.strictEqual(reposManager.folderManagers[0].repository.rootUri.toString(), repo1.rootUri.toString());
140+
});
141+
});
142+
});

src/view/reviewsManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class ReviewsManager extends Disposable {
103103
);
104104
if (reviewManagerIndex >= 0) {
105105
const manager = this._reviewManagers[reviewManagerIndex];
106-
this._reviewManagers.splice(reviewManagerIndex);
106+
this._reviewManagers.splice(reviewManagerIndex, 1);
107107
manager.dispose();
108108
}
109109
}

0 commit comments

Comments
 (0)