Skip to content

Commit d1b84fc

Browse files
Copiloteleanorjboyd
andcommitted
Add comprehensive tests for getProjectInstallable
- Test finding dev-requirements.txt at workspace root - Test deduplication of files found by multiple patterns - Test finding files in subdirectories and requirements/ folders - Test filtering files by project directories - All 181 unit tests passing (5 new tests added) Co-authored-by: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com>
1 parent bc01e81 commit d1b84fc

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import assert from 'assert';
2+
import * as sinon from 'sinon';
3+
import { Uri } from 'vscode';
4+
import { getProjectInstallable } from '../../../managers/builtin/pipUtils';
5+
import * as wapi from '../../../common/workspace.apis';
6+
import * as winapi from '../../../common/window.apis';
7+
8+
suite('Pip Utils - getProjectInstallable', () => {
9+
let findFilesStub: sinon.SinonStub;
10+
let withProgressStub: sinon.SinonStub;
11+
let mockApi: any;
12+
13+
setup(() => {
14+
findFilesStub = sinon.stub(wapi, 'findFiles');
15+
// Stub withProgress to immediately execute the callback
16+
withProgressStub = sinon.stub(winapi, 'withProgress');
17+
withProgressStub.callsFake(async (_options: any, callback: any) => {
18+
return await callback(undefined, { isCancellationRequested: false });
19+
});
20+
21+
mockApi = {
22+
getPythonProject: (uri: Uri) => {
23+
// Return a project for any URI in /workspace
24+
if (uri.fsPath.startsWith('/workspace')) {
25+
return { uri: Uri.file('/workspace') };
26+
}
27+
return undefined;
28+
},
29+
};
30+
});
31+
32+
teardown(() => {
33+
sinon.restore();
34+
});
35+
36+
test('should find dev-requirements.txt at workspace root', async () => {
37+
// Arrange: Mock findFiles to return both requirements.txt and dev-requirements.txt
38+
findFilesStub.callsFake((pattern: string) => {
39+
if (pattern === '**/*requirements*.txt') {
40+
// This pattern might not match root-level files in VS Code
41+
return Promise.resolve([]);
42+
} else if (pattern === '*requirements*.txt') {
43+
// This pattern should match root-level files
44+
return Promise.resolve([
45+
Uri.file('/workspace/requirements.txt'),
46+
Uri.file('/workspace/dev-requirements.txt'),
47+
Uri.file('/workspace/test-requirements.txt'),
48+
]);
49+
} else if (pattern === '**/requirements/*.txt') {
50+
return Promise.resolve([]);
51+
} else if (pattern === '**/pyproject.toml') {
52+
return Promise.resolve([]);
53+
}
54+
return Promise.resolve([]);
55+
});
56+
57+
// Act: Call getProjectInstallable
58+
const projects = [{ name: 'workspace', uri: Uri.file('/workspace') }];
59+
const result = await getProjectInstallable(mockApi, projects);
60+
61+
// Assert: Should find all three requirements files
62+
assert.strictEqual(result.length, 3, 'Should find three requirements files');
63+
64+
const names = result.map((r) => r.name).sort();
65+
assert.deepStrictEqual(
66+
names,
67+
['dev-requirements.txt', 'requirements.txt', 'test-requirements.txt'],
68+
'Should find requirements.txt, dev-requirements.txt, and test-requirements.txt',
69+
);
70+
71+
// Verify each file has correct properties
72+
result.forEach((item) => {
73+
assert.strictEqual(item.group, 'Requirements', 'Should be in Requirements group');
74+
assert.ok(item.args, 'Should have args');
75+
assert.strictEqual(item.args?.length, 2, 'Should have 2 args');
76+
assert.strictEqual(item.args?.[0], '-r', 'First arg should be -r');
77+
assert.ok(item.uri, 'Should have a URI');
78+
});
79+
});
80+
81+
test('should deduplicate files found by multiple patterns', async () => {
82+
// Arrange: Mock both patterns to return the same file
83+
findFilesStub.callsFake((pattern: string) => {
84+
if (pattern === '**/*requirements*.txt') {
85+
return Promise.resolve([
86+
Uri.file('/workspace/dev-requirements.txt'),
87+
]);
88+
} else if (pattern === '*requirements*.txt') {
89+
return Promise.resolve([
90+
Uri.file('/workspace/dev-requirements.txt'),
91+
Uri.file('/workspace/requirements.txt'),
92+
]);
93+
} else if (pattern === '**/requirements/*.txt') {
94+
return Promise.resolve([]);
95+
} else if (pattern === '**/pyproject.toml') {
96+
return Promise.resolve([]);
97+
}
98+
return Promise.resolve([]);
99+
});
100+
101+
// Act: Call getProjectInstallable
102+
const projects = [{ name: 'workspace', uri: Uri.file('/workspace') }];
103+
const result = await getProjectInstallable(mockApi, projects);
104+
105+
// Assert: Should deduplicate and only have 2 unique files
106+
assert.strictEqual(result.length, 2, 'Should deduplicate and have 2 unique files');
107+
108+
const names = result.map((r) => r.name).sort();
109+
assert.deepStrictEqual(
110+
names,
111+
['dev-requirements.txt', 'requirements.txt'],
112+
'Should have deduplicated results',
113+
);
114+
});
115+
116+
test('should find requirements files in subdirectories', async () => {
117+
// Arrange: Mock findFiles to return files in subdirectories
118+
findFilesStub.callsFake((pattern: string) => {
119+
if (pattern === '**/*requirements*.txt') {
120+
return Promise.resolve([
121+
Uri.file('/workspace/subdir/dev-requirements.txt'),
122+
]);
123+
} else if (pattern === '*requirements*.txt') {
124+
return Promise.resolve([
125+
Uri.file('/workspace/requirements.txt'),
126+
]);
127+
} else if (pattern === '**/requirements/*.txt') {
128+
return Promise.resolve([
129+
Uri.file('/workspace/requirements/test.txt'),
130+
]);
131+
} else if (pattern === '**/pyproject.toml') {
132+
return Promise.resolve([]);
133+
}
134+
return Promise.resolve([]);
135+
});
136+
137+
// Act: Call getProjectInstallable
138+
const projects = [{ name: 'workspace', uri: Uri.file('/workspace') }];
139+
const result = await getProjectInstallable(mockApi, projects);
140+
141+
// Assert: Should find all files
142+
assert.strictEqual(result.length, 3, 'Should find three files');
143+
144+
const names = result.map((r) => r.name).sort();
145+
assert.deepStrictEqual(
146+
names,
147+
['dev-requirements.txt', 'requirements.txt', 'test.txt'],
148+
'Should find files at different levels',
149+
);
150+
});
151+
152+
test('should return empty array when no projects provided', async () => {
153+
// Act: Call with no projects
154+
const result = await getProjectInstallable(mockApi, undefined);
155+
156+
// Assert: Should return empty array
157+
assert.strictEqual(result.length, 0, 'Should return empty array');
158+
assert.ok(!findFilesStub.called, 'Should not call findFiles when no projects');
159+
});
160+
161+
test('should filter out files not in project directories', async () => {
162+
// Arrange: Mock findFiles to return files from multiple directories
163+
findFilesStub.callsFake((pattern: string) => {
164+
if (pattern === '*requirements*.txt') {
165+
return Promise.resolve([
166+
Uri.file('/workspace/requirements.txt'),
167+
Uri.file('/other-dir/requirements.txt'), // Should be filtered out
168+
]);
169+
} else {
170+
return Promise.resolve([]);
171+
}
172+
});
173+
174+
// Act: Call with only /workspace project
175+
const projects = [{ name: 'workspace', uri: Uri.file('/workspace') }];
176+
const result = await getProjectInstallable(mockApi, projects);
177+
178+
// Assert: Should only include files from /workspace
179+
assert.strictEqual(result.length, 1, 'Should only include files from project directory');
180+
const firstResult = result[0];
181+
assert.ok(firstResult, 'Should have at least one result');
182+
assert.strictEqual(firstResult.name, 'requirements.txt');
183+
assert.ok(firstResult.uri, 'Should have a URI');
184+
assert.ok(firstResult.uri.fsPath.startsWith('/workspace'), 'Should be in workspace directory');
185+
});
186+
});

0 commit comments

Comments
 (0)