@aborruso/ckan-mcp-server 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/openspec/apply.md +23 -0
- package/.claude/commands/openspec/archive.md +27 -0
- package/.claude/commands/openspec/proposal.md +28 -0
- package/.claude/settings.local.json +31 -0
- package/.gemini/commands/openspec/apply.toml +21 -0
- package/.gemini/commands/openspec/archive.toml +25 -0
- package/.gemini/commands/openspec/proposal.toml +26 -0
- package/.mcp.json +12 -0
- package/.opencode/command/openspec-apply.md +24 -0
- package/.opencode/command/openspec-archive.md +27 -0
- package/.opencode/command/openspec-proposal.md +29 -0
- package/AGENTS.md +18 -0
- package/CLAUDE.md +320 -0
- package/EXAMPLES.md +707 -0
- package/LICENSE.txt +21 -0
- package/LOG.md +154 -0
- package/PRD.md +912 -0
- package/README.md +468 -0
- package/REFACTORING.md +237 -0
- package/dist/index.js +1277 -0
- package/openspec/AGENTS.md +456 -0
- package/openspec/changes/archive/2026-01-08-add-mcp-resources/design.md +115 -0
- package/openspec/changes/archive/2026-01-08-add-mcp-resources/proposal.md +52 -0
- package/openspec/changes/archive/2026-01-08-add-mcp-resources/specs/mcp-resources/spec.md +92 -0
- package/openspec/changes/archive/2026-01-08-add-mcp-resources/tasks.md +56 -0
- package/openspec/changes/archive/2026-01-08-expand-test-coverage-specs/design.md +355 -0
- package/openspec/changes/archive/2026-01-08-expand-test-coverage-specs/proposal.md +161 -0
- package/openspec/changes/archive/2026-01-08-expand-test-coverage-specs/tasks.md +162 -0
- package/openspec/changes/archive/2026-01-08-translate-project-to-english/proposal.md +115 -0
- package/openspec/changes/archive/2026-01-08-translate-project-to-english/specs/documentation-language/spec.md +32 -0
- package/openspec/changes/archive/2026-01-08-translate-project-to-english/tasks.md +115 -0
- package/openspec/changes/archive/add-automated-tests/design.md +324 -0
- package/openspec/changes/archive/add-automated-tests/proposal.md +167 -0
- package/openspec/changes/archive/add-automated-tests/specs/automated-testing/spec.md +143 -0
- package/openspec/changes/archive/add-automated-tests/tasks.md +132 -0
- package/openspec/project.md +113 -0
- package/openspec/specs/documentation-language/spec.md +32 -0
- package/openspec/specs/mcp-resources/spec.md +94 -0
- package/package.json +46 -0
- package/spunti.md +19 -0
- package/tasks/todo.md +124 -0
- package/test-urls.js +18 -0
- package/tmp/test-org-search.js +55 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# MCP Resources Capability
|
|
2
|
+
|
|
3
|
+
## ADDED Requirements
|
|
4
|
+
|
|
5
|
+
### Requirement: Dataset Resource Template
|
|
6
|
+
|
|
7
|
+
The system SHALL expose a resource template for accessing CKAN dataset metadata via the URI pattern `ckan://{server}/dataset/{id}`.
|
|
8
|
+
|
|
9
|
+
#### Scenario: Read dataset metadata successfully
|
|
10
|
+
|
|
11
|
+
- **WHEN** client reads `ckan://dati.gov.it/dataset/vaccini-covid`
|
|
12
|
+
- **THEN** server returns JSON with complete dataset metadata including title, description, resources, organization, tags
|
|
13
|
+
|
|
14
|
+
#### Scenario: Dataset not found
|
|
15
|
+
|
|
16
|
+
- **WHEN** client reads `ckan://demo.ckan.org/dataset/nonexistent-id`
|
|
17
|
+
- **THEN** server returns error indicating dataset not found
|
|
18
|
+
|
|
19
|
+
#### Scenario: Server unreachable
|
|
20
|
+
|
|
21
|
+
- **WHEN** client reads `ckan://invalid-server.example/dataset/test`
|
|
22
|
+
- **THEN** server returns error indicating server unreachable
|
|
23
|
+
|
|
24
|
+
### Requirement: Resource Resource Template
|
|
25
|
+
|
|
26
|
+
The system SHALL expose a resource template for accessing CKAN resource metadata via the URI pattern `ckan://{server}/resource/{id}`.
|
|
27
|
+
|
|
28
|
+
#### Scenario: Read resource metadata successfully
|
|
29
|
+
|
|
30
|
+
- **WHEN** client reads `ckan://dati.gov.it/resource/abc-123-def`
|
|
31
|
+
- **THEN** server returns JSON with resource metadata including name, format, URL, size, and download link
|
|
32
|
+
|
|
33
|
+
#### Scenario: Resource not found
|
|
34
|
+
|
|
35
|
+
- **WHEN** client reads `ckan://demo.ckan.org/resource/invalid-id`
|
|
36
|
+
- **THEN** server returns error indicating resource not found
|
|
37
|
+
|
|
38
|
+
### Requirement: Organization Resource Template
|
|
39
|
+
|
|
40
|
+
The system SHALL expose a resource template for accessing CKAN organization metadata via the URI pattern `ckan://{server}/organization/{name}`.
|
|
41
|
+
|
|
42
|
+
#### Scenario: Read organization metadata successfully
|
|
43
|
+
|
|
44
|
+
- **WHEN** client reads `ckan://dati.gov.it/organization/regione-toscana`
|
|
45
|
+
- **THEN** server returns JSON with organization metadata including title, description, and dataset count
|
|
46
|
+
|
|
47
|
+
#### Scenario: Organization not found
|
|
48
|
+
|
|
49
|
+
- **WHEN** client reads `ckan://demo.ckan.org/organization/nonexistent-org`
|
|
50
|
+
- **THEN** server returns error indicating organization not found
|
|
51
|
+
|
|
52
|
+
### Requirement: URI Scheme Parsing
|
|
53
|
+
|
|
54
|
+
The system SHALL parse `ckan://` URIs extracting server hostname and path components.
|
|
55
|
+
|
|
56
|
+
#### Scenario: Parse standard URI
|
|
57
|
+
|
|
58
|
+
- **WHEN** URI is `ckan://dati.gov.it/dataset/test-id`
|
|
59
|
+
- **THEN** server extracts: server=`https://dati.gov.it`, type=`dataset`, id=`test-id`
|
|
60
|
+
|
|
61
|
+
#### Scenario: Parse URI with www prefix
|
|
62
|
+
|
|
63
|
+
- **WHEN** URI is `ckan://www.dati.gov.it/dataset/test-id`
|
|
64
|
+
- **THEN** server extracts: server=`https://www.dati.gov.it`, type=`dataset`, id=`test-id`
|
|
65
|
+
|
|
66
|
+
#### Scenario: Reject malformed URI
|
|
67
|
+
|
|
68
|
+
- **WHEN** URI is `ckan://invalid` (missing path)
|
|
69
|
+
- **THEN** server returns validation error
|
|
70
|
+
|
|
71
|
+
### Requirement: Resource Response Format
|
|
72
|
+
|
|
73
|
+
The system SHALL return resource content as JSON with standard MCP resource response structure.
|
|
74
|
+
|
|
75
|
+
#### Scenario: JSON response structure
|
|
76
|
+
|
|
77
|
+
- **WHEN** client reads any valid resource URI
|
|
78
|
+
- **THEN** response contains `contents` array with `uri`, `mimeType` (application/json), and `text` (JSON string)
|
|
79
|
+
|
|
80
|
+
#### Scenario: Large response truncation
|
|
81
|
+
|
|
82
|
+
- **WHEN** resource content exceeds CHARACTER_LIMIT
|
|
83
|
+
- **THEN** response is truncated to CHARACTER_LIMIT with truncation indicator
|
|
84
|
+
|
|
85
|
+
### Requirement: Resource Discovery
|
|
86
|
+
|
|
87
|
+
The system SHALL list available resource templates when client requests resource list.
|
|
88
|
+
|
|
89
|
+
#### Scenario: List resource templates
|
|
90
|
+
|
|
91
|
+
- **WHEN** client calls `resources/listTemplates`
|
|
92
|
+
- **THEN** server returns list of available URI templates with descriptions
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Tasks: Add MCP Resource Templates
|
|
2
|
+
|
|
3
|
+
## 1. Setup
|
|
4
|
+
|
|
5
|
+
- [x] 1.1 Create `src/resources/` directory
|
|
6
|
+
- [x] 1.2 Create `src/resources/index.ts` with `registerAllResources()` export
|
|
7
|
+
|
|
8
|
+
## 2. URI Utilities
|
|
9
|
+
|
|
10
|
+
- [x] 2.1 Create URI parsing function for `ckan://` scheme
|
|
11
|
+
- [x] 2.2 Add validation for malformed URIs
|
|
12
|
+
- [x] 2.3 Handle edge cases (www prefix, trailing slashes)
|
|
13
|
+
|
|
14
|
+
## 3. Dataset Resource
|
|
15
|
+
|
|
16
|
+
- [x] 3.1 Create `src/resources/dataset.ts`
|
|
17
|
+
- [x] 3.2 Register template `ckan://{server}/dataset/{id}`
|
|
18
|
+
- [x] 3.3 Implement handler using `makeCkanRequest('package_show')`
|
|
19
|
+
- [x] 3.4 Return JSON response with dataset metadata
|
|
20
|
+
|
|
21
|
+
## 4. Resource Resource
|
|
22
|
+
|
|
23
|
+
- [x] 4.1 Create `src/resources/resource.ts`
|
|
24
|
+
- [x] 4.2 Register template `ckan://{server}/resource/{id}`
|
|
25
|
+
- [x] 4.3 Implement handler using `makeCkanRequest('resource_show')`
|
|
26
|
+
- [x] 4.4 Include download URL in response
|
|
27
|
+
|
|
28
|
+
## 5. Organization Resource
|
|
29
|
+
|
|
30
|
+
- [x] 5.1 Create `src/resources/organization.ts`
|
|
31
|
+
- [x] 5.2 Register template `ckan://{server}/organization/{name}`
|
|
32
|
+
- [x] 5.3 Implement handler using `makeCkanRequest('organization_show')`
|
|
33
|
+
- [x] 5.4 Return JSON response with organization metadata
|
|
34
|
+
|
|
35
|
+
## 6. Integration
|
|
36
|
+
|
|
37
|
+
- [x] 6.1 Import `registerAllResources` in `src/index.ts`
|
|
38
|
+
- [x] 6.2 Call `registerAllResources(server)` after tool registration
|
|
39
|
+
- [x] 6.3 Build and verify no compilation errors
|
|
40
|
+
|
|
41
|
+
## 7. Testing
|
|
42
|
+
|
|
43
|
+
- [x] 7.1 Create `tests/integration/resources.test.ts`
|
|
44
|
+
- [x] 7.2 Add tests for dataset resource template
|
|
45
|
+
- [x] 7.3 Add tests for resource resource template
|
|
46
|
+
- [x] 7.4 Add tests for organization resource template
|
|
47
|
+
- [x] 7.5 Add tests for URI parsing edge cases
|
|
48
|
+
- [x] 7.6 Add tests for error handling (invalid URI, server errors)
|
|
49
|
+
- [x] 7.7 Run full test suite, ensure 100% pass
|
|
50
|
+
|
|
51
|
+
## 8. Documentation
|
|
52
|
+
|
|
53
|
+
- [x] 8.1 Update README.md with resource templates section
|
|
54
|
+
- [x] 8.2 Update CLAUDE.md architecture section
|
|
55
|
+
- [x] 8.3 Add examples to EXAMPLES.md (deferred - basic examples in README)
|
|
56
|
+
- [x] 8.4 Update LOG.md with changes
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# Design: Expand Test Coverage for Remaining Tools
|
|
2
|
+
|
|
3
|
+
This document explains the testing approach for expanding coverage to package, organization, and datastore tools, following patterns established in the initial testing implementation.
|
|
4
|
+
|
|
5
|
+
## Testing Approach
|
|
6
|
+
|
|
7
|
+
### Pattern Reuse
|
|
8
|
+
|
|
9
|
+
We follow the successful pattern from the initial testing proposal:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// 1. Mock axios
|
|
13
|
+
vi.mock('axios');
|
|
14
|
+
|
|
15
|
+
// 2. Import fixtures
|
|
16
|
+
import packageSearchFixture from '../fixtures/responses/package-search-success.json';
|
|
17
|
+
|
|
18
|
+
// 3. Setup test
|
|
19
|
+
it('returns expected structure', async () => {
|
|
20
|
+
vi.mocked(axios.get).mockResolvedValue({ data: packageSearchFixture });
|
|
21
|
+
|
|
22
|
+
const result = await ckan_package_search({...});
|
|
23
|
+
|
|
24
|
+
// 4. Validate result
|
|
25
|
+
expect(result).toBeDefined();
|
|
26
|
+
expect(result.content[0].text).toContain('...');
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Test Structure
|
|
31
|
+
|
|
32
|
+
Each tool category gets its own test file:
|
|
33
|
+
- `tests/integration/package.test.ts`
|
|
34
|
+
- `tests/integration/organization.test.ts`
|
|
35
|
+
- `tests/integration/datastore.test.ts`
|
|
36
|
+
|
|
37
|
+
## Test Scenarios by Tool Category
|
|
38
|
+
|
|
39
|
+
### Package Tools
|
|
40
|
+
|
|
41
|
+
#### ckan_package_search Tests
|
|
42
|
+
|
|
43
|
+
**Happy Paths:**
|
|
44
|
+
1. Basic search with query parameter
|
|
45
|
+
2. Search with facets (facet_field, facet_limit)
|
|
46
|
+
3. Search with pagination (start, rows)
|
|
47
|
+
4. Search with sorting (sort)
|
|
48
|
+
5. Search with filter query (fq)
|
|
49
|
+
6. Empty results (q returns no matches)
|
|
50
|
+
|
|
51
|
+
**Edge Cases:**
|
|
52
|
+
7. Very large results (rows > 1000)
|
|
53
|
+
8. Multiple facet fields
|
|
54
|
+
9. Complex query (q + fq + facets)
|
|
55
|
+
10. Markdown output format validation
|
|
56
|
+
11. JSON output format validation
|
|
57
|
+
|
|
58
|
+
**Error Scenarios:**
|
|
59
|
+
12. Invalid query syntax (if applicable)
|
|
60
|
+
13. Server error during search
|
|
61
|
+
14. Timeout during search
|
|
62
|
+
|
|
63
|
+
#### ckan_package_show Tests
|
|
64
|
+
|
|
65
|
+
**Happy Paths:**
|
|
66
|
+
1. Show package with valid ID
|
|
67
|
+
2. Package with multiple resources
|
|
68
|
+
3. Package with tags
|
|
69
|
+
4. Package with organization
|
|
70
|
+
5. Package without resources
|
|
71
|
+
|
|
72
|
+
**Edge Cases:**
|
|
73
|
+
6. Package with very long description (test truncation)
|
|
74
|
+
7. Package with many resources (>10)
|
|
75
|
+
|
|
76
|
+
**Error Scenarios:**
|
|
77
|
+
8. Invalid package ID (404)
|
|
78
|
+
9. Server error during fetch
|
|
79
|
+
10. Timeout during fetch
|
|
80
|
+
|
|
81
|
+
### Organization Tools
|
|
82
|
+
|
|
83
|
+
#### ckan_organization_list Tests
|
|
84
|
+
|
|
85
|
+
**Happy Paths:**
|
|
86
|
+
1. List all organizations (default)
|
|
87
|
+
2. List with limit parameter
|
|
88
|
+
3. List with offset parameter
|
|
89
|
+
4. List with sort parameter
|
|
90
|
+
5. Empty result (no organizations)
|
|
91
|
+
|
|
92
|
+
**Edge Cases:**
|
|
93
|
+
6. Very large list (all organizations)
|
|
94
|
+
7. Sorting by different fields (name, title, package_count)
|
|
95
|
+
|
|
96
|
+
**Error Scenarios:**
|
|
97
|
+
8. Server error during list
|
|
98
|
+
9. Timeout during list
|
|
99
|
+
|
|
100
|
+
#### ckan_organization_show Tests
|
|
101
|
+
|
|
102
|
+
**Happy Paths:**
|
|
103
|
+
1. Show organization with valid ID
|
|
104
|
+
2. Organization with users
|
|
105
|
+
3. Organization with package_count
|
|
106
|
+
4. Organization with extras (custom fields)
|
|
107
|
+
|
|
108
|
+
**Edge Cases:**
|
|
109
|
+
5. Organization with very long description
|
|
110
|
+
|
|
111
|
+
**Error Scenarios:**
|
|
112
|
+
6. Invalid organization ID (404)
|
|
113
|
+
7. Server error during fetch
|
|
114
|
+
8. Timeout during fetch
|
|
115
|
+
|
|
116
|
+
#### ckan_organization_search Tests
|
|
117
|
+
|
|
118
|
+
**Happy Paths:**
|
|
119
|
+
1. Search with query
|
|
120
|
+
2. Search with wildcard (*)
|
|
121
|
+
3. Search with facets
|
|
122
|
+
4. Search with pagination
|
|
123
|
+
|
|
124
|
+
**Edge Cases:**
|
|
125
|
+
5. Search returning many results
|
|
126
|
+
6. Search returning no results
|
|
127
|
+
|
|
128
|
+
**Error Scenarios:**
|
|
129
|
+
7. Invalid query
|
|
130
|
+
8. Server error during search
|
|
131
|
+
9. Timeout during search
|
|
132
|
+
|
|
133
|
+
### DataStore Tools
|
|
134
|
+
|
|
135
|
+
#### ckan_datastore_search Tests
|
|
136
|
+
|
|
137
|
+
**Happy Paths:**
|
|
138
|
+
1. Basic query with resource_id
|
|
139
|
+
2. Query with filters
|
|
140
|
+
3. Query with sorting
|
|
141
|
+
4. Query with pagination (limit, offset)
|
|
142
|
+
5. Query with multiple filters
|
|
143
|
+
6. Query with field list (fields parameter)
|
|
144
|
+
|
|
145
|
+
**Edge Cases:**
|
|
146
|
+
7. Large result set (test truncation)
|
|
147
|
+
8. Complex filters (AND/OR logic)
|
|
148
|
+
9. Empty result (no records)
|
|
149
|
+
|
|
150
|
+
**Error Scenarios:**
|
|
151
|
+
10. Invalid resource_id
|
|
152
|
+
11. Invalid filter syntax
|
|
153
|
+
12. Server error during query
|
|
154
|
+
13. Timeout during query
|
|
155
|
+
14. DataStore not enabled for resource
|
|
156
|
+
|
|
157
|
+
## Output Format Validation
|
|
158
|
+
|
|
159
|
+
All tools support both markdown and JSON output. Tests should validate:
|
|
160
|
+
|
|
161
|
+
### Markdown Format
|
|
162
|
+
```typescript
|
|
163
|
+
test('returns markdown format', async () => {
|
|
164
|
+
vi.mocked(axios.get).mockResolvedValue({ data: fixture });
|
|
165
|
+
|
|
166
|
+
const result = await ckan_package_search({ format: 'markdown', ... });
|
|
167
|
+
|
|
168
|
+
expect(result.content[0].type).toBe('text');
|
|
169
|
+
expect(result.content[0].text).toMatch(/^# /); // Markdown heading
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### JSON Format
|
|
174
|
+
```typescript
|
|
175
|
+
test('returns JSON format', async () => {
|
|
176
|
+
vi.mocked(axios.get).mockResolvedValue({ data: fixture });
|
|
177
|
+
|
|
178
|
+
const result = await ckan_package_search({ format: 'json', ... });
|
|
179
|
+
|
|
180
|
+
expect(result.content[0].type).toBe('resource');
|
|
181
|
+
expect(result.structuredContent).toBeDefined();
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Error Handling Tests
|
|
186
|
+
|
|
187
|
+
All tools should handle errors gracefully:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
test('handles 404 error', async () => {
|
|
191
|
+
vi.mocked(axios.get).mockRejectedValue({
|
|
192
|
+
isAxiosError: true,
|
|
193
|
+
response: { status: 404, data: notFoundFixture }
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const result = await ckan_package_show({ package_id: 'invalid' });
|
|
197
|
+
|
|
198
|
+
expect(result.isError).toBe(true);
|
|
199
|
+
expect(result.content[0].text).toContain('Not found');
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Fixture Strategy
|
|
204
|
+
|
|
205
|
+
### Existing Fixtures
|
|
206
|
+
|
|
207
|
+
Reuse fixtures from initial proposal:
|
|
208
|
+
- `package-search-success.json` - already exists
|
|
209
|
+
- `package-show-success.json` - already exists
|
|
210
|
+
- `organization-list-success.json` - already exists
|
|
211
|
+
- `organization-show-success.json` - already exists
|
|
212
|
+
- `organization-search-success.json` - already exists
|
|
213
|
+
- `datastore-search-success.json` - already exists
|
|
214
|
+
|
|
215
|
+
### Additional Fixtures Needed
|
|
216
|
+
|
|
217
|
+
May need to create:
|
|
218
|
+
- `package-empty-results.json` - for empty search results
|
|
219
|
+
- `organization-empty-results.json` - for empty org list
|
|
220
|
+
- `datastore-empty-results.json` - for empty data queries
|
|
221
|
+
- Error fixtures specific to each tool if needed
|
|
222
|
+
|
|
223
|
+
### Creating New Fixtures
|
|
224
|
+
|
|
225
|
+
Follow existing structure:
|
|
226
|
+
|
|
227
|
+
**Success fixture:**
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"help": "API help URL",
|
|
231
|
+
"success": true,
|
|
232
|
+
"result": { ... }
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Error fixture:**
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"success": false,
|
|
240
|
+
"error": {
|
|
241
|
+
"__type": "Error Type",
|
|
242
|
+
"message": "Error message",
|
|
243
|
+
"status": 404
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Coverage Goals
|
|
249
|
+
|
|
250
|
+
### Target Metrics
|
|
251
|
+
|
|
252
|
+
| Component | Current Coverage | Target Coverage | Tests Needed |
|
|
253
|
+
|-----------|------------------|-----------------|---------------|
|
|
254
|
+
| Utils | 100% | 100% | ✅ Done |
|
|
255
|
+
| Status Tool | ~50% | 75-85% | ✅ Done |
|
|
256
|
+
| Package Tools | 0% | 75-85% | ~16 tests |
|
|
257
|
+
| Organization Tools | 0% | 75-85% | ~18 tests |
|
|
258
|
+
| DataStore Tool | 0% | 75-85% | ~14 tests |
|
|
259
|
+
| **Overall** | **~68%** | **80%+** | **~48 tests** |
|
|
260
|
+
|
|
261
|
+
### Verification
|
|
262
|
+
|
|
263
|
+
After each tool category is tested, run:
|
|
264
|
+
```bash
|
|
265
|
+
npm run test:coverage
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Ensure coverage increases and meets targets.
|
|
269
|
+
|
|
270
|
+
## Test Writing Guidelines
|
|
271
|
+
|
|
272
|
+
### Naming Convention
|
|
273
|
+
```typescript
|
|
274
|
+
// Good
|
|
275
|
+
test('ckan_package_search with facets returns aggregated data', () => { ... });
|
|
276
|
+
|
|
277
|
+
// Bad
|
|
278
|
+
test('search works', () => { ... });
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### AAA Pattern
|
|
282
|
+
```typescript
|
|
283
|
+
test('search with pagination', async () => {
|
|
284
|
+
// Arrange
|
|
285
|
+
const params = { q: 'test', start: 10, rows: 20 };
|
|
286
|
+
|
|
287
|
+
// Act
|
|
288
|
+
const result = await ckan_package_search(params);
|
|
289
|
+
|
|
290
|
+
// Assert
|
|
291
|
+
expect(result.content[0].text).toContain('Showing 10-20 of');
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### One Assertion Per Test
|
|
296
|
+
```typescript
|
|
297
|
+
// Good
|
|
298
|
+
test('truncates long descriptions', async () => {
|
|
299
|
+
const result = await ckan_package_show({ package_id: 'long-desc' });
|
|
300
|
+
expect(result.content[0].text).toContain('[Response truncated]');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Bad
|
|
304
|
+
test('package_show works', async () => {
|
|
305
|
+
const result = await ckan_package_show({ package_id: 'pkg' });
|
|
306
|
+
expect(result).toBeDefined();
|
|
307
|
+
expect(result.content).toBeDefined();
|
|
308
|
+
expect(result.content[0]).toBeDefined();
|
|
309
|
+
expect(result.content[0].text).toBeDefined();
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Implementation Order
|
|
314
|
+
|
|
315
|
+
1. **Package Tools** (highest priority)
|
|
316
|
+
- Most complex
|
|
317
|
+
- Most used
|
|
318
|
+
- Largest code block (~350 lines)
|
|
319
|
+
|
|
320
|
+
2. **Organization Tools** (medium priority)
|
|
321
|
+
- Medium complexity
|
|
322
|
+
- Medium code block (~341 lines)
|
|
323
|
+
|
|
324
|
+
3. **DataStore Tool** (lower priority)
|
|
325
|
+
- Simpler logic
|
|
326
|
+
- Smaller code block (~146 lines)
|
|
327
|
+
- May have platform-specific behavior
|
|
328
|
+
|
|
329
|
+
## Future Enhancements
|
|
330
|
+
|
|
331
|
+
### Short Term
|
|
332
|
+
- Contract tests for CKAN API responses
|
|
333
|
+
- Visual regression tests for markdown output
|
|
334
|
+
- Performance benchmarks for queries
|
|
335
|
+
|
|
336
|
+
### Medium Term
|
|
337
|
+
- Increase coverage to 90%+
|
|
338
|
+
- Add contract tests for all tool schemas
|
|
339
|
+
- Add integration with real test CKAN instance (optional)
|
|
340
|
+
|
|
341
|
+
### Long Term
|
|
342
|
+
- Fuzz testing for input validation
|
|
343
|
+
- Chaos testing for error handling
|
|
344
|
+
- Property-based testing
|
|
345
|
+
|
|
346
|
+
## Conclusion
|
|
347
|
+
|
|
348
|
+
This test expansion follows the proven patterns from the initial testing proposal, focusing on:
|
|
349
|
+
|
|
350
|
+
- **Consistency**: Same structure, fixtures, and patterns
|
|
351
|
+
- **Completeness**: Cover all tool categories
|
|
352
|
+
- **Quality**: Test both happy paths and error scenarios
|
|
353
|
+
- **Maintainability**: Simple, focused, well-documented tests
|
|
354
|
+
|
|
355
|
+
The incremental implementation ensures we achieve 80%+ coverage while keeping the work manageable and debuggable.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Proposal: Expand Test Coverage to Remaining Tools
|
|
2
|
+
|
|
3
|
+
**Status:** Completed
|
|
4
|
+
**Created:** 2026-01-08
|
|
5
|
+
**Author:** OpenCode
|
|
6
|
+
**Related:** Archived proposal "add-automated-tests"
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
Add integration tests for the remaining MCP tools (package, organization, datastore) to achieve 80%+ code coverage and improve test reliability.
|
|
11
|
+
|
|
12
|
+
## Motivation
|
|
13
|
+
|
|
14
|
+
The initial automated testing implementation successfully established the testing foundation but only covered:
|
|
15
|
+
- Utility functions (100% coverage)
|
|
16
|
+
- Status tool (partial coverage)
|
|
17
|
+
- Overall coverage: ~68%
|
|
18
|
+
|
|
19
|
+
To meet the 80% coverage target and ensure reliability, we need to test the remaining three tool categories that represent the majority of the codebase:
|
|
20
|
+
- Package tools: ~350 lines
|
|
21
|
+
- Organization tools: ~341 lines
|
|
22
|
+
- DataStore tools: ~146 lines
|
|
23
|
+
|
|
24
|
+
Adding these tests will:
|
|
25
|
+
- Increase overall coverage to 80%+
|
|
26
|
+
- Catch bugs in tool implementations
|
|
27
|
+
- Document expected behavior through tests
|
|
28
|
+
- Enable confident refactoring of tool code
|
|
29
|
+
- Provide safety net for future changes
|
|
30
|
+
|
|
31
|
+
## Scope
|
|
32
|
+
|
|
33
|
+
### Included
|
|
34
|
+
- **Integration tests** for package tools (ckan_package_search, ckan_package_show)
|
|
35
|
+
- **Integration tests** for organization tools (ckan_organization_list, ckan_organization_show, ckan_organization_search)
|
|
36
|
+
- **Integration tests** for datastore tool (ckan_datastore_search)
|
|
37
|
+
- Additional fixtures for error scenarios if needed
|
|
38
|
+
- Tests for both markdown and JSON output formats
|
|
39
|
+
- Tests for edge cases (empty results, pagination, filters)
|
|
40
|
+
|
|
41
|
+
### Excluded
|
|
42
|
+
- E2e tests with real CKAN servers (already excluded)
|
|
43
|
+
- Performance/benchmark tests (future enhancement)
|
|
44
|
+
- UI/interaction tests (no UI in this project)
|
|
45
|
+
- Testing MCP SDK internals (out of scope)
|
|
46
|
+
- Testing external libraries (axios, zod, express)
|
|
47
|
+
|
|
48
|
+
## Proposed Changes
|
|
49
|
+
|
|
50
|
+
### Test Strategy
|
|
51
|
+
|
|
52
|
+
Following the same pattern established in the first proposal:
|
|
53
|
+
1. Use Vitest with mocked axios
|
|
54
|
+
2. Reuse existing fixtures from `tests/fixtures/`
|
|
55
|
+
3. Create new fixtures only if needed
|
|
56
|
+
4. Test success and error scenarios
|
|
57
|
+
5. Test both markdown and JSON output formats
|
|
58
|
+
|
|
59
|
+
### Test Coverage Breakdown
|
|
60
|
+
|
|
61
|
+
#### Package Tools (~6 tests)
|
|
62
|
+
- Basic search with query
|
|
63
|
+
- Search with facets
|
|
64
|
+
- Search with pagination
|
|
65
|
+
- Search with sorting
|
|
66
|
+
- Search with filter query
|
|
67
|
+
- Show package details
|
|
68
|
+
|
|
69
|
+
#### Organization Tools (~8 tests)
|
|
70
|
+
- List organizations
|
|
71
|
+
- List with sorting
|
|
72
|
+
- Show organization details
|
|
73
|
+
- Search organizations
|
|
74
|
+
- Error scenarios (404, invalid ID)
|
|
75
|
+
|
|
76
|
+
#### DataStore Tool (~6 tests)
|
|
77
|
+
- Basic query
|
|
78
|
+
- Query with filters
|
|
79
|
+
- Query with sorting
|
|
80
|
+
- Query with pagination
|
|
81
|
+
- Error scenarios
|
|
82
|
+
- Output format validation
|
|
83
|
+
|
|
84
|
+
### Coverage Target
|
|
85
|
+
|
|
86
|
+
**Goal**: Increase from ~68% to 80%+
|
|
87
|
+
|
|
88
|
+
**Expected breakdown after completion**:
|
|
89
|
+
- Utils: 100% (already achieved)
|
|
90
|
+
- Status tool: 75-85%
|
|
91
|
+
- Package tools: 75-85%
|
|
92
|
+
- Organization tools: 75-85%
|
|
93
|
+
- DataStore tool: 75-85%
|
|
94
|
+
- **Overall**: 80%+
|
|
95
|
+
|
|
96
|
+
### Incremental Approach
|
|
97
|
+
|
|
98
|
+
1. Start with package tools (largest, most complex)
|
|
99
|
+
2. Add organization tests
|
|
100
|
+
3. Add datastore tests
|
|
101
|
+
4. Validate coverage after each batch
|
|
102
|
+
5. Adjust if needed
|
|
103
|
+
|
|
104
|
+
## Alternatives Considered
|
|
105
|
+
|
|
106
|
+
1. **Test all tools at once**
|
|
107
|
+
- *Rejected*: Too large, hard to debug, follow incremental pattern
|
|
108
|
+
|
|
109
|
+
2. **Skip integration tests, only unit tests**
|
|
110
|
+
- *Rejected*: Would miss tool behavior and CKAN API interactions
|
|
111
|
+
|
|
112
|
+
3. **Use real CKAN servers for testing**
|
|
113
|
+
- *Rejected*: Already excluded in original proposal (slow, unreliable)
|
|
114
|
+
|
|
115
|
+
4. **Test only happy paths, ignore errors**
|
|
116
|
+
- *Rejected*: Error handling is critical for robust tools
|
|
117
|
+
|
|
118
|
+
## Impact Assessment
|
|
119
|
+
|
|
120
|
+
### Benefits
|
|
121
|
+
- + Increased coverage from 68% to 80%+
|
|
122
|
+
- + Better bug detection in tool code
|
|
123
|
+
- + Safer refactoring of tool implementations
|
|
124
|
+
- + Documented expected behavior for all tools
|
|
125
|
+
- + Higher confidence in code quality
|
|
126
|
+
|
|
127
|
+
### Risks
|
|
128
|
+
- - Initial time investment for writing ~20 more tests
|
|
129
|
+
- - Test maintenance overhead as tool code evolves
|
|
130
|
+
- - Possible need for additional fixtures
|
|
131
|
+
|
|
132
|
+
### Mitigation
|
|
133
|
+
- Reuse existing fixtures where possible
|
|
134
|
+
- Follow established test patterns
|
|
135
|
+
- Write tests incrementally, validate after each batch
|
|
136
|
+
- Keep tests simple and focused
|
|
137
|
+
|
|
138
|
+
## Open Questions
|
|
139
|
+
|
|
140
|
+
None - reusing established patterns from first proposal.
|
|
141
|
+
|
|
142
|
+
## Dependencies
|
|
143
|
+
|
|
144
|
+
Depends on:
|
|
145
|
+
- Proposal "add-automated-tests" (archived) - provides test infrastructure and fixtures
|
|
146
|
+
- Existing fixtures in `tests/fixtures/` - may need additions
|
|
147
|
+
|
|
148
|
+
## Success Criteria
|
|
149
|
+
|
|
150
|
+
- [ ] All package tools have integration tests
|
|
151
|
+
- [ ] All organization tools have integration tests
|
|
152
|
+
- [ ] DataStore tool has integration tests
|
|
153
|
+
- [ ] Tests pass (green CI if exists)
|
|
154
|
+
- [ ] Overall coverage meets 80% threshold
|
|
155
|
+
- [ ] Tests follow established patterns from first proposal
|
|
156
|
+
- [ ] Documentation updated if needed
|
|
157
|
+
- [ ] Validation: `openspec validate expand-test-coverage --strict` passes
|
|
158
|
+
|
|
159
|
+
## Notes
|
|
160
|
+
|
|
161
|
+
This proposal is a direct continuation of the "add-automated-tests" proposal. All testing infrastructure (Vitest, fixtures, patterns) is already in place, so the focus is purely on writing the actual test files for the remaining tools.
|