memory-journal-mcp 4.4.2 → 5.0.0
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/.github/workflows/codeql.yml +1 -6
- package/.github/workflows/docker-publish.yml +15 -49
- package/.github/workflows/lint-and-test.yml +1 -1
- package/.github/workflows/secrets-scanning.yml +4 -3
- package/.github/workflows/security-update.yml +3 -3
- package/CHANGELOG.md +213 -0
- package/CONTRIBUTING.md +132 -97
- package/DOCKER_README.md +184 -235
- package/Dockerfile +27 -24
- package/README.md +218 -190
- package/SECURITY.md +27 -35
- package/dist/cli.js +16 -1
- package/dist/cli.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +5 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +133 -73
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/constants/icons.d.ts +2 -2
- package/dist/constants/icons.d.ts.map +1 -1
- package/dist/constants/icons.js +7 -6
- package/dist/constants/icons.js.map +1 -1
- package/dist/database/SqliteAdapter.d.ts +37 -24
- package/dist/database/SqliteAdapter.d.ts.map +1 -1
- package/dist/database/SqliteAdapter.js +319 -157
- package/dist/database/SqliteAdapter.js.map +1 -1
- package/dist/database/schema.d.ts +45 -0
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/database/schema.js +92 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/filtering/ToolFilter.d.ts +1 -1
- package/dist/filtering/ToolFilter.d.ts.map +1 -1
- package/dist/filtering/ToolFilter.js +13 -2
- package/dist/filtering/ToolFilter.js.map +1 -1
- package/dist/github/GitHubIntegration.d.ts.map +1 -1
- package/dist/github/GitHubIntegration.js +1 -3
- package/dist/github/GitHubIntegration.js.map +1 -1
- package/dist/handlers/prompts/github.d.ts +12 -0
- package/dist/handlers/prompts/github.d.ts.map +1 -0
- package/dist/handlers/prompts/github.js +178 -0
- package/dist/handlers/prompts/github.js.map +1 -0
- package/dist/handlers/prompts/index.d.ts +23 -2
- package/dist/handlers/prompts/index.d.ts.map +1 -1
- package/dist/handlers/prompts/index.js +7 -432
- package/dist/handlers/prompts/index.js.map +1 -1
- package/dist/handlers/prompts/workflow.d.ts +12 -0
- package/dist/handlers/prompts/workflow.d.ts.map +1 -0
- package/dist/handlers/prompts/workflow.js +277 -0
- package/dist/handlers/prompts/workflow.js.map +1 -0
- package/dist/handlers/resources/core.d.ts +11 -0
- package/dist/handlers/resources/core.d.ts.map +1 -0
- package/dist/handlers/resources/core.js +433 -0
- package/dist/handlers/resources/core.js.map +1 -0
- package/dist/handlers/resources/github.d.ts +11 -0
- package/dist/handlers/resources/github.d.ts.map +1 -0
- package/dist/handlers/resources/github.js +314 -0
- package/dist/handlers/resources/github.js.map +1 -0
- package/dist/handlers/resources/graph.d.ts +11 -0
- package/dist/handlers/resources/graph.d.ts.map +1 -0
- package/dist/handlers/resources/graph.js +204 -0
- package/dist/handlers/resources/graph.js.map +1 -0
- package/dist/handlers/resources/index.d.ts +5 -20
- package/dist/handlers/resources/index.d.ts.map +1 -1
- package/dist/handlers/resources/index.js +16 -1278
- package/dist/handlers/resources/index.js.map +1 -1
- package/dist/handlers/resources/shared.d.ts +60 -0
- package/dist/handlers/resources/shared.d.ts.map +1 -0
- package/dist/handlers/resources/shared.js +49 -0
- package/dist/handlers/resources/shared.js.map +1 -0
- package/dist/handlers/resources/team.d.ts +13 -0
- package/dist/handlers/resources/team.d.ts.map +1 -0
- package/dist/handlers/resources/team.js +119 -0
- package/dist/handlers/resources/team.js.map +1 -0
- package/dist/handlers/resources/templates.d.ts +13 -0
- package/dist/handlers/resources/templates.d.ts.map +1 -0
- package/dist/handlers/resources/templates.js +310 -0
- package/dist/handlers/resources/templates.js.map +1 -0
- package/dist/handlers/tools/admin.d.ts +8 -0
- package/dist/handlers/tools/admin.d.ts.map +1 -0
- package/dist/handlers/tools/admin.js +270 -0
- package/dist/handlers/tools/admin.js.map +1 -0
- package/dist/handlers/tools/analytics.d.ts +8 -0
- package/dist/handlers/tools/analytics.d.ts.map +1 -0
- package/dist/handlers/tools/analytics.js +256 -0
- package/dist/handlers/tools/analytics.js.map +1 -0
- package/dist/handlers/tools/backup.d.ts +8 -0
- package/dist/handlers/tools/backup.d.ts.map +1 -0
- package/dist/handlers/tools/backup.js +224 -0
- package/dist/handlers/tools/backup.js.map +1 -0
- package/dist/handlers/tools/core.d.ts +9 -0
- package/dist/handlers/tools/core.d.ts.map +1 -0
- package/dist/handlers/tools/core.js +326 -0
- package/dist/handlers/tools/core.js.map +1 -0
- package/dist/handlers/tools/export.d.ts +8 -0
- package/dist/handlers/tools/export.d.ts.map +1 -0
- package/dist/handlers/tools/export.js +89 -0
- package/dist/handlers/tools/export.js.map +1 -0
- package/dist/handlers/tools/github/helpers.d.ts +34 -0
- package/dist/handlers/tools/github/helpers.d.ts.map +1 -0
- package/dist/handlers/tools/github/helpers.js +52 -0
- package/dist/handlers/tools/github/helpers.js.map +1 -0
- package/dist/handlers/tools/github/insights-tools.d.ts +8 -0
- package/dist/handlers/tools/github/insights-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/insights-tools.js +104 -0
- package/dist/handlers/tools/github/insights-tools.js.map +1 -0
- package/dist/handlers/tools/github/issue-tools.d.ts +8 -0
- package/dist/handlers/tools/github/issue-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/issue-tools.js +359 -0
- package/dist/handlers/tools/github/issue-tools.js.map +1 -0
- package/dist/handlers/tools/github/kanban-tools.d.ts +8 -0
- package/dist/handlers/tools/github/kanban-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/kanban-tools.js +108 -0
- package/dist/handlers/tools/github/kanban-tools.js.map +1 -0
- package/dist/handlers/tools/github/milestone-tools.d.ts +9 -0
- package/dist/handlers/tools/github/milestone-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/milestone-tools.js +302 -0
- package/dist/handlers/tools/github/milestone-tools.js.map +1 -0
- package/dist/handlers/tools/github/mutation-tools.d.ts +12 -0
- package/dist/handlers/tools/github/mutation-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/mutation-tools.js +15 -0
- package/dist/handlers/tools/github/mutation-tools.js.map +1 -0
- package/dist/handlers/tools/github/read-tools.d.ts +8 -0
- package/dist/handlers/tools/github/read-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/read-tools.js +260 -0
- package/dist/handlers/tools/github/read-tools.js.map +1 -0
- package/dist/handlers/tools/github/schemas.d.ts +467 -0
- package/dist/handlers/tools/github/schemas.d.ts.map +1 -0
- package/dist/handlers/tools/github/schemas.js +335 -0
- package/dist/handlers/tools/github/schemas.js.map +1 -0
- package/dist/handlers/tools/github.d.ts +14 -0
- package/dist/handlers/tools/github.d.ts.map +1 -0
- package/dist/handlers/tools/github.js +28 -0
- package/dist/handlers/tools/github.js.map +1 -0
- package/dist/handlers/tools/index.d.ts +15 -20
- package/dist/handlers/tools/index.d.ts.map +1 -1
- package/dist/handlers/tools/index.js +117 -2909
- package/dist/handlers/tools/index.js.map +1 -1
- package/dist/handlers/tools/relationships.d.ts +8 -0
- package/dist/handlers/tools/relationships.d.ts.map +1 -0
- package/dist/handlers/tools/relationships.js +308 -0
- package/dist/handlers/tools/relationships.js.map +1 -0
- package/dist/handlers/tools/schemas.d.ts +108 -0
- package/dist/handlers/tools/schemas.d.ts.map +1 -0
- package/dist/handlers/tools/schemas.js +122 -0
- package/dist/handlers/tools/schemas.js.map +1 -0
- package/dist/handlers/tools/search.d.ts +8 -0
- package/dist/handlers/tools/search.d.ts.map +1 -0
- package/dist/handlers/tools/search.js +282 -0
- package/dist/handlers/tools/search.js.map +1 -0
- package/dist/handlers/tools/team.d.ts +11 -0
- package/dist/handlers/tools/team.d.ts.map +1 -0
- package/dist/handlers/tools/team.js +239 -0
- package/dist/handlers/tools/team.js.map +1 -0
- package/dist/server/McpServer.d.ts +4 -0
- package/dist/server/McpServer.d.ts.map +1 -1
- package/dist/server/McpServer.js +48 -297
- package/dist/server/McpServer.js.map +1 -1
- package/dist/server/Scheduler.d.ts +91 -0
- package/dist/server/Scheduler.d.ts.map +1 -0
- package/dist/server/Scheduler.js +201 -0
- package/dist/server/Scheduler.js.map +1 -0
- package/dist/transports/http.d.ts +66 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +519 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/types/entities.d.ts +101 -0
- package/dist/types/entities.d.ts.map +1 -0
- package/dist/types/entities.js +5 -0
- package/dist/types/entities.js.map +1 -0
- package/dist/types/filtering.d.ts +34 -0
- package/dist/types/filtering.d.ts.map +1 -0
- package/dist/types/filtering.js +5 -0
- package/dist/types/filtering.js.map +1 -0
- package/dist/types/github.d.ts +166 -0
- package/dist/types/github.d.ts.map +1 -0
- package/dist/types/github.js +5 -0
- package/dist/types/github.js.map +1 -0
- package/dist/types/index.d.ts +35 -292
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error-helpers.d.ts +37 -0
- package/dist/utils/error-helpers.d.ts.map +1 -0
- package/dist/utils/error-helpers.js +47 -0
- package/dist/utils/error-helpers.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +6 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/security-utils.d.ts +0 -21
- package/dist/utils/security-utils.d.ts.map +1 -1
- package/dist/utils/security-utils.js +0 -47
- package/dist/utils/security-utils.js.map +1 -1
- package/dist/vector/VectorSearchManager.d.ts.map +1 -1
- package/dist/vector/VectorSearchManager.js +9 -32
- package/dist/vector/VectorSearchManager.js.map +1 -1
- package/docker-compose.yml +11 -2
- package/hooks/README.md +107 -0
- package/hooks/cursor/hooks.json +10 -0
- package/hooks/cursor/memory-journal.mdc +22 -0
- package/hooks/cursor/session-end.sh +19 -0
- package/hooks/kilo-code/session-end-mode.json +11 -0
- package/hooks/kiro/session-end.md +13 -0
- package/mcp-config-example.json +1 -0
- package/package.json +11 -9
- package/playwright.config.ts +29 -0
- package/releases/v4.5.0.md +116 -0
- package/releases/v5.0.0.md +105 -0
- package/scripts/generate-server-instructions.ts +176 -0
- package/scripts/server-instructions-function-body.ts +77 -0
- package/server.json +3 -3
- package/src/cli.ts +45 -1
- package/src/constants/ServerInstructions.ts +133 -73
- package/src/constants/icons.ts +8 -7
- package/src/constants/server-instructions.md +268 -0
- package/src/database/SqliteAdapter.ts +358 -192
- package/src/database/schema.ts +125 -0
- package/src/filtering/ToolFilter.ts +13 -2
- package/src/github/GitHubIntegration.ts +1 -3
- package/src/handlers/prompts/github.ts +209 -0
- package/src/handlers/prompts/index.ts +10 -499
- package/src/handlers/prompts/workflow.ts +314 -0
- package/src/handlers/resources/core.ts +528 -0
- package/src/handlers/resources/github.ts +358 -0
- package/src/handlers/resources/graph.ts +254 -0
- package/src/handlers/resources/index.ts +23 -1570
- package/src/handlers/resources/shared.ts +103 -0
- package/src/handlers/resources/team.ts +133 -0
- package/src/handlers/resources/templates.ts +374 -0
- package/src/handlers/tools/admin.ts +285 -0
- package/src/handlers/tools/analytics.ts +301 -0
- package/src/handlers/tools/backup.ts +242 -0
- package/src/handlers/tools/core.ts +350 -0
- package/src/handlers/tools/export.ts +115 -0
- package/src/handlers/tools/github/helpers.ts +86 -0
- package/src/handlers/tools/github/insights-tools.ts +119 -0
- package/src/handlers/tools/github/issue-tools.ts +439 -0
- package/src/handlers/tools/github/kanban-tools.ts +134 -0
- package/src/handlers/tools/github/milestone-tools.ts +392 -0
- package/src/handlers/tools/github/mutation-tools.ts +17 -0
- package/src/handlers/tools/github/read-tools.ts +328 -0
- package/src/handlers/tools/github/schemas.ts +369 -0
- package/src/handlers/tools/github.ts +36 -0
- package/src/handlers/tools/index.ts +144 -3325
- package/src/handlers/tools/relationships.ts +358 -0
- package/src/handlers/tools/schemas.ts +132 -0
- package/src/handlers/tools/search.ts +343 -0
- package/src/handlers/tools/team.ts +273 -0
- package/src/server/McpServer.ts +63 -358
- package/src/server/Scheduler.ts +278 -0
- package/src/transports/http.ts +635 -0
- package/src/types/entities.ts +145 -0
- package/src/types/filtering.ts +54 -0
- package/src/types/github.ts +180 -0
- package/src/types/index.ts +67 -375
- package/src/utils/error-helpers.ts +52 -0
- package/src/utils/logger.ts +6 -3
- package/src/utils/security-utils.ts +0 -52
- package/src/vector/VectorSearchManager.ts +9 -33
- package/tests/constants/icons.test.ts +1 -2
- package/tests/constants/server-instructions.test.ts +30 -4
- package/tests/database/sqlite-adapter.test.ts +91 -7
- package/tests/e2e/auth.spec.ts +154 -0
- package/tests/e2e/health.spec.ts +63 -0
- package/tests/e2e/protocols.spec.ts +134 -0
- package/tests/e2e/resources.spec.ts +103 -0
- package/tests/e2e/scheduler.spec.ts +79 -0
- package/tests/e2e/security.spec.ts +91 -0
- package/tests/e2e/sessions.spec.ts +95 -0
- package/tests/e2e/stateless.spec.ts +121 -0
- package/tests/e2e/tools.spec.ts +111 -0
- package/tests/filtering/tool-filter.test.ts +46 -0
- package/tests/handlers/error-path-coverage.test.ts +324 -0
- package/tests/handlers/github-resource-handlers.test.ts +453 -0
- package/tests/handlers/github-tool-handlers.test.ts +899 -0
- package/tests/handlers/prompt-handler-coverage.test.ts +106 -0
- package/tests/handlers/prompt-handlers.test.ts +40 -0
- package/tests/handlers/resource-handler-coverage.test.ts +181 -0
- package/tests/handlers/resource-handlers.test.ts +33 -9
- package/tests/handlers/search-tool-handlers.test.ts +272 -0
- package/tests/handlers/targeted-gap-closure.test.ts +387 -0
- package/tests/handlers/team-resource-handlers.test.ts +156 -0
- package/tests/handlers/team-tool-handlers.test.ts +301 -0
- package/tests/handlers/tool-handler-coverage.test.ts +469 -0
- package/tests/handlers/tool-handlers.test.ts +2 -2
- package/tests/security/sql-injection.test.ts +3 -54
- package/tests/server/mcp-server.test.ts +503 -8
- package/tests/server/scheduler.test.ts +400 -0
- package/tests/transports/http-transport.test.ts +620 -0
- package/tests/vector/vector-search-manager.test.ts +60 -0
- package/vitest.config.ts +4 -1
- package/.memory-journal-team.db +0 -0
- package/.vscode/settings.json +0 -84
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Handler Coverage Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for handlers/prompts/index.ts uncovered paths:
|
|
5
|
+
* - execQuery with empty result set
|
|
6
|
+
* - execQuery with multi-row results
|
|
7
|
+
* - getPrompt with unknown name (throws)
|
|
8
|
+
* - getPrompts listing
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
12
|
+
import { SqliteAdapter } from '../../src/database/SqliteAdapter.js'
|
|
13
|
+
import { execQuery, getPrompts, getPrompt } from '../../src/handlers/prompts/index.js'
|
|
14
|
+
|
|
15
|
+
describe('Prompt Handlers - Coverage', () => {
|
|
16
|
+
let db: SqliteAdapter
|
|
17
|
+
const testDbPath = './test-prompts-cov.db'
|
|
18
|
+
|
|
19
|
+
beforeAll(async () => {
|
|
20
|
+
db = new SqliteAdapter(testDbPath)
|
|
21
|
+
await db.initialize()
|
|
22
|
+
// Seed entries for query tests
|
|
23
|
+
db.createEntry({ content: 'Prompt test entry 1', tags: ['prompt-test'] })
|
|
24
|
+
db.createEntry({ content: 'Prompt test entry 2', tags: ['prompt-test'] })
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
afterAll(() => {
|
|
28
|
+
db.close()
|
|
29
|
+
try {
|
|
30
|
+
const fs = require('node:fs')
|
|
31
|
+
if (fs.existsSync(testDbPath)) fs.unlinkSync(testDbPath)
|
|
32
|
+
} catch {
|
|
33
|
+
// Ignore cleanup errors
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// ========================================================================
|
|
38
|
+
// execQuery
|
|
39
|
+
// ========================================================================
|
|
40
|
+
|
|
41
|
+
describe('execQuery', () => {
|
|
42
|
+
it('should return rows for a valid query', () => {
|
|
43
|
+
const rows = execQuery(db, 'SELECT id, content FROM memory_journal LIMIT 2')
|
|
44
|
+
expect(rows.length).toBe(2)
|
|
45
|
+
expect(rows[0]).toHaveProperty('id')
|
|
46
|
+
expect(rows[0]).toHaveProperty('content')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should return empty array for no-match query', () => {
|
|
50
|
+
const rows = execQuery(
|
|
51
|
+
db,
|
|
52
|
+
"SELECT id FROM memory_journal WHERE content = 'nonexistent_xyz_12345'"
|
|
53
|
+
)
|
|
54
|
+
expect(rows).toEqual([])
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should handle parameterized queries', () => {
|
|
58
|
+
const rows = execQuery(
|
|
59
|
+
db,
|
|
60
|
+
'SELECT id, content FROM memory_journal WHERE content LIKE ?',
|
|
61
|
+
['%Prompt test%']
|
|
62
|
+
)
|
|
63
|
+
expect(rows.length).toBe(2)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should return empty array for query that returns no columns', () => {
|
|
67
|
+
const rows = execQuery(db, 'SELECT id FROM memory_journal WHERE id = -1')
|
|
68
|
+
expect(rows).toEqual([])
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// ========================================================================
|
|
73
|
+
// getPrompts
|
|
74
|
+
// ========================================================================
|
|
75
|
+
|
|
76
|
+
describe('getPrompts', () => {
|
|
77
|
+
it('should return all prompt definitions', () => {
|
|
78
|
+
const prompts = getPrompts()
|
|
79
|
+
expect(prompts.length).toBeGreaterThan(0)
|
|
80
|
+
|
|
81
|
+
for (const p of prompts) {
|
|
82
|
+
const prompt = p as { name: string; description: string }
|
|
83
|
+
expect(typeof prompt.name).toBe('string')
|
|
84
|
+
expect(typeof prompt.description).toBe('string')
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// ========================================================================
|
|
90
|
+
// getPrompt
|
|
91
|
+
// ========================================================================
|
|
92
|
+
|
|
93
|
+
describe('getPrompt', () => {
|
|
94
|
+
it('should throw for unknown prompt name', () => {
|
|
95
|
+
expect(() => getPrompt('nonexistent_prompt', {}, db)).toThrow('Unknown prompt')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should return messages for a valid prompt', () => {
|
|
99
|
+
const prompts = getPrompts()
|
|
100
|
+
const firstName = (prompts[0] as { name: string }).name
|
|
101
|
+
const result = getPrompt(firstName, {}, db)
|
|
102
|
+
expect(result.messages).toBeDefined()
|
|
103
|
+
expect(result.messages.length).toBeGreaterThan(0)
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
})
|
|
@@ -87,5 +87,45 @@ describe('Prompt Handlers', () => {
|
|
|
87
87
|
it('should throw for unknown prompt name', () => {
|
|
88
88
|
expect(() => getPrompt('nonexistent_prompt_xyz', {}, db)).toThrow()
|
|
89
89
|
})
|
|
90
|
+
|
|
91
|
+
it('should return messages for analyze-period with date arguments', () => {
|
|
92
|
+
const today = new Date().toISOString().split('T')[0]!
|
|
93
|
+
const result = getPrompt('analyze-period', { start_date: today, end_date: today }, db)
|
|
94
|
+
|
|
95
|
+
expect(result.messages).toBeDefined()
|
|
96
|
+
expect(result.messages.length).toBeGreaterThan(0)
|
|
97
|
+
const text = (result.messages[0]?.content as { text: string }).text
|
|
98
|
+
expect(text).toContain(today)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should return messages for find-related with query argument', () => {
|
|
102
|
+
const result = getPrompt('find-related', { query: 'test' }, db)
|
|
103
|
+
|
|
104
|
+
expect(result.messages).toBeDefined()
|
|
105
|
+
expect(result.messages.length).toBeGreaterThan(0)
|
|
106
|
+
const text = (result.messages[0]?.content as { text: string }).text
|
|
107
|
+
expect(text).toContain('test')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should return messages for get-context-bundle prompt', () => {
|
|
111
|
+
const result = getPrompt('get-context-bundle', {}, db)
|
|
112
|
+
|
|
113
|
+
expect(result.messages).toBeDefined()
|
|
114
|
+
expect(result.messages.length).toBeGreaterThan(0)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('should return messages for goal-tracker prompt', () => {
|
|
118
|
+
const result = getPrompt('goal-tracker', {}, db)
|
|
119
|
+
|
|
120
|
+
expect(result.messages).toBeDefined()
|
|
121
|
+
expect(result.messages.length).toBeGreaterThan(0)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should return messages for prepare-retro with days argument', () => {
|
|
125
|
+
const result = getPrompt('prepare-retro', { days: '14' }, db)
|
|
126
|
+
|
|
127
|
+
expect(result.messages).toBeDefined()
|
|
128
|
+
expect(result.messages.length).toBeGreaterThan(0)
|
|
129
|
+
})
|
|
90
130
|
})
|
|
91
131
|
})
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource Handler Coverage Tests — Targeted Gap Closure
|
|
3
|
+
*
|
|
4
|
+
* Tests for remaining uncovered lines in:
|
|
5
|
+
* - handlers/resources/index.ts: getBaseUri non-memory URL, template match + ResourceResult
|
|
6
|
+
* - handlers/resources/templates.ts: invalid params for template resources
|
|
7
|
+
* - handlers/resources/core.ts: CI status branches, scheduler section
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
11
|
+
import { readResource } from '../../src/handlers/resources/index.js'
|
|
12
|
+
import { SqliteAdapter } from '../../src/database/SqliteAdapter.js'
|
|
13
|
+
|
|
14
|
+
describe('Resource Handler Coverage', () => {
|
|
15
|
+
let db: SqliteAdapter
|
|
16
|
+
const testDbPath = './test-resource-coverage.db'
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
db = new SqliteAdapter(testDbPath)
|
|
20
|
+
await db.initialize()
|
|
21
|
+
|
|
22
|
+
// Create entries with project/issue/PR links for template tests
|
|
23
|
+
db.createEntry({
|
|
24
|
+
content: 'Resource coverage test entry',
|
|
25
|
+
tags: ['resource-test'],
|
|
26
|
+
projectNumber: 42,
|
|
27
|
+
issueNumber: 7,
|
|
28
|
+
prNumber: 3,
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
afterAll(() => {
|
|
33
|
+
db.close()
|
|
34
|
+
try {
|
|
35
|
+
const fs = require('node:fs')
|
|
36
|
+
if (fs.existsSync(testDbPath)) fs.unlinkSync(testDbPath)
|
|
37
|
+
} catch {
|
|
38
|
+
// cleanup
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// ========================================================================
|
|
43
|
+
// handlers/resources/index.ts — getBaseUri, template matching
|
|
44
|
+
// ========================================================================
|
|
45
|
+
|
|
46
|
+
describe('readResource - base URI parsing', () => {
|
|
47
|
+
it('should handle memory:// URIs with query parameters', async () => {
|
|
48
|
+
const result = await readResource('memory://recent?limit=5&format=brief', db)
|
|
49
|
+
expect(result.data).toBeDefined()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should throw for unknown resource URI', async () => {
|
|
53
|
+
await expect(readResource('memory://nonexistent-resource-xyz', db)).rejects.toThrow(
|
|
54
|
+
'Unknown resource'
|
|
55
|
+
)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('should throw for non-memory URI schemes', async () => {
|
|
59
|
+
await expect(readResource('https://example.com/unknown', db)).rejects.toThrow(
|
|
60
|
+
'Unknown resource'
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// ========================================================================
|
|
66
|
+
// handlers/resources/templates.ts — invalid template params (lines 33, 67, 100)
|
|
67
|
+
// ========================================================================
|
|
68
|
+
|
|
69
|
+
describe('template resources — invalid params', () => {
|
|
70
|
+
it('should return error for invalid project timeline number', async () => {
|
|
71
|
+
const result = await readResource('memory://projects/abc/timeline', db)
|
|
72
|
+
expect(result.data).toHaveProperty('error')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should return entries for valid project timeline', async () => {
|
|
76
|
+
const result = await readResource('memory://projects/42/timeline', db)
|
|
77
|
+
const data = result.data as { projectNumber: number; entries: unknown[] }
|
|
78
|
+
expect(data.projectNumber).toBe(42)
|
|
79
|
+
expect(data.entries).toBeDefined()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should return error for invalid issue entries number', async () => {
|
|
83
|
+
const result = await readResource('memory://issues/abc/entries', db)
|
|
84
|
+
expect(result.data).toHaveProperty('error')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('should return entries for valid issue entries', async () => {
|
|
88
|
+
const result = await readResource('memory://issues/7/entries', db)
|
|
89
|
+
const data = result.data as { issueNumber: number; entries: unknown[] }
|
|
90
|
+
expect(data.issueNumber).toBe(7)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should return error for invalid PR entries number', async () => {
|
|
94
|
+
const result = await readResource('memory://prs/abc/entries', db)
|
|
95
|
+
expect(result.data).toHaveProperty('error')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should return entries for valid PR entries', async () => {
|
|
99
|
+
const result = await readResource('memory://prs/3/entries', db)
|
|
100
|
+
const data = result.data as { prNumber: number; entries: unknown[] }
|
|
101
|
+
expect(data.prNumber).toBe(3)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('should return error for invalid PR timeline number', async () => {
|
|
105
|
+
const result = await readResource('memory://prs/abc/timeline', db)
|
|
106
|
+
expect(result.data).toHaveProperty('error')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should return timeline without GitHub for valid PR', async () => {
|
|
110
|
+
const result = await readResource('memory://prs/3/timeline', db)
|
|
111
|
+
const data = result.data as {
|
|
112
|
+
prNumber: number
|
|
113
|
+
prMetadata: null
|
|
114
|
+
timelineNote: string
|
|
115
|
+
}
|
|
116
|
+
expect(data.prNumber).toBe(3)
|
|
117
|
+
expect(data.prMetadata).toBeNull()
|
|
118
|
+
expect(data.timelineNote).toContain('unavailable')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should return error for invalid kanban project number', async () => {
|
|
122
|
+
const result = await readResource('memory://kanban/abc', db)
|
|
123
|
+
expect(result.data).toHaveProperty('error')
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('should return error for kanban without github', async () => {
|
|
127
|
+
const result = await readResource('memory://kanban/1', db)
|
|
128
|
+
const data = result.data as { error: string }
|
|
129
|
+
expect(data.error).toContain('not available')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should return error for invalid kanban diagram number', async () => {
|
|
133
|
+
const result = await readResource('memory://kanban/abc/diagram', db)
|
|
134
|
+
expect(result.data).toHaveProperty('error')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('should return mermaid fallback for diagram without github', async () => {
|
|
138
|
+
const result = await readResource('memory://kanban/1/diagram', db)
|
|
139
|
+
const data = result.data as { format: string; diagram: string }
|
|
140
|
+
expect(data.format).toBe('mermaid')
|
|
141
|
+
expect(data.diagram).toContain('not available')
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Note: health endpoint is served by HttpTransport, not as a memory:// resource.
|
|
146
|
+
|
|
147
|
+
// ========================================================================
|
|
148
|
+
// handlers/resources/core.ts — instructions resource
|
|
149
|
+
// ========================================================================
|
|
150
|
+
|
|
151
|
+
describe('core resources — instructions', () => {
|
|
152
|
+
it('should return instructions text', async () => {
|
|
153
|
+
const result = await readResource('memory://instructions', db)
|
|
154
|
+
expect(result.data).toBeDefined()
|
|
155
|
+
expect(typeof result.data).toBe('string')
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// ========================================================================
|
|
160
|
+
// handlers/resources/core.ts — briefing resource
|
|
161
|
+
// ========================================================================
|
|
162
|
+
|
|
163
|
+
describe('core resources — briefing', () => {
|
|
164
|
+
it('should return briefing data', async () => {
|
|
165
|
+
const result = await readResource('memory://briefing', db)
|
|
166
|
+
expect(result.data).toBeDefined()
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// ========================================================================
|
|
171
|
+
// handlers/resources/core.ts — recent with entry_type filter
|
|
172
|
+
// ========================================================================
|
|
173
|
+
|
|
174
|
+
describe('core resources — recent with filters', () => {
|
|
175
|
+
it('should return recent entries with query params', async () => {
|
|
176
|
+
const result = await readResource('memory://recent?limit=3', db)
|
|
177
|
+
const data = result.data as { entries: unknown[] }
|
|
178
|
+
expect(data.entries).toBeDefined()
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
})
|
|
@@ -76,7 +76,6 @@ describe('Resource Handlers', () => {
|
|
|
76
76
|
expect(uris).toContain('memory://recent')
|
|
77
77
|
expect(uris).toContain('memory://significant')
|
|
78
78
|
expect(uris).toContain('memory://tags')
|
|
79
|
-
expect(uris).toContain('memory://team/recent')
|
|
80
79
|
})
|
|
81
80
|
|
|
82
81
|
it('should include template resources', () => {
|
|
@@ -122,7 +121,7 @@ describe('Resource Handlers', () => {
|
|
|
122
121
|
const result = await readResource('memory://instructions', db, undefined, null)
|
|
123
122
|
|
|
124
123
|
const text = result.data as string
|
|
125
|
-
expect(text).toContain('Active Tools (
|
|
124
|
+
expect(text).toContain('Active Tools (42)')
|
|
126
125
|
})
|
|
127
126
|
|
|
128
127
|
it('should read memory://recent', async () => {
|
|
@@ -203,13 +202,6 @@ describe('Resource Handlers', () => {
|
|
|
203
202
|
expect(data.timestamp).toBeDefined()
|
|
204
203
|
})
|
|
205
204
|
|
|
206
|
-
it('should read memory://team/recent', async () => {
|
|
207
|
-
const result = await readResource('memory://team/recent', db)
|
|
208
|
-
|
|
209
|
-
const data = result.data as { entries: unknown[]; count: number }
|
|
210
|
-
expect(data.entries).toBeDefined()
|
|
211
|
-
})
|
|
212
|
-
|
|
213
205
|
it('should read memory://graph/recent', async () => {
|
|
214
206
|
const result = await readResource('memory://graph/recent', db)
|
|
215
207
|
|
|
@@ -335,5 +327,37 @@ describe('Resource Handlers', () => {
|
|
|
335
327
|
const data = result.data as { prNumber: number }
|
|
336
328
|
expect(data.prNumber).toBe(15)
|
|
337
329
|
})
|
|
330
|
+
|
|
331
|
+
it('should handle resource URI with query parameters', async () => {
|
|
332
|
+
// Query params should be stripped for matching but the full URI is passed to the handler
|
|
333
|
+
const result = await readResource('memory://recent?limit=5', db)
|
|
334
|
+
const data = result.data as { entries: unknown[]; count: number }
|
|
335
|
+
expect(data.entries).toBeDefined()
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
it('should handle resource URI with hash fragment', async () => {
|
|
339
|
+
const result = await readResource('memory://recent#section', db)
|
|
340
|
+
const data = result.data as { entries: unknown[]; count: number }
|
|
341
|
+
expect(data.entries).toBeDefined()
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it('should handle template URI with query parameters', async () => {
|
|
345
|
+
const result = await readResource('memory://projects/42/timeline?sort=asc', db)
|
|
346
|
+
const data = result.data as { projectNumber: number }
|
|
347
|
+
expect(data.projectNumber).toBe(42)
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
it('should return no-github diagram for kanban without integration', async () => {
|
|
351
|
+
const result = await readResource(
|
|
352
|
+
'memory://kanban/1/diagram',
|
|
353
|
+
db,
|
|
354
|
+
undefined,
|
|
355
|
+
undefined,
|
|
356
|
+
null // no github
|
|
357
|
+
)
|
|
358
|
+
const data = result.data as { format: string; diagram: string; message: string }
|
|
359
|
+
expect(data.format).toBe('mermaid')
|
|
360
|
+
expect(data.diagram).toContain('GitHub integration not available')
|
|
361
|
+
})
|
|
338
362
|
})
|
|
339
363
|
})
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Tool Handler Coverage Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for search tool uncovered code paths:
|
|
5
|
+
* - search_entries with teamDb (cross-database merge)
|
|
6
|
+
* - search_by_date_range with teamDb
|
|
7
|
+
* - search_entries with GitHub filters
|
|
8
|
+
* - search_entries with no query and no filters (recent entries path)
|
|
9
|
+
* - search_by_date_range with entry_type filter
|
|
10
|
+
* - mergeAndDedup helper (dedup, sort, limit)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
14
|
+
import { callTool } from '../../src/handlers/tools/index.js'
|
|
15
|
+
import { SqliteAdapter } from '../../src/database/SqliteAdapter.js'
|
|
16
|
+
|
|
17
|
+
describe('Search Tool Handlers - Coverage', () => {
|
|
18
|
+
let db: SqliteAdapter
|
|
19
|
+
let teamDb: SqliteAdapter
|
|
20
|
+
const testDbPath = './test-search-cov.db'
|
|
21
|
+
const teamDbPath = './test-search-team-cov.db'
|
|
22
|
+
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
db = new SqliteAdapter(testDbPath)
|
|
25
|
+
await db.initialize()
|
|
26
|
+
teamDb = new SqliteAdapter(teamDbPath)
|
|
27
|
+
await teamDb.initialize()
|
|
28
|
+
|
|
29
|
+
// Seed personal entries
|
|
30
|
+
db.createEntry({
|
|
31
|
+
content: 'Personal alpha entry',
|
|
32
|
+
entryType: 'personal_reflection',
|
|
33
|
+
tags: ['alpha'],
|
|
34
|
+
projectNumber: 42,
|
|
35
|
+
issueNumber: 7,
|
|
36
|
+
prNumber: 10,
|
|
37
|
+
})
|
|
38
|
+
db.createEntry({
|
|
39
|
+
content: 'Personal beta entry',
|
|
40
|
+
entryType: 'project_decision',
|
|
41
|
+
tags: ['beta'],
|
|
42
|
+
isPersonal: true,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Seed team entries
|
|
46
|
+
teamDb.createEntry({
|
|
47
|
+
content: 'Team gamma entry',
|
|
48
|
+
entryType: 'personal_reflection',
|
|
49
|
+
tags: ['gamma'],
|
|
50
|
+
projectNumber: 42,
|
|
51
|
+
})
|
|
52
|
+
teamDb.createEntry({
|
|
53
|
+
content: 'Team delta entry',
|
|
54
|
+
entryType: 'project_decision',
|
|
55
|
+
tags: ['delta'],
|
|
56
|
+
})
|
|
57
|
+
// Duplicate content to test dedup
|
|
58
|
+
teamDb.createEntry({
|
|
59
|
+
content: 'Personal alpha entry',
|
|
60
|
+
entryType: 'personal_reflection',
|
|
61
|
+
tags: ['dup'],
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
afterAll(() => {
|
|
66
|
+
db.close()
|
|
67
|
+
teamDb.close()
|
|
68
|
+
try {
|
|
69
|
+
const fs = require('node:fs')
|
|
70
|
+
for (const p of [testDbPath, teamDbPath]) {
|
|
71
|
+
if (fs.existsSync(p)) fs.unlinkSync(p)
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// Ignore cleanup errors
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// ========================================================================
|
|
79
|
+
// search_entries — cross-database merge
|
|
80
|
+
// ========================================================================
|
|
81
|
+
|
|
82
|
+
describe('search_entries with teamDb', () => {
|
|
83
|
+
it('should merge personal and team results', async () => {
|
|
84
|
+
const result = (await callTool(
|
|
85
|
+
'search_entries',
|
|
86
|
+
{ query: 'entry', limit: 10 },
|
|
87
|
+
db,
|
|
88
|
+
undefined,
|
|
89
|
+
undefined,
|
|
90
|
+
undefined,
|
|
91
|
+
undefined,
|
|
92
|
+
teamDb
|
|
93
|
+
)) as { entries: unknown[]; count: number }
|
|
94
|
+
|
|
95
|
+
// Should have entries from both DBs (deduped)
|
|
96
|
+
expect(result.count).toBeGreaterThan(0)
|
|
97
|
+
expect(result.entries.length).toBe(result.count)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should deduplicate entries with same content', async () => {
|
|
101
|
+
const result = (await callTool(
|
|
102
|
+
'search_entries',
|
|
103
|
+
{ query: 'alpha', limit: 50 },
|
|
104
|
+
db,
|
|
105
|
+
undefined,
|
|
106
|
+
undefined,
|
|
107
|
+
undefined,
|
|
108
|
+
undefined,
|
|
109
|
+
teamDb
|
|
110
|
+
)) as { entries: { content: string }[]; count: number }
|
|
111
|
+
|
|
112
|
+
// "Personal alpha entry" exists in both DBs — should be deduped
|
|
113
|
+
const alphaEntries = result.entries.filter((e) =>
|
|
114
|
+
e.content.includes('Personal alpha entry')
|
|
115
|
+
)
|
|
116
|
+
expect(alphaEntries.length).toBe(1)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should merge recent entries (no query, no filters)', async () => {
|
|
120
|
+
const result = (await callTool(
|
|
121
|
+
'search_entries',
|
|
122
|
+
{ limit: 10 },
|
|
123
|
+
db,
|
|
124
|
+
undefined,
|
|
125
|
+
undefined,
|
|
126
|
+
undefined,
|
|
127
|
+
undefined,
|
|
128
|
+
teamDb
|
|
129
|
+
)) as { entries: unknown[]; count: number }
|
|
130
|
+
|
|
131
|
+
expect(result.count).toBeGreaterThan(0)
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// ========================================================================
|
|
136
|
+
// search_entries — GitHub filters
|
|
137
|
+
// ========================================================================
|
|
138
|
+
|
|
139
|
+
describe('search_entries with filters', () => {
|
|
140
|
+
it('should filter by project_number', async () => {
|
|
141
|
+
const result = (await callTool(
|
|
142
|
+
'search_entries',
|
|
143
|
+
{ project_number: 42, limit: 10 },
|
|
144
|
+
db
|
|
145
|
+
)) as { entries: unknown[]; count: number }
|
|
146
|
+
|
|
147
|
+
expect(result.count).toBeGreaterThan(0)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('should filter by issue_number', async () => {
|
|
151
|
+
const result = (await callTool(
|
|
152
|
+
'search_entries',
|
|
153
|
+
{ issue_number: 7, limit: 10 },
|
|
154
|
+
db
|
|
155
|
+
)) as { entries: unknown[]; count: number }
|
|
156
|
+
|
|
157
|
+
expect(result.count).toBeGreaterThan(0)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should filter by pr_number', async () => {
|
|
161
|
+
const result = (await callTool('search_entries', { pr_number: 10, limit: 10 }, db)) as {
|
|
162
|
+
entries: unknown[]
|
|
163
|
+
count: number
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
expect(result.count).toBeGreaterThan(0)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should filter by is_personal', async () => {
|
|
170
|
+
const result = (await callTool(
|
|
171
|
+
'search_entries',
|
|
172
|
+
{ is_personal: true, limit: 10 },
|
|
173
|
+
db
|
|
174
|
+
)) as { entries: unknown[]; count: number }
|
|
175
|
+
|
|
176
|
+
expect(result.count).toBeGreaterThan(0)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('should handle combined filters with teamDb', async () => {
|
|
180
|
+
const result = (await callTool(
|
|
181
|
+
'search_entries',
|
|
182
|
+
{ project_number: 42, limit: 10 },
|
|
183
|
+
db,
|
|
184
|
+
undefined,
|
|
185
|
+
undefined,
|
|
186
|
+
undefined,
|
|
187
|
+
undefined,
|
|
188
|
+
teamDb
|
|
189
|
+
)) as { entries: unknown[]; count: number }
|
|
190
|
+
|
|
191
|
+
expect(result.count).toBeGreaterThan(0)
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
// ========================================================================
|
|
196
|
+
// search_by_date_range — cross-database merge
|
|
197
|
+
// ========================================================================
|
|
198
|
+
|
|
199
|
+
describe('search_by_date_range with teamDb', () => {
|
|
200
|
+
it('should merge personal and team results by date', async () => {
|
|
201
|
+
const today = new Date().toISOString().split('T')[0]!
|
|
202
|
+
const result = (await callTool(
|
|
203
|
+
'search_by_date_range',
|
|
204
|
+
{ start_date: today, end_date: today },
|
|
205
|
+
db,
|
|
206
|
+
undefined,
|
|
207
|
+
undefined,
|
|
208
|
+
undefined,
|
|
209
|
+
undefined,
|
|
210
|
+
teamDb
|
|
211
|
+
)) as { entries: unknown[]; count: number }
|
|
212
|
+
|
|
213
|
+
expect(result.count).toBeGreaterThan(0)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('should apply entry_type filter', async () => {
|
|
217
|
+
const today = new Date().toISOString().split('T')[0]!
|
|
218
|
+
const result = (await callTool(
|
|
219
|
+
'search_by_date_range',
|
|
220
|
+
{
|
|
221
|
+
start_date: today,
|
|
222
|
+
end_date: today,
|
|
223
|
+
entry_type: 'project_decision',
|
|
224
|
+
},
|
|
225
|
+
db
|
|
226
|
+
)) as { entries: unknown[]; count: number }
|
|
227
|
+
|
|
228
|
+
expect(result.count).toBeGreaterThan(0)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('should apply tags filter', async () => {
|
|
232
|
+
const today = new Date().toISOString().split('T')[0]!
|
|
233
|
+
const result = (await callTool(
|
|
234
|
+
'search_by_date_range',
|
|
235
|
+
{
|
|
236
|
+
start_date: today,
|
|
237
|
+
end_date: today,
|
|
238
|
+
tags: ['alpha'],
|
|
239
|
+
},
|
|
240
|
+
db
|
|
241
|
+
)) as { entries: unknown[]; count: number }
|
|
242
|
+
|
|
243
|
+
expect(result.count).toBeGreaterThan(0)
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('should return Zod error for invalid date format', async () => {
|
|
247
|
+
const result = (await callTool(
|
|
248
|
+
'search_by_date_range',
|
|
249
|
+
{ start_date: 'not-a-date', end_date: 'invalid' },
|
|
250
|
+
db
|
|
251
|
+
)) as { error: string }
|
|
252
|
+
|
|
253
|
+
expect(result.error).toBeDefined()
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
// ========================================================================
|
|
258
|
+
// search_entries — Zod error handling
|
|
259
|
+
// ========================================================================
|
|
260
|
+
|
|
261
|
+
describe('search_entries error handling', () => {
|
|
262
|
+
it('should return error for invalid pr_status enum', async () => {
|
|
263
|
+
const result = (await callTool(
|
|
264
|
+
'search_entries',
|
|
265
|
+
{ pr_status: 'invalid_status', limit: 5 },
|
|
266
|
+
db
|
|
267
|
+
)) as { error: string }
|
|
268
|
+
|
|
269
|
+
expect(result.error).toBeDefined()
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
})
|