Skip to content

Commit 13f0f1e

Browse files
feat: implement get_project_details core functionality
Implements handler for detailed project information including process, work item types, and teams. Closes #101
1 parent 6cc0a9b commit 13f0f1e

File tree

9 files changed

+1009
-1
lines changed

9 files changed

+1009
-1
lines changed
+20-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
11
## Current Task
22

3-
No task is currently in progress. Please take the next task from todo.md.
3+
**Task**: Implement get_project_details core functionality (GitHub Issue #101)
4+
**Phase**: Completion
5+
6+
### Description
7+
Implement a handler for the `get_project_details` functionality, which will fetch comprehensive information about a specific Azure DevOps project, including metadata, process information, work item types, fields, and team information.
8+
9+
### Notes
10+
- Need to create the following files:
11+
- `src/features/projects/get-project-details/feature.ts` - Core implementation
12+
- `src/features/projects/get-project-details/schema.ts` - Request/response schema
13+
- `src/features/projects/get-project-details/index.ts` - Export
14+
- The handler should be registered in `src/server.ts` with the name `mcp_azuredevops_get_project_details`
15+
- Need to implement tests for the feature
16+
17+
### Sub-tasks
18+
- [x] Create schema for get_project_details
19+
- [x] Create tests for get_project_details
20+
- [x] Implement get_project_details feature
21+
- [x] Register the handler in server.ts
22+
- [x] Update exports in projects/index.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import { WebApi } from 'azure-devops-node-api';
2+
import { getProjectDetails } from './feature';
3+
import {
4+
getTestConnection,
5+
shouldSkipIntegrationTest,
6+
} from '@/shared/test/test-helpers';
7+
8+
describe('getProjectDetails integration', () => {
9+
let connection: WebApi | null = null;
10+
let projectName: string;
11+
12+
beforeAll(async () => {
13+
// Get a real connection using environment variables
14+
connection = await getTestConnection();
15+
projectName = process.env.AZURE_DEVOPS_DEFAULT_PROJECT || 'DefaultProject';
16+
});
17+
18+
test('should retrieve basic project details from Azure DevOps', async () => {
19+
// Skip if no connection is available
20+
if (shouldSkipIntegrationTest()) {
21+
return;
22+
}
23+
24+
// This connection must be available if we didn't skip
25+
if (!connection) {
26+
throw new Error(
27+
'Connection should be available when test is not skipped',
28+
);
29+
}
30+
31+
// Act - make an actual API call to Azure DevOps
32+
const result = await getProjectDetails(connection, {
33+
projectId: projectName,
34+
});
35+
36+
// Assert on the actual response
37+
expect(result).toBeDefined();
38+
expect(result.name).toBe(projectName);
39+
expect(result.id).toBeDefined();
40+
expect(result.url).toBeDefined();
41+
expect(result.state).toBeDefined();
42+
43+
// Verify basic project structure
44+
expect(result.visibility).toBeDefined();
45+
expect(result.lastUpdateTime).toBeDefined();
46+
expect(result.capabilities).toBeDefined();
47+
});
48+
49+
test('should retrieve project details with teams from Azure DevOps', async () => {
50+
// Skip if no connection is available
51+
if (shouldSkipIntegrationTest()) {
52+
return;
53+
}
54+
55+
// This connection must be available if we didn't skip
56+
if (!connection) {
57+
throw new Error(
58+
'Connection should be available when test is not skipped',
59+
);
60+
}
61+
62+
// Act - make an actual API call to Azure DevOps
63+
const result = await getProjectDetails(connection, {
64+
projectId: projectName,
65+
includeTeams: true,
66+
});
67+
68+
// Assert on the actual response
69+
expect(result).toBeDefined();
70+
expect(result.teams).toBeDefined();
71+
expect(Array.isArray(result.teams)).toBe(true);
72+
73+
// There should be at least one team (the default team)
74+
if (result.teams && result.teams.length > 0) {
75+
const team = result.teams[0];
76+
expect(team.id).toBeDefined();
77+
expect(team.name).toBeDefined();
78+
expect(team.url).toBeDefined();
79+
}
80+
});
81+
82+
test('should retrieve project details with process information from Azure DevOps', async () => {
83+
// Skip if no connection is available
84+
if (shouldSkipIntegrationTest()) {
85+
return;
86+
}
87+
88+
// This connection must be available if we didn't skip
89+
if (!connection) {
90+
throw new Error(
91+
'Connection should be available when test is not skipped',
92+
);
93+
}
94+
95+
// Act - make an actual API call to Azure DevOps
96+
const result = await getProjectDetails(connection, {
97+
projectId: projectName,
98+
includeProcess: true,
99+
});
100+
101+
// Assert on the actual response
102+
expect(result).toBeDefined();
103+
expect(result.process).toBeDefined();
104+
expect(result.process?.name).toBeDefined();
105+
});
106+
107+
test('should retrieve project details with work item types from Azure DevOps', async () => {
108+
// Skip if no connection is available
109+
if (shouldSkipIntegrationTest()) {
110+
return;
111+
}
112+
113+
// This connection must be available if we didn't skip
114+
if (!connection) {
115+
throw new Error(
116+
'Connection should be available when test is not skipped',
117+
);
118+
}
119+
120+
// Act - make an actual API call to Azure DevOps
121+
const result = await getProjectDetails(connection, {
122+
projectId: projectName,
123+
includeProcess: true,
124+
includeWorkItemTypes: true,
125+
});
126+
127+
// Assert on the actual response
128+
expect(result).toBeDefined();
129+
expect(result.process).toBeDefined();
130+
expect(result.process?.workItemTypes).toBeDefined();
131+
expect(Array.isArray(result.process?.workItemTypes)).toBe(true);
132+
133+
// There should be at least one work item type
134+
if (result.process?.workItemTypes && result.process.workItemTypes.length > 0) {
135+
const workItemType = result.process.workItemTypes[0];
136+
expect(workItemType.name).toBeDefined();
137+
expect(workItemType.description).toBeDefined();
138+
expect(workItemType.states).toBeDefined();
139+
}
140+
});
141+
142+
test('should retrieve project details with fields from Azure DevOps', async () => {
143+
// Skip if no connection is available
144+
if (shouldSkipIntegrationTest()) {
145+
return;
146+
}
147+
148+
// This connection must be available if we didn't skip
149+
if (!connection) {
150+
throw new Error(
151+
'Connection should be available when test is not skipped',
152+
);
153+
}
154+
155+
// Act - make an actual API call to Azure DevOps
156+
const result = await getProjectDetails(connection, {
157+
projectId: projectName,
158+
includeProcess: true,
159+
includeWorkItemTypes: true,
160+
includeFields: true,
161+
});
162+
163+
// Assert on the actual response
164+
expect(result).toBeDefined();
165+
expect(result.process).toBeDefined();
166+
expect(result.process?.workItemTypes).toBeDefined();
167+
168+
// There should be at least one work item type with fields
169+
if (result.process?.workItemTypes && result.process.workItemTypes.length > 0) {
170+
const workItemType = result.process.workItemTypes[0];
171+
expect(workItemType.fields).toBeDefined();
172+
expect(Array.isArray(workItemType.fields)).toBe(true);
173+
174+
// There should be at least one field (like Title)
175+
if (workItemType.fields && workItemType.fields.length > 0) {
176+
const field = workItemType.fields[0];
177+
expect(field.name).toBeDefined();
178+
expect(field.referenceName).toBeDefined();
179+
}
180+
}
181+
});
182+
183+
test('should throw error when project is not found', async () => {
184+
// Skip if no connection is available
185+
if (shouldSkipIntegrationTest()) {
186+
return;
187+
}
188+
189+
// This connection must be available if we didn't skip
190+
if (!connection) {
191+
throw new Error(
192+
'Connection should be available when test is not skipped',
193+
);
194+
}
195+
196+
// Use a non-existent project name
197+
const nonExistentProjectName = 'non-existent-project-' + Date.now();
198+
199+
// Act & Assert - should throw an error for non-existent project
200+
await expect(
201+
getProjectDetails(connection, {
202+
projectId: nonExistentProjectName,
203+
}),
204+
).rejects.toThrow(/not found|Failed to get project/);
205+
});
206+
});

0 commit comments

Comments
 (0)