memory-journal-mcp 6.1.2 → 6.2.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/README.md +44 -28
- package/dist/{chunk-X4SWFATC.js → chunk-BI4ZNSKA.js} +38 -24
- package/dist/{chunk-HCEWINSB.js → chunk-N6EBIDN7.js} +99 -102
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/tools-WPRY5MJ6.js +2 -0
- package/package.json +10 -1
- package/skills/github-commander/SKILL.md +151 -0
- package/skills/github-commander/config/project-config.example.md +125 -0
- package/skills/github-commander/workflows/code-quality-audit.md +80 -0
- package/skills/github-commander/workflows/full-audit.md +134 -0
- package/skills/github-commander/workflows/issue-triage.md +239 -0
- package/skills/github-commander/workflows/milestone-sprint.md +81 -0
- package/skills/github-commander/workflows/perf-audit.md +142 -0
- package/skills/github-commander/workflows/pr-review.md +123 -0
- package/skills/github-commander/workflows/security-audit.md +170 -0
- package/skills/github-commander/workflows/update-deps.md +109 -0
- package/.dockerignore +0 -139
- package/.gitattributes +0 -20
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -95
- package/.github/ISSUE_TEMPLATE/config.yml +0 -11
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -110
- package/.github/ISSUE_TEMPLATE/question.md +0 -78
- package/.github/aw/actions-lock.json +0 -14
- package/.github/copilot-instructions.md +0 -122
- package/.github/dependabot.yml +0 -93
- package/.github/pull_request_template.md +0 -135
- package/.github/workflows/README.md +0 -133
- package/.github/workflows/agentics-maintenance.yml +0 -141
- package/.github/workflows/auto-release.yml +0 -68
- package/.github/workflows/ci-health-monitor.lock.yml +0 -1121
- package/.github/workflows/ci-health-monitor.md +0 -87
- package/.github/workflows/codeql.yml +0 -41
- package/.github/workflows/dependabot-auto-merge.yml +0 -42
- package/.github/workflows/dependency-maintenance.lock.yml +0 -1182
- package/.github/workflows/dependency-maintenance.md +0 -147
- package/.github/workflows/docker-publish.yml +0 -254
- package/.github/workflows/docs-drift-detector.lock.yml +0 -1142
- package/.github/workflows/docs-drift-detector.md +0 -115
- package/.github/workflows/lint-and-test.yml +0 -60
- package/.github/workflows/publish-npm.yml +0 -85
- package/.github/workflows/secrets-scanning.yml +0 -32
- package/.github/workflows/security-update.yml +0 -127
- package/.gitleaks.toml +0 -9
- package/.prettierignore +0 -21
- package/.prettierrc +0 -33
- package/.scout-ignore +0 -12
- package/.trivyignore +0 -21
- package/CHANGELOG.md +0 -1814
- package/CODE_OF_CONDUCT.md +0 -133
- package/CONTRIBUTING.md +0 -263
- package/DOCKER_README.md +0 -331
- package/Dockerfile +0 -128
- package/SECURITY.md +0 -227
- package/UNRELEASED.md +0 -1
- package/dist/tools-T4U5A3X4.js +0 -2
- package/docker-compose.yml +0 -71
- package/docs/README.md +0 -18
- package/docs/agentic-journal-synergy.md +0 -175
- package/docs/copilot-setup.md +0 -72
- package/eslint.config.js +0 -110
- package/mcp-config-example.json +0 -21
- package/playwright.config.ts +0 -35
- package/releases/v2.1.0.md +0 -220
- package/releases/v2.2.0.md +0 -168
- package/releases/v3.0.0.md +0 -237
- package/releases/v3.1.0.md +0 -104
- package/releases/v3.1.1.md +0 -42
- package/releases/v3.1.2.md +0 -40
- package/releases/v3.1.3.md +0 -64
- package/releases/v3.1.4.md +0 -32
- package/releases/v3.1.5.md +0 -44
- package/releases/v4.0.0.md +0 -71
- package/releases/v4.1.0.md +0 -88
- package/releases/v4.2.0.md +0 -90
- package/releases/v4.3.0.md +0 -92
- package/releases/v4.3.1.md +0 -69
- package/releases/v4.4.0.md +0 -120
- package/releases/v4.4.1.md +0 -33
- package/releases/v4.4.2.md +0 -31
- package/releases/v4.5.0.md +0 -116
- package/releases/v5.0.0.md +0 -105
- package/releases/v5.0.1.md +0 -25
- package/releases/v5.1.0.md +0 -83
- package/releases/v5.1.1.md +0 -10
- package/releases/v6.0.0.md +0 -48
- package/releases/v6.0.1.md +0 -36
- package/releases/v6.1.0.md +0 -68
- package/releases/v6.1.1.md +0 -30
- package/releases/v6.1.2.md +0 -23
- package/scripts/generate-server-instructions.ts +0 -306
- package/scripts/server-instructions-function-body.ts +0 -107
- package/scripts/server-instructions-gotchas.ts +0 -45
- package/server.json +0 -42
- package/social-preview.png +0 -0
- package/src/auth/auth-context.ts +0 -78
- package/src/auth/authorization-server-discovery.ts +0 -263
- package/src/auth/errors.ts +0 -215
- package/src/auth/index.ts +0 -58
- package/src/auth/middleware.ts +0 -392
- package/src/auth/oauth-resource-server.ts +0 -170
- package/src/auth/scope-map.ts +0 -46
- package/src/auth/scopes.ts +0 -256
- package/src/auth/token-validator.ts +0 -293
- package/src/auth/transport-agnostic.ts +0 -164
- package/src/auth/types.ts +0 -372
- package/src/cli.ts +0 -279
- package/src/codemode/api-constants.ts +0 -263
- package/src/codemode/api.ts +0 -302
- package/src/codemode/auto-return.ts +0 -65
- package/src/codemode/index.ts +0 -47
- package/src/codemode/sandbox-factory.ts +0 -144
- package/src/codemode/sandbox.ts +0 -220
- package/src/codemode/security.ts +0 -155
- package/src/codemode/types.ts +0 -228
- package/src/codemode/worker-sandbox.ts +0 -277
- package/src/codemode/worker-script.ts +0 -239
- package/src/constants/icons.ts +0 -183
- package/src/constants/server-instructions.md +0 -166
- package/src/constants/server-instructions.ts +0 -514
- package/src/database/adapter-factory.ts +0 -16
- package/src/database/core/entry-columns.ts +0 -10
- package/src/database/core/interfaces.ts +0 -188
- package/src/database/core/schema.ts +0 -152
- package/src/database/sqlite-adapter/backup.ts +0 -167
- package/src/database/sqlite-adapter/entries/crud.ts +0 -233
- package/src/database/sqlite-adapter/entries/importance.ts +0 -76
- package/src/database/sqlite-adapter/entries/index.ts +0 -142
- package/src/database/sqlite-adapter/entries/search.ts +0 -294
- package/src/database/sqlite-adapter/entries/shared.ts +0 -102
- package/src/database/sqlite-adapter/entries/statistics.ts +0 -162
- package/src/database/sqlite-adapter/index.ts +0 -265
- package/src/database/sqlite-adapter/native-connection.ts +0 -301
- package/src/database/sqlite-adapter/relationships.ts +0 -70
- package/src/database/sqlite-adapter/tags.ts +0 -182
- package/src/filtering/tool-filter.ts +0 -312
- package/src/github/github-integration/client.ts +0 -114
- package/src/github/github-integration/index.ts +0 -297
- package/src/github/github-integration/insights.ts +0 -155
- package/src/github/github-integration/issues.ts +0 -213
- package/src/github/github-integration/milestones.ts +0 -262
- package/src/github/github-integration/projects.ts +0 -414
- package/src/github/github-integration/pull-requests.ts +0 -235
- package/src/github/github-integration/repository.ts +0 -110
- package/src/github/github-integration/types.ts +0 -43
- package/src/handlers/prompts/github.ts +0 -210
- package/src/handlers/prompts/index.ts +0 -97
- package/src/handlers/prompts/workflow.ts +0 -361
- package/src/handlers/resources/core/briefing/context-section.ts +0 -182
- package/src/handlers/resources/core/briefing/github-section.ts +0 -354
- package/src/handlers/resources/core/briefing/index.ts +0 -106
- package/src/handlers/resources/core/briefing/user-message.ts +0 -114
- package/src/handlers/resources/core/health.ts +0 -75
- package/src/handlers/resources/core/index.ts +0 -31
- package/src/handlers/resources/core/instructions.ts +0 -45
- package/src/handlers/resources/core/utilities.ts +0 -310
- package/src/handlers/resources/github.ts +0 -340
- package/src/handlers/resources/graph.ts +0 -218
- package/src/handlers/resources/help.ts +0 -410
- package/src/handlers/resources/index.ts +0 -143
- package/src/handlers/resources/shared.ts +0 -219
- package/src/handlers/resources/team.ts +0 -134
- package/src/handlers/resources/templates.ts +0 -334
- package/src/handlers/tools/admin.ts +0 -351
- package/src/handlers/tools/analytics.ts +0 -346
- package/src/handlers/tools/backup.ts +0 -272
- package/src/handlers/tools/codemode.ts +0 -188
- package/src/handlers/tools/core.ts +0 -359
- package/src/handlers/tools/error-fields-mixin.ts +0 -10
- package/src/handlers/tools/export.ts +0 -150
- package/src/handlers/tools/github/copilot-tools.ts +0 -72
- package/src/handlers/tools/github/helpers.ts +0 -125
- package/src/handlers/tools/github/insights-tools.ts +0 -112
- package/src/handlers/tools/github/issue-tools.ts +0 -442
- package/src/handlers/tools/github/kanban-tools.ts +0 -153
- package/src/handlers/tools/github/milestone-tools.ts +0 -371
- package/src/handlers/tools/github/mutation-tools.ts +0 -17
- package/src/handlers/tools/github/read-tools.ts +0 -302
- package/src/handlers/tools/github/schemas.ts +0 -435
- package/src/handlers/tools/github.ts +0 -39
- package/src/handlers/tools/index.ts +0 -255
- package/src/handlers/tools/relationships.ts +0 -390
- package/src/handlers/tools/schemas.ts +0 -165
- package/src/handlers/tools/search.ts +0 -448
- package/src/handlers/tools/team/admin-tools.ts +0 -164
- package/src/handlers/tools/team/analytics-tools.ts +0 -233
- package/src/handlers/tools/team/backup-tools.ts +0 -83
- package/src/handlers/tools/team/core-tools.ts +0 -197
- package/src/handlers/tools/team/export-tools.ts +0 -130
- package/src/handlers/tools/team/helpers.ts +0 -66
- package/src/handlers/tools/team/index.ts +0 -45
- package/src/handlers/tools/team/relationship-tools.ts +0 -219
- package/src/handlers/tools/team/schemas.ts +0 -558
- package/src/handlers/tools/team/search-tools.ts +0 -145
- package/src/handlers/tools/team/vector-tools.ts +0 -261
- package/src/index.ts +0 -57
- package/src/server/mcp-server.ts +0 -446
- package/src/server/registration.ts +0 -141
- package/src/server/scheduler.ts +0 -283
- package/src/transports/http/handlers.ts +0 -78
- package/src/transports/http/index.ts +0 -8
- package/src/transports/http/security.ts +0 -147
- package/src/transports/http/server/index.ts +0 -397
- package/src/transports/http/server/legacy-sse.ts +0 -87
- package/src/transports/http/server/stateful.ts +0 -222
- package/src/transports/http/server/stateless.ts +0 -42
- package/src/transports/http/types.ts +0 -132
- package/src/types/entities.ts +0 -145
- package/src/types/error-types.ts +0 -92
- package/src/types/errors.ts +0 -200
- package/src/types/filtering.ts +0 -55
- package/src/types/github.ts +0 -216
- package/src/types/index.ts +0 -348
- package/src/utils/error-helpers.ts +0 -78
- package/src/utils/errors/error-response-fields.ts +0 -29
- package/src/utils/errors/suggestions.ts +0 -94
- package/src/utils/github-helpers.ts +0 -33
- package/src/utils/logger.ts +0 -107
- package/src/utils/mcp-logger.ts +0 -155
- package/src/utils/progress-utils.ts +0 -100
- package/src/utils/query-helpers.ts +0 -78
- package/src/utils/resource-annotations.ts +0 -75
- package/src/utils/security-utils.ts +0 -198
- package/src/utils/vector-index-helpers.ts +0 -24
- package/src/vector/vector-search-manager.ts +0 -409
- package/src/version.ts +0 -15
- package/test-server/README.md +0 -193
- package/test-server/code-map.md +0 -399
- package/test-server/test-agent-experience.md +0 -213
- package/test-server/test-filter-instructions.mjs +0 -295
- package/test-server/test-instruction-levels.mjs +0 -102
- package/test-server/test-preflight.md +0 -55
- package/test-server/test-prompts.mjs +0 -185
- package/test-server/test-scheduler.mjs +0 -174
- package/test-server/test-tool-annotations.mjs +0 -115
- package/test-server/test-tools-codemode.md +0 -632
- package/test-server/test-tools-codemode2.md +0 -1218
- package/test-server/test-tools-team.md +0 -215
- package/test-server/test-tools.md +0 -429
- package/test-server/test-tools2.md +0 -361
- package/test-server/test-tools3.md +0 -396
- package/test-server/tool-reference.md +0 -231
- package/tests/README.md +0 -54
- package/tests/auth/auth-context.test.ts +0 -162
- package/tests/auth/authorization-server-discovery.test.ts +0 -265
- package/tests/auth/errors.test.ts +0 -170
- package/tests/auth/middleware.test.ts +0 -585
- package/tests/auth/oauth-resource-server.test.ts +0 -173
- package/tests/auth/scope-map.test.ts +0 -66
- package/tests/auth/scopes.test.ts +0 -347
- package/tests/auth/token-validator.test.ts +0 -271
- package/tests/codemode/api.test.ts +0 -396
- package/tests/codemode/auto-return.test.ts +0 -167
- package/tests/codemode/codemode-tool-handlers.test.ts +0 -197
- package/tests/codemode/sandbox-factory.test.ts +0 -152
- package/tests/codemode/sandbox.test.ts +0 -190
- package/tests/codemode/security.test.ts +0 -242
- package/tests/codemode/worker-sandbox.test.ts +0 -106
- package/tests/constants/icons.test.ts +0 -101
- package/tests/constants/server-instructions.test.ts +0 -514
- package/tests/database/crud-workflow-branches.test.ts +0 -418
- package/tests/database/database-branches.test.ts +0 -132
- package/tests/database/entries-auth-branches.test.ts +0 -390
- package/tests/database/native-connection.test.ts +0 -249
- package/tests/database/shared-helpers.test.ts +0 -103
- package/tests/database/sqlite-adapter.bench.ts +0 -63
- package/tests/database/sqlite-adapter.test.ts +0 -690
- package/tests/database/tags.test.ts +0 -134
- package/tests/e2e/README.md +0 -39
- package/tests/e2e/auth.spec.ts +0 -106
- package/tests/e2e/codemode-abuse.spec.ts +0 -75
- package/tests/e2e/health.spec.ts +0 -63
- package/tests/e2e/helpers.ts +0 -139
- package/tests/e2e/oauth-discovery.spec.ts +0 -102
- package/tests/e2e/oauth-scopes.spec.ts +0 -222
- package/tests/e2e/payloads-admin.spec.ts +0 -76
- package/tests/e2e/payloads-analytics.spec.ts +0 -37
- package/tests/e2e/payloads-backup-restore.spec.ts +0 -102
- package/tests/e2e/payloads-backup.spec.ts +0 -44
- package/tests/e2e/payloads-codemode-api.spec.ts +0 -131
- package/tests/e2e/payloads-codemode-readonly.spec.ts +0 -116
- package/tests/e2e/payloads-codemode.spec.ts +0 -116
- package/tests/e2e/payloads-core.spec.ts +0 -82
- package/tests/e2e/payloads-error-contracts.spec.ts +0 -159
- package/tests/e2e/payloads-export.spec.ts +0 -46
- package/tests/e2e/payloads-github-degradation.spec.ts +0 -73
- package/tests/e2e/payloads-github.spec.ts +0 -176
- package/tests/e2e/payloads-relationships.spec.ts +0 -56
- package/tests/e2e/payloads-search.spec.ts +0 -64
- package/tests/e2e/payloads-team-happy.spec.ts +0 -231
- package/tests/e2e/payloads-team.spec.ts +0 -174
- package/tests/e2e/prompts-expanded.spec.ts +0 -137
- package/tests/e2e/prompts.spec.ts +0 -62
- package/tests/e2e/protocols.spec.ts +0 -134
- package/tests/e2e/rate-limiting.spec.ts +0 -291
- package/tests/e2e/resources-briefing-env.spec.ts +0 -106
- package/tests/e2e/resources-complete.spec.ts +0 -180
- package/tests/e2e/resources-expanded.spec.ts +0 -83
- package/tests/e2e/resources-instructions-levels.spec.ts +0 -145
- package/tests/e2e/resources-templates.spec.ts +0 -123
- package/tests/e2e/resources.spec.ts +0 -103
- package/tests/e2e/scheduler.spec.ts +0 -79
- package/tests/e2e/security.spec.ts +0 -112
- package/tests/e2e/session-advanced.spec.ts +0 -152
- package/tests/e2e/sessions.spec.ts +0 -95
- package/tests/e2e/stateless.spec.ts +0 -79
- package/tests/e2e/streaming.spec.ts +0 -176
- package/tests/e2e/tool-filtering-presets.spec.ts +0 -192
- package/tests/e2e/tool-filtering.spec.ts +0 -77
- package/tests/e2e/tools.spec.ts +0 -111
- package/tests/filtering/tool-filter.test.ts +0 -314
- package/tests/github/client-issues-errors.test.ts +0 -433
- package/tests/github/github-integration-branches.test.ts +0 -490
- package/tests/github/github-integration.test.ts +0 -1015
- package/tests/github/github-managers-branches.test.ts +0 -907
- package/tests/github/pull-requests.test.ts +0 -334
- package/tests/handlers/analytics-branches.test.ts +0 -222
- package/tests/handlers/backup-branches.test.ts +0 -270
- package/tests/handlers/briefing-context-section.test.ts +0 -388
- package/tests/handlers/briefing-github-section.test.ts +0 -392
- package/tests/handlers/briefing-user-message.test.ts +0 -405
- package/tests/handlers/codemode-tools.test.ts +0 -85
- package/tests/handlers/copilot-tools.test.ts +0 -126
- package/tests/handlers/error-path-coverage.test.ts +0 -324
- package/tests/handlers/export-tools.test.ts +0 -203
- package/tests/handlers/github-resource-handlers.test.ts +0 -929
- package/tests/handlers/github-tool-handlers.test.ts +0 -1452
- package/tests/handlers/handler-error-branches.test.ts +0 -346
- package/tests/handlers/help-resource.test.ts +0 -92
- package/tests/handlers/prompt-handler-coverage.test.ts +0 -108
- package/tests/handlers/prompt-handlers.test.ts +0 -131
- package/tests/handlers/resource-handler-coverage.test.ts +0 -281
- package/tests/handlers/resource-handlers.test.ts +0 -357
- package/tests/handlers/resource-prompt-branches.test.ts +0 -495
- package/tests/handlers/search-tool-handlers.test.ts +0 -379
- package/tests/handlers/targeted-gap-closure.test.ts +0 -387
- package/tests/handlers/team-admin.test.ts +0 -291
- package/tests/handlers/team-analytics.test.ts +0 -220
- package/tests/handlers/team-core.test.ts +0 -148
- package/tests/handlers/team-data.test.ts +0 -198
- package/tests/handlers/team-relationships.test.ts +0 -271
- package/tests/handlers/team-resource-handlers.test.ts +0 -161
- package/tests/handlers/team-search.test.ts +0 -134
- package/tests/handlers/team-tool-handlers.test.ts +0 -301
- package/tests/handlers/team-vector.test.ts +0 -213
- package/tests/handlers/template-github-branches.test.ts +0 -676
- package/tests/handlers/tool-annotations.test.ts +0 -90
- package/tests/handlers/tool-handler-coverage.test.ts +0 -514
- package/tests/handlers/tool-handlers.test.ts +0 -510
- package/tests/handlers/tool-output-schemas.test.ts +0 -116
- package/tests/handlers/vector-tool-handlers.test.ts +0 -238
- package/tests/security/sql-injection.test.ts +0 -284
- package/tests/server/mcp-server.bench.ts +0 -55
- package/tests/server/mcp-server.test.ts +0 -1326
- package/tests/server/scheduler.test.ts +0 -400
- package/tests/transports/http-legacy-sse.test.ts +0 -275
- package/tests/transports/http-security.test.ts +0 -322
- package/tests/transports/http-stateful.test.ts +0 -487
- package/tests/transports/http-transport-server.test.ts +0 -301
- package/tests/transports/http-transport.test.ts +0 -771
- package/tests/utils/github-helpers.test.ts +0 -58
- package/tests/utils/logger.test.ts +0 -180
- package/tests/utils/mcp-logger.test.ts +0 -211
- package/tests/utils/progress-utils.test.ts +0 -156
- package/tests/utils/query-helpers.test.ts +0 -80
- package/tests/utils/security-utils.test.ts +0 -82
- package/tests/vector/vector-search-branches.test.ts +0 -111
- package/tests/vector/vector-search-manager.test.ts +0 -375
- package/tests/vector/vector-search.bench.ts +0 -48
- package/tsconfig.json +0 -42
- package/tsup.config.ts +0 -19
- package/vitest.config.ts +0 -25
|
@@ -1,1452 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub Tool Handler Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests GitHub-dependent tools using a mock GitHubIntegration object
|
|
5
|
-
* passed to callTool().
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest'
|
|
9
|
-
import { callTool } from '../../src/handlers/tools/index.js'
|
|
10
|
-
import { DatabaseAdapter } from '../../src/database/sqlite-adapter/index.js'
|
|
11
|
-
import type { GitHubIntegration } from '../../src/github/github-integration.js'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Creates a mock GitHubIntegration with controllable method responses.
|
|
15
|
-
*/
|
|
16
|
-
function createMockGitHub(overrides: Partial<Record<string, unknown>> = {}): GitHubIntegration {
|
|
17
|
-
const defaults = {
|
|
18
|
-
isApiAvailable: vi.fn().mockReturnValue(true),
|
|
19
|
-
getRepoInfo: vi.fn().mockResolvedValue({
|
|
20
|
-
owner: 'testowner',
|
|
21
|
-
repo: 'testrepo',
|
|
22
|
-
branch: 'main',
|
|
23
|
-
remoteUrl: 'git@github.com:testowner/testrepo.git',
|
|
24
|
-
}),
|
|
25
|
-
getCachedRepoInfo: vi.fn().mockReturnValue({
|
|
26
|
-
owner: 'testowner',
|
|
27
|
-
repo: 'testrepo',
|
|
28
|
-
branch: 'main',
|
|
29
|
-
remoteUrl: 'git@github.com:testowner/testrepo.git',
|
|
30
|
-
}),
|
|
31
|
-
getRepoContext: vi.fn().mockResolvedValue({
|
|
32
|
-
repoName: 'testrepo',
|
|
33
|
-
branch: 'main',
|
|
34
|
-
commit: 'abc1234',
|
|
35
|
-
remoteUrl: 'url',
|
|
36
|
-
projects: [],
|
|
37
|
-
issues: [],
|
|
38
|
-
pullRequests: [],
|
|
39
|
-
workflowRuns: [],
|
|
40
|
-
milestones: [],
|
|
41
|
-
}),
|
|
42
|
-
getIssues: vi
|
|
43
|
-
.fn()
|
|
44
|
-
.mockResolvedValue([
|
|
45
|
-
{ number: 1, title: 'Test Issue', url: 'url1', state: 'OPEN', milestone: null },
|
|
46
|
-
]),
|
|
47
|
-
getIssue: vi.fn().mockResolvedValue({
|
|
48
|
-
number: 1,
|
|
49
|
-
title: 'Test Issue',
|
|
50
|
-
url: 'url1',
|
|
51
|
-
state: 'OPEN',
|
|
52
|
-
nodeId: 'NODE_1',
|
|
53
|
-
body: 'Issue body',
|
|
54
|
-
labels: ['bug'],
|
|
55
|
-
assignees: ['dev1'],
|
|
56
|
-
createdAt: '2025-01-01T00:00:00Z',
|
|
57
|
-
updatedAt: '2025-01-02T00:00:00Z',
|
|
58
|
-
closedAt: null,
|
|
59
|
-
commentsCount: 3,
|
|
60
|
-
milestone: null,
|
|
61
|
-
}),
|
|
62
|
-
createIssue: vi.fn().mockResolvedValue({
|
|
63
|
-
number: 42,
|
|
64
|
-
url: 'https://github.com/testowner/testrepo/issues/42',
|
|
65
|
-
title: 'New Issue',
|
|
66
|
-
nodeId: 'NODE_42',
|
|
67
|
-
}),
|
|
68
|
-
closeIssue: vi.fn().mockResolvedValue({
|
|
69
|
-
success: true,
|
|
70
|
-
url: 'https://github.com/testowner/testrepo/issues/1',
|
|
71
|
-
}),
|
|
72
|
-
getPullRequests: vi
|
|
73
|
-
.fn()
|
|
74
|
-
.mockResolvedValue([{ number: 10, title: 'Feature', url: 'url10', state: 'OPEN' }]),
|
|
75
|
-
getPullRequest: vi.fn().mockResolvedValue({
|
|
76
|
-
number: 10,
|
|
77
|
-
title: 'Feature',
|
|
78
|
-
url: 'url10',
|
|
79
|
-
state: 'OPEN',
|
|
80
|
-
body: 'PR body',
|
|
81
|
-
draft: false,
|
|
82
|
-
headBranch: 'feature',
|
|
83
|
-
baseBranch: 'main',
|
|
84
|
-
author: 'dev1',
|
|
85
|
-
createdAt: '2025-01-01T00:00:00Z',
|
|
86
|
-
updatedAt: '2025-01-02T00:00:00Z',
|
|
87
|
-
mergedAt: null,
|
|
88
|
-
closedAt: null,
|
|
89
|
-
additions: 100,
|
|
90
|
-
deletions: 50,
|
|
91
|
-
changedFiles: 5,
|
|
92
|
-
}),
|
|
93
|
-
getWorkflowRuns: vi.fn().mockResolvedValue([]),
|
|
94
|
-
getProjectKanban: vi.fn().mockResolvedValue(null),
|
|
95
|
-
getMilestones: vi.fn().mockResolvedValue([
|
|
96
|
-
{
|
|
97
|
-
number: 1,
|
|
98
|
-
title: 'v1.0',
|
|
99
|
-
description: 'First',
|
|
100
|
-
state: 'open',
|
|
101
|
-
url: 'url',
|
|
102
|
-
dueOn: null,
|
|
103
|
-
openIssues: 5,
|
|
104
|
-
closedIssues: 10,
|
|
105
|
-
createdAt: '2025-01-01T00:00:00Z',
|
|
106
|
-
updatedAt: '2025-01-01T00:00:00Z',
|
|
107
|
-
creator: 'owner1',
|
|
108
|
-
},
|
|
109
|
-
]),
|
|
110
|
-
getMilestone: vi.fn().mockResolvedValue({
|
|
111
|
-
number: 1,
|
|
112
|
-
title: 'v1.0',
|
|
113
|
-
description: 'First',
|
|
114
|
-
state: 'open',
|
|
115
|
-
url: 'url',
|
|
116
|
-
dueOn: null,
|
|
117
|
-
openIssues: 5,
|
|
118
|
-
closedIssues: 10,
|
|
119
|
-
createdAt: '2025-01-01T00:00:00Z',
|
|
120
|
-
updatedAt: '2025-01-01T00:00:00Z',
|
|
121
|
-
creator: 'owner1',
|
|
122
|
-
}),
|
|
123
|
-
createMilestone: vi.fn().mockResolvedValue({
|
|
124
|
-
number: 3,
|
|
125
|
-
title: 'v2.0',
|
|
126
|
-
description: null,
|
|
127
|
-
state: 'open',
|
|
128
|
-
url: 'url',
|
|
129
|
-
dueOn: null,
|
|
130
|
-
openIssues: 0,
|
|
131
|
-
closedIssues: 0,
|
|
132
|
-
createdAt: '2025-01-01T00:00:00Z',
|
|
133
|
-
updatedAt: '2025-01-01T00:00:00Z',
|
|
134
|
-
creator: 'dev1',
|
|
135
|
-
}),
|
|
136
|
-
updateMilestone: vi.fn().mockResolvedValue({
|
|
137
|
-
number: 1,
|
|
138
|
-
title: 'v1.1',
|
|
139
|
-
description: null,
|
|
140
|
-
state: 'closed',
|
|
141
|
-
url: 'url',
|
|
142
|
-
dueOn: null,
|
|
143
|
-
openIssues: 0,
|
|
144
|
-
closedIssues: 15,
|
|
145
|
-
createdAt: '2025-01-01T00:00:00Z',
|
|
146
|
-
updatedAt: '2025-02-01T00:00:00Z',
|
|
147
|
-
creator: 'dev1',
|
|
148
|
-
}),
|
|
149
|
-
deleteMilestone: vi.fn().mockResolvedValue({ success: true }),
|
|
150
|
-
moveProjectItem: vi.fn().mockResolvedValue({ success: true }),
|
|
151
|
-
addProjectItem: vi.fn().mockResolvedValue({ success: true, itemId: 'PVTITEM_NEW' }),
|
|
152
|
-
clearCache: vi.fn(),
|
|
153
|
-
invalidateCache: vi.fn(),
|
|
154
|
-
}
|
|
155
|
-
return { ...defaults, ...overrides } as unknown as GitHubIntegration
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
describe('GitHub Tool Handlers', () => {
|
|
159
|
-
let db: DatabaseAdapter
|
|
160
|
-
const testDbPath = './test-gh-tools.db'
|
|
161
|
-
|
|
162
|
-
beforeAll(async () => {
|
|
163
|
-
db = new DatabaseAdapter(testDbPath)
|
|
164
|
-
await db.initialize()
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
afterAll(() => {
|
|
168
|
-
db.close()
|
|
169
|
-
try {
|
|
170
|
-
const fs = require('node:fs')
|
|
171
|
-
if (fs.existsSync(testDbPath)) fs.unlinkSync(testDbPath)
|
|
172
|
-
} catch {
|
|
173
|
-
// Ignore cleanup errors
|
|
174
|
-
}
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
// ========================================================================
|
|
178
|
-
// Issues
|
|
179
|
-
// ========================================================================
|
|
180
|
-
|
|
181
|
-
describe('get_github_issues', () => {
|
|
182
|
-
it('should return issues from mock', async () => {
|
|
183
|
-
const github = createMockGitHub()
|
|
184
|
-
const result = (await callTool('get_github_issues', {}, db, undefined, github)) as {
|
|
185
|
-
issues: unknown[]
|
|
186
|
-
count: number
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
expect(result.issues).toBeDefined()
|
|
190
|
-
expect(result.count).toBe(1)
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
it('should return error when no github', async () => {
|
|
194
|
-
const result = (await callTool('get_github_issues', {}, db, undefined, undefined)) as {
|
|
195
|
-
error: string
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
199
|
-
})
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
describe('get_github_issue', () => {
|
|
203
|
-
it('should return issue details', async () => {
|
|
204
|
-
const github = createMockGitHub()
|
|
205
|
-
const result = (await callTool(
|
|
206
|
-
'get_github_issue',
|
|
207
|
-
{ issue_number: 1 },
|
|
208
|
-
db,
|
|
209
|
-
undefined,
|
|
210
|
-
github
|
|
211
|
-
)) as { issue: { number: number }; journalEntries?: unknown[] }
|
|
212
|
-
|
|
213
|
-
expect(result.issue.number).toBe(1)
|
|
214
|
-
})
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
describe('get_github_prs', () => {
|
|
218
|
-
it('should return pull requests', async () => {
|
|
219
|
-
const github = createMockGitHub()
|
|
220
|
-
const result = (await callTool('get_github_prs', {}, db, undefined, github)) as {
|
|
221
|
-
pullRequests: unknown[]
|
|
222
|
-
count: number
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
expect(result.pullRequests).toBeDefined()
|
|
226
|
-
expect(result.count).toBe(1)
|
|
227
|
-
})
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
describe('get_github_pr', () => {
|
|
231
|
-
it('should return PR details', async () => {
|
|
232
|
-
const github = createMockGitHub()
|
|
233
|
-
const result = (await callTool(
|
|
234
|
-
'get_github_pr',
|
|
235
|
-
{ pr_number: 10 },
|
|
236
|
-
db,
|
|
237
|
-
undefined,
|
|
238
|
-
github
|
|
239
|
-
)) as { pullRequest: { number: number } }
|
|
240
|
-
|
|
241
|
-
expect(result.pullRequest.number).toBe(10)
|
|
242
|
-
})
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
// ========================================================================
|
|
246
|
-
// Context
|
|
247
|
-
// ========================================================================
|
|
248
|
-
|
|
249
|
-
describe('get_github_context', () => {
|
|
250
|
-
it('should return repo context', async () => {
|
|
251
|
-
const github = createMockGitHub()
|
|
252
|
-
const result = (await callTool('get_github_context', {}, db, undefined, github)) as {
|
|
253
|
-
repoName: string
|
|
254
|
-
branch: string
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
expect(result.repoName).toBe('testrepo')
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
it('should return error when no github', async () => {
|
|
261
|
-
const result = (await callTool('get_github_context', {}, db, undefined, undefined)) as {
|
|
262
|
-
error: string
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
266
|
-
})
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
// ========================================================================
|
|
270
|
-
// Kanban
|
|
271
|
-
// ========================================================================
|
|
272
|
-
|
|
273
|
-
describe('get_kanban_board', () => {
|
|
274
|
-
it('should return error when project not found', async () => {
|
|
275
|
-
const github = createMockGitHub()
|
|
276
|
-
const result = (await callTool(
|
|
277
|
-
'get_kanban_board',
|
|
278
|
-
{ project_number: 1 },
|
|
279
|
-
db,
|
|
280
|
-
undefined,
|
|
281
|
-
github
|
|
282
|
-
)) as { error: string }
|
|
283
|
-
|
|
284
|
-
// getProjectKanban returns null by default
|
|
285
|
-
expect(result.error).toContain('not found')
|
|
286
|
-
})
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
// ========================================================================
|
|
290
|
-
// Milestones
|
|
291
|
-
// ========================================================================
|
|
292
|
-
|
|
293
|
-
describe('get_github_milestones', () => {
|
|
294
|
-
it('should return milestones', async () => {
|
|
295
|
-
const github = createMockGitHub()
|
|
296
|
-
const result = (await callTool('get_github_milestones', {}, db, undefined, github)) as {
|
|
297
|
-
milestones: unknown[]
|
|
298
|
-
count: number
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
expect(result.milestones).toBeDefined()
|
|
302
|
-
expect(result.count).toBe(1)
|
|
303
|
-
})
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
describe('get_github_milestone', () => {
|
|
307
|
-
it('should return single milestone', async () => {
|
|
308
|
-
const github = createMockGitHub()
|
|
309
|
-
const result = (await callTool(
|
|
310
|
-
'get_github_milestone',
|
|
311
|
-
{ milestone_number: 1 },
|
|
312
|
-
db,
|
|
313
|
-
undefined,
|
|
314
|
-
github
|
|
315
|
-
)) as { milestone: { number: number } }
|
|
316
|
-
|
|
317
|
-
expect(result.milestone.number).toBe(1)
|
|
318
|
-
})
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
describe('create_github_milestone', () => {
|
|
322
|
-
it('should create milestone', async () => {
|
|
323
|
-
const github = createMockGitHub()
|
|
324
|
-
const result = (await callTool(
|
|
325
|
-
'create_github_milestone',
|
|
326
|
-
{ title: 'v2.0' },
|
|
327
|
-
db,
|
|
328
|
-
undefined,
|
|
329
|
-
github
|
|
330
|
-
)) as { success: boolean; milestone: { number: number } }
|
|
331
|
-
|
|
332
|
-
expect(result.success).toBe(true)
|
|
333
|
-
expect(result.milestone.number).toBe(3)
|
|
334
|
-
})
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
describe('update_github_milestone', () => {
|
|
338
|
-
it('should update milestone', async () => {
|
|
339
|
-
const github = createMockGitHub()
|
|
340
|
-
const result = (await callTool(
|
|
341
|
-
'update_github_milestone',
|
|
342
|
-
{ milestone_number: 1, title: 'v1.1' },
|
|
343
|
-
db,
|
|
344
|
-
undefined,
|
|
345
|
-
github
|
|
346
|
-
)) as { success: boolean; milestone: { title: string } }
|
|
347
|
-
|
|
348
|
-
expect(result.success).toBe(true)
|
|
349
|
-
expect(result.milestone.title).toBe('v1.1')
|
|
350
|
-
})
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
describe('delete_github_milestone', () => {
|
|
354
|
-
it('should delete milestone', async () => {
|
|
355
|
-
const github = createMockGitHub()
|
|
356
|
-
const result = (await callTool(
|
|
357
|
-
'delete_github_milestone',
|
|
358
|
-
{ milestone_number: 1, confirm: true },
|
|
359
|
-
db,
|
|
360
|
-
undefined,
|
|
361
|
-
github
|
|
362
|
-
)) as { success: boolean }
|
|
363
|
-
|
|
364
|
-
expect(result.success).toBe(true)
|
|
365
|
-
})
|
|
366
|
-
})
|
|
367
|
-
|
|
368
|
-
// ========================================================================
|
|
369
|
-
// Issue with entry tools
|
|
370
|
-
// ========================================================================
|
|
371
|
-
|
|
372
|
-
describe('create_github_issue_with_entry', () => {
|
|
373
|
-
it('should create issue and journal entry', async () => {
|
|
374
|
-
const github = createMockGitHub()
|
|
375
|
-
const result = (await callTool(
|
|
376
|
-
'create_github_issue_with_entry',
|
|
377
|
-
{ title: 'Test Issue', journal_content: 'Created test issue for tracking' },
|
|
378
|
-
db,
|
|
379
|
-
undefined,
|
|
380
|
-
github
|
|
381
|
-
)) as { success: boolean; issue: { number: number }; journalEntry: { id: number } }
|
|
382
|
-
|
|
383
|
-
expect(result.success).toBe(true)
|
|
384
|
-
expect(result.issue.number).toBe(42)
|
|
385
|
-
expect(result.journalEntry.id).toBeGreaterThan(0)
|
|
386
|
-
})
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
// ========================================================================
|
|
390
|
-
// Kanban operations
|
|
391
|
-
// ========================================================================
|
|
392
|
-
|
|
393
|
-
describe('move_kanban_item', () => {
|
|
394
|
-
it('should move item when board and status found', async () => {
|
|
395
|
-
const github = createMockGitHub({
|
|
396
|
-
getProjectKanban: vi.fn().mockResolvedValue({
|
|
397
|
-
projectId: 'PVT_1',
|
|
398
|
-
projectTitle: 'Board',
|
|
399
|
-
statusFieldId: 'FIELD_1',
|
|
400
|
-
statusOptions: [
|
|
401
|
-
{ id: 'OPT_TODO', name: 'Todo' },
|
|
402
|
-
{ id: 'OPT_DONE', name: 'Done' },
|
|
403
|
-
],
|
|
404
|
-
columns: [],
|
|
405
|
-
totalItems: 0,
|
|
406
|
-
}),
|
|
407
|
-
})
|
|
408
|
-
|
|
409
|
-
const result = (await callTool(
|
|
410
|
-
'move_kanban_item',
|
|
411
|
-
{
|
|
412
|
-
project_number: 1,
|
|
413
|
-
item_id: 'PVTITEM_1',
|
|
414
|
-
target_status: 'Done',
|
|
415
|
-
},
|
|
416
|
-
db,
|
|
417
|
-
undefined,
|
|
418
|
-
github
|
|
419
|
-
)) as { success: boolean; newStatus: string }
|
|
420
|
-
|
|
421
|
-
expect(result.success).toBe(true)
|
|
422
|
-
expect(result.newStatus).toBe('Done')
|
|
423
|
-
})
|
|
424
|
-
|
|
425
|
-
it('should return error when status not found', async () => {
|
|
426
|
-
const github = createMockGitHub({
|
|
427
|
-
getProjectKanban: vi.fn().mockResolvedValue({
|
|
428
|
-
projectId: 'PVT_1',
|
|
429
|
-
statusFieldId: 'FIELD_1',
|
|
430
|
-
statusOptions: [{ id: 'OPT_TODO', name: 'Todo' }],
|
|
431
|
-
columns: [],
|
|
432
|
-
totalItems: 0,
|
|
433
|
-
}),
|
|
434
|
-
})
|
|
435
|
-
|
|
436
|
-
const result = (await callTool(
|
|
437
|
-
'move_kanban_item',
|
|
438
|
-
{
|
|
439
|
-
project_number: 1,
|
|
440
|
-
item_id: 'PVTITEM_1',
|
|
441
|
-
target_status: 'Nonexistent',
|
|
442
|
-
},
|
|
443
|
-
db,
|
|
444
|
-
undefined,
|
|
445
|
-
github
|
|
446
|
-
)) as { error: string; availableStatuses: string[] }
|
|
447
|
-
|
|
448
|
-
expect(result.error).toContain('not found')
|
|
449
|
-
expect(result.availableStatuses).toEqual(['Todo'])
|
|
450
|
-
})
|
|
451
|
-
|
|
452
|
-
it('should return error when no github', async () => {
|
|
453
|
-
const result = (await callTool(
|
|
454
|
-
'move_kanban_item',
|
|
455
|
-
{
|
|
456
|
-
project_number: 1,
|
|
457
|
-
item_id: 'X',
|
|
458
|
-
target_status: 'Done',
|
|
459
|
-
},
|
|
460
|
-
db
|
|
461
|
-
)) as { error: string }
|
|
462
|
-
|
|
463
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
it('should return error when project not found', async () => {
|
|
467
|
-
const github = createMockGitHub()
|
|
468
|
-
const result = (await callTool(
|
|
469
|
-
'move_kanban_item',
|
|
470
|
-
{
|
|
471
|
-
project_number: 999,
|
|
472
|
-
item_id: 'X',
|
|
473
|
-
target_status: 'Done',
|
|
474
|
-
},
|
|
475
|
-
db,
|
|
476
|
-
undefined,
|
|
477
|
-
github
|
|
478
|
-
)) as { error: string }
|
|
479
|
-
|
|
480
|
-
expect(result.error).toContain('not found')
|
|
481
|
-
})
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
// ========================================================================
|
|
485
|
-
// Close issue with entry
|
|
486
|
-
// ========================================================================
|
|
487
|
-
|
|
488
|
-
describe('close_github_issue_with_entry', () => {
|
|
489
|
-
it('should close issue and create journal entry', async () => {
|
|
490
|
-
const github = createMockGitHub()
|
|
491
|
-
|
|
492
|
-
const result = (await callTool(
|
|
493
|
-
'close_github_issue_with_entry',
|
|
494
|
-
{ issue_number: 1, resolution_notes: 'Fixed the bug' },
|
|
495
|
-
db,
|
|
496
|
-
undefined,
|
|
497
|
-
github
|
|
498
|
-
)) as {
|
|
499
|
-
success: boolean
|
|
500
|
-
issue: { number: number; newState: string }
|
|
501
|
-
journalEntry: { id: number }
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
expect(result.success).toBe(true)
|
|
505
|
-
expect(result.issue.number).toBe(1)
|
|
506
|
-
expect(result.issue.newState).toBe('CLOSED')
|
|
507
|
-
expect(result.journalEntry.id).toBeGreaterThan(0)
|
|
508
|
-
})
|
|
509
|
-
|
|
510
|
-
it('should close issue with move_to_done', async () => {
|
|
511
|
-
const github = createMockGitHub({
|
|
512
|
-
getProjectKanban: vi.fn().mockResolvedValue({
|
|
513
|
-
projectId: 'PVT_1',
|
|
514
|
-
projectTitle: 'Board',
|
|
515
|
-
statusFieldId: 'FIELD_1',
|
|
516
|
-
statusOptions: [
|
|
517
|
-
{ id: 'OPT_DONE', name: 'Done' },
|
|
518
|
-
{ id: 'OPT_TODO', name: 'Todo' },
|
|
519
|
-
],
|
|
520
|
-
columns: [
|
|
521
|
-
{
|
|
522
|
-
status: 'Todo',
|
|
523
|
-
items: [
|
|
524
|
-
{
|
|
525
|
-
id: 'PVTITEM_ISSUE1',
|
|
526
|
-
title: 'Test Issue',
|
|
527
|
-
type: 'ISSUE',
|
|
528
|
-
number: 1,
|
|
529
|
-
},
|
|
530
|
-
],
|
|
531
|
-
},
|
|
532
|
-
],
|
|
533
|
-
totalItems: 1,
|
|
534
|
-
}),
|
|
535
|
-
})
|
|
536
|
-
|
|
537
|
-
const result = (await callTool(
|
|
538
|
-
'close_github_issue_with_entry',
|
|
539
|
-
{
|
|
540
|
-
issue_number: 1,
|
|
541
|
-
resolution_notes: 'Done!',
|
|
542
|
-
move_to_done: true,
|
|
543
|
-
project_number: 1,
|
|
544
|
-
},
|
|
545
|
-
db,
|
|
546
|
-
undefined,
|
|
547
|
-
github
|
|
548
|
-
)) as {
|
|
549
|
-
success: boolean
|
|
550
|
-
issue: { number: number }
|
|
551
|
-
kanbanMove?: { success: boolean }
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
expect(result.success).toBe(true)
|
|
555
|
-
})
|
|
556
|
-
|
|
557
|
-
it('should return error when issue not found', async () => {
|
|
558
|
-
const github = createMockGitHub({
|
|
559
|
-
getIssue: vi.fn().mockResolvedValue(null),
|
|
560
|
-
})
|
|
561
|
-
|
|
562
|
-
const result = (await callTool(
|
|
563
|
-
'close_github_issue_with_entry',
|
|
564
|
-
{ issue_number: 999 },
|
|
565
|
-
db,
|
|
566
|
-
undefined,
|
|
567
|
-
github
|
|
568
|
-
)) as { error: string }
|
|
569
|
-
|
|
570
|
-
expect(result.error).toContain('not found')
|
|
571
|
-
})
|
|
572
|
-
|
|
573
|
-
it('should return error when issue already closed', async () => {
|
|
574
|
-
const github = createMockGitHub({
|
|
575
|
-
getIssue: vi.fn().mockResolvedValue({
|
|
576
|
-
number: 1,
|
|
577
|
-
title: 'Test',
|
|
578
|
-
url: 'url',
|
|
579
|
-
state: 'CLOSED',
|
|
580
|
-
}),
|
|
581
|
-
})
|
|
582
|
-
|
|
583
|
-
const result = (await callTool(
|
|
584
|
-
'close_github_issue_with_entry',
|
|
585
|
-
{ issue_number: 1 },
|
|
586
|
-
db,
|
|
587
|
-
undefined,
|
|
588
|
-
github
|
|
589
|
-
)) as { error: string }
|
|
590
|
-
|
|
591
|
-
expect(result.error).toContain('already closed')
|
|
592
|
-
})
|
|
593
|
-
|
|
594
|
-
it('should return error when no github', async () => {
|
|
595
|
-
const result = (await callTool(
|
|
596
|
-
'close_github_issue_with_entry',
|
|
597
|
-
{ issue_number: 1 },
|
|
598
|
-
db
|
|
599
|
-
)) as { error: string }
|
|
600
|
-
|
|
601
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
602
|
-
})
|
|
603
|
-
})
|
|
604
|
-
|
|
605
|
-
// ========================================================================
|
|
606
|
-
// Repository Insights
|
|
607
|
-
// ========================================================================
|
|
608
|
-
|
|
609
|
-
describe('get_repo_insights', () => {
|
|
610
|
-
it('should return stars section by default', async () => {
|
|
611
|
-
const github = createMockGitHub({
|
|
612
|
-
getRepoStats: vi.fn().mockResolvedValue({
|
|
613
|
-
stars: 42,
|
|
614
|
-
forks: 5,
|
|
615
|
-
watchers: 3,
|
|
616
|
-
openIssues: 2,
|
|
617
|
-
size: 100,
|
|
618
|
-
defaultBranch: 'main',
|
|
619
|
-
}),
|
|
620
|
-
})
|
|
621
|
-
|
|
622
|
-
const result = (await callTool(
|
|
623
|
-
'get_repo_insights',
|
|
624
|
-
{},
|
|
625
|
-
db,
|
|
626
|
-
undefined,
|
|
627
|
-
github
|
|
628
|
-
)) as Record<string, unknown>
|
|
629
|
-
|
|
630
|
-
expect(result['owner']).toBe('testowner')
|
|
631
|
-
expect(result['repo']).toBe('testrepo')
|
|
632
|
-
expect(result['stars']).toBe(42)
|
|
633
|
-
expect(result['forks']).toBe(5)
|
|
634
|
-
})
|
|
635
|
-
|
|
636
|
-
it('should return traffic section', async () => {
|
|
637
|
-
const github = createMockGitHub({
|
|
638
|
-
getTrafficData: vi.fn().mockResolvedValue({
|
|
639
|
-
views: { total: 100, uniques: 50 },
|
|
640
|
-
clones: { total: 20, uniques: 10 },
|
|
641
|
-
}),
|
|
642
|
-
})
|
|
643
|
-
|
|
644
|
-
const result = (await callTool(
|
|
645
|
-
'get_repo_insights',
|
|
646
|
-
{ sections: 'traffic' },
|
|
647
|
-
db,
|
|
648
|
-
undefined,
|
|
649
|
-
github
|
|
650
|
-
)) as Record<string, unknown>
|
|
651
|
-
|
|
652
|
-
expect(result['traffic']).toBeDefined()
|
|
653
|
-
})
|
|
654
|
-
|
|
655
|
-
it('should return referrers section', async () => {
|
|
656
|
-
const github = createMockGitHub({
|
|
657
|
-
getTopReferrers: vi
|
|
658
|
-
.fn()
|
|
659
|
-
.mockResolvedValue([{ referrer: 'google.com', count: 10, uniques: 5 }]),
|
|
660
|
-
})
|
|
661
|
-
|
|
662
|
-
const result = (await callTool(
|
|
663
|
-
'get_repo_insights',
|
|
664
|
-
{ sections: 'referrers' },
|
|
665
|
-
db,
|
|
666
|
-
undefined,
|
|
667
|
-
github
|
|
668
|
-
)) as Record<string, unknown>
|
|
669
|
-
|
|
670
|
-
expect(result['referrers']).toBeDefined()
|
|
671
|
-
})
|
|
672
|
-
|
|
673
|
-
it('should return paths section', async () => {
|
|
674
|
-
const github = createMockGitHub({
|
|
675
|
-
getPopularPaths: vi
|
|
676
|
-
.fn()
|
|
677
|
-
.mockResolvedValue([
|
|
678
|
-
{ path: '/readme', title: 'README', count: 50, uniques: 25 },
|
|
679
|
-
]),
|
|
680
|
-
})
|
|
681
|
-
|
|
682
|
-
const result = (await callTool(
|
|
683
|
-
'get_repo_insights',
|
|
684
|
-
{ sections: 'paths' },
|
|
685
|
-
db,
|
|
686
|
-
undefined,
|
|
687
|
-
github
|
|
688
|
-
)) as Record<string, unknown>
|
|
689
|
-
|
|
690
|
-
expect(result['paths']).toBeDefined()
|
|
691
|
-
})
|
|
692
|
-
|
|
693
|
-
it('should return all sections', async () => {
|
|
694
|
-
const github = createMockGitHub({
|
|
695
|
-
getRepoStats: vi.fn().mockResolvedValue({
|
|
696
|
-
stars: 42,
|
|
697
|
-
forks: 5,
|
|
698
|
-
watchers: 3,
|
|
699
|
-
openIssues: 2,
|
|
700
|
-
size: 100,
|
|
701
|
-
defaultBranch: 'main',
|
|
702
|
-
}),
|
|
703
|
-
getTrafficData: vi.fn().mockResolvedValue({
|
|
704
|
-
views: { total: 100, uniques: 50 },
|
|
705
|
-
clones: { total: 20, uniques: 10 },
|
|
706
|
-
}),
|
|
707
|
-
getTopReferrers: vi.fn().mockResolvedValue([]),
|
|
708
|
-
getPopularPaths: vi.fn().mockResolvedValue([]),
|
|
709
|
-
})
|
|
710
|
-
|
|
711
|
-
const result = (await callTool(
|
|
712
|
-
'get_repo_insights',
|
|
713
|
-
{ sections: 'all' },
|
|
714
|
-
db,
|
|
715
|
-
undefined,
|
|
716
|
-
github
|
|
717
|
-
)) as Record<string, unknown>
|
|
718
|
-
|
|
719
|
-
expect(result['stars']).toBe(42)
|
|
720
|
-
expect(result['traffic']).toBeDefined()
|
|
721
|
-
expect(result['referrers']).toBeDefined()
|
|
722
|
-
expect(result['paths']).toBeDefined()
|
|
723
|
-
// 'all' section includes size and defaultBranch
|
|
724
|
-
expect(result['size']).toBe(100)
|
|
725
|
-
expect(result['defaultBranch']).toBe('main')
|
|
726
|
-
})
|
|
727
|
-
|
|
728
|
-
it('should return error when no github', async () => {
|
|
729
|
-
const result = (await callTool('get_repo_insights', {}, db, undefined, undefined)) as {
|
|
730
|
-
error: string
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
734
|
-
})
|
|
735
|
-
|
|
736
|
-
it('should return error when no owner/repo detected', async () => {
|
|
737
|
-
const github = createMockGitHub({
|
|
738
|
-
getRepoInfo: vi.fn().mockResolvedValue({
|
|
739
|
-
owner: null,
|
|
740
|
-
repo: null,
|
|
741
|
-
branch: null,
|
|
742
|
-
}),
|
|
743
|
-
})
|
|
744
|
-
|
|
745
|
-
const result = (await callTool('get_repo_insights', {}, db, undefined, github)) as {
|
|
746
|
-
error: string
|
|
747
|
-
requiresUserInput: boolean
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
expect(result.error).toContain('Could not auto-detect')
|
|
751
|
-
expect(result.requiresUserInput).toBe(true)
|
|
752
|
-
})
|
|
753
|
-
})
|
|
754
|
-
|
|
755
|
-
// ========================================================================
|
|
756
|
-
// Milestone edge cases
|
|
757
|
-
// ========================================================================
|
|
758
|
-
|
|
759
|
-
describe('milestone edge cases', () => {
|
|
760
|
-
it('get_github_milestones should return error when no repo', async () => {
|
|
761
|
-
const github = createMockGitHub({
|
|
762
|
-
getRepoInfo: vi.fn().mockResolvedValue({
|
|
763
|
-
owner: null,
|
|
764
|
-
repo: null,
|
|
765
|
-
branch: null,
|
|
766
|
-
}),
|
|
767
|
-
})
|
|
768
|
-
|
|
769
|
-
const result = (await callTool('get_github_milestones', {}, db, undefined, github)) as {
|
|
770
|
-
error: string
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
expect(result.error).toBeDefined()
|
|
774
|
-
})
|
|
775
|
-
|
|
776
|
-
it('get_github_milestone should return not found', async () => {
|
|
777
|
-
const github = createMockGitHub({
|
|
778
|
-
getMilestone: vi.fn().mockResolvedValue(null),
|
|
779
|
-
})
|
|
780
|
-
|
|
781
|
-
const result = (await callTool(
|
|
782
|
-
'get_github_milestone',
|
|
783
|
-
{ milestone_number: 999 },
|
|
784
|
-
db,
|
|
785
|
-
undefined,
|
|
786
|
-
github
|
|
787
|
-
)) as { error: string }
|
|
788
|
-
|
|
789
|
-
expect(result.error).toContain('not found')
|
|
790
|
-
})
|
|
791
|
-
|
|
792
|
-
it('create_github_milestone should return error when creation fails', async () => {
|
|
793
|
-
const github = createMockGitHub({
|
|
794
|
-
createMilestone: vi.fn().mockResolvedValue(null),
|
|
795
|
-
})
|
|
796
|
-
|
|
797
|
-
const result = (await callTool(
|
|
798
|
-
'create_github_milestone',
|
|
799
|
-
{ title: 'Will fail' },
|
|
800
|
-
db,
|
|
801
|
-
undefined,
|
|
802
|
-
github
|
|
803
|
-
)) as { error: string }
|
|
804
|
-
|
|
805
|
-
expect(result.error).toContain('Failed')
|
|
806
|
-
})
|
|
807
|
-
|
|
808
|
-
it('create_github_milestone with due date', async () => {
|
|
809
|
-
const github = createMockGitHub()
|
|
810
|
-
|
|
811
|
-
const result = (await callTool(
|
|
812
|
-
'create_github_milestone',
|
|
813
|
-
{ title: 'v3.0', due_on: '2026-06-01' },
|
|
814
|
-
db,
|
|
815
|
-
undefined,
|
|
816
|
-
github
|
|
817
|
-
)) as { success: boolean; milestone: { number: number } }
|
|
818
|
-
|
|
819
|
-
expect(result.success).toBe(true)
|
|
820
|
-
})
|
|
821
|
-
|
|
822
|
-
it('update_github_milestone should return error when update fails', async () => {
|
|
823
|
-
const github = createMockGitHub({
|
|
824
|
-
updateMilestone: vi.fn().mockResolvedValue(null),
|
|
825
|
-
})
|
|
826
|
-
|
|
827
|
-
const result = (await callTool(
|
|
828
|
-
'update_github_milestone',
|
|
829
|
-
{ milestone_number: 1, title: 'Will fail' },
|
|
830
|
-
db,
|
|
831
|
-
undefined,
|
|
832
|
-
github
|
|
833
|
-
)) as { error: string }
|
|
834
|
-
|
|
835
|
-
expect(result.error).toContain('Failed')
|
|
836
|
-
})
|
|
837
|
-
|
|
838
|
-
it('delete_github_milestone should return error when delete fails', async () => {
|
|
839
|
-
const github = createMockGitHub({
|
|
840
|
-
deleteMilestone: vi.fn().mockResolvedValue({ success: false }),
|
|
841
|
-
})
|
|
842
|
-
|
|
843
|
-
const result = (await callTool(
|
|
844
|
-
'delete_github_milestone',
|
|
845
|
-
{ milestone_number: 1, confirm: true },
|
|
846
|
-
db,
|
|
847
|
-
undefined,
|
|
848
|
-
github
|
|
849
|
-
)) as { success: boolean; message: string }
|
|
850
|
-
|
|
851
|
-
expect(result.success).toBe(false)
|
|
852
|
-
expect(result.message).toContain('Failed')
|
|
853
|
-
})
|
|
854
|
-
|
|
855
|
-
it('delete_github_milestone without confirm is rejected by zod', async () => {
|
|
856
|
-
const github = createMockGitHub()
|
|
857
|
-
|
|
858
|
-
// confirm must be literal true, passing false should fail
|
|
859
|
-
try {
|
|
860
|
-
await callTool(
|
|
861
|
-
'delete_github_milestone',
|
|
862
|
-
{ milestone_number: 1, confirm: false },
|
|
863
|
-
db,
|
|
864
|
-
undefined,
|
|
865
|
-
github
|
|
866
|
-
)
|
|
867
|
-
// If we get here, check the result for error
|
|
868
|
-
} catch {
|
|
869
|
-
// Expected: zod validation failure
|
|
870
|
-
}
|
|
871
|
-
})
|
|
872
|
-
|
|
873
|
-
it('get_github_milestones should return error when no github', async () => {
|
|
874
|
-
const result = (await callTool(
|
|
875
|
-
'get_github_milestones',
|
|
876
|
-
{},
|
|
877
|
-
db,
|
|
878
|
-
undefined,
|
|
879
|
-
undefined
|
|
880
|
-
)) as { error: string }
|
|
881
|
-
|
|
882
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
883
|
-
})
|
|
884
|
-
})
|
|
885
|
-
|
|
886
|
-
// ========================================================================
|
|
887
|
-
// Backup tools
|
|
888
|
-
// ========================================================================
|
|
889
|
-
|
|
890
|
-
describe('backup_journal', () => {
|
|
891
|
-
it('should create a backup', async () => {
|
|
892
|
-
const result = (await callTool('backup_journal', {}, db)) as {
|
|
893
|
-
success: boolean
|
|
894
|
-
filename: string
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
expect(result.success).toBe(true)
|
|
898
|
-
expect(result.filename).toBeDefined()
|
|
899
|
-
})
|
|
900
|
-
|
|
901
|
-
it('should create a backup with custom name', async () => {
|
|
902
|
-
const result = (await callTool('backup_journal', { name: 'my-test-backup' }, db)) as {
|
|
903
|
-
success: boolean
|
|
904
|
-
filename: string
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
expect(result.success).toBe(true)
|
|
908
|
-
})
|
|
909
|
-
})
|
|
910
|
-
|
|
911
|
-
describe('list_backups', () => {
|
|
912
|
-
it('should list backups', async () => {
|
|
913
|
-
const result = (await callTool('list_backups', {}, db)) as {
|
|
914
|
-
backups: unknown[]
|
|
915
|
-
total: number
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
expect(result.backups).toBeDefined()
|
|
919
|
-
expect(typeof result.total).toBe('number')
|
|
920
|
-
})
|
|
921
|
-
})
|
|
922
|
-
|
|
923
|
-
describe('cleanup_backups', () => {
|
|
924
|
-
it('should cleanup old backups', async () => {
|
|
925
|
-
const result = (await callTool('cleanup_backups', { keep_count: 5 }, db)) as {
|
|
926
|
-
success: boolean
|
|
927
|
-
keptCount: number
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
expect(result.success).toBe(true)
|
|
931
|
-
expect(typeof result.keptCount).toBe('number')
|
|
932
|
-
})
|
|
933
|
-
})
|
|
934
|
-
|
|
935
|
-
// ========================================================================
|
|
936
|
-
// create_github_issue_with_entry - project integration paths
|
|
937
|
-
// ========================================================================
|
|
938
|
-
|
|
939
|
-
describe('create_github_issue_with_entry - project integration', () => {
|
|
940
|
-
it('should add issue to project and set initial status', async () => {
|
|
941
|
-
const github = createMockGitHub({
|
|
942
|
-
getProjectKanban: vi.fn().mockResolvedValue({
|
|
943
|
-
projectId: 'PVT_1',
|
|
944
|
-
projectTitle: 'Board',
|
|
945
|
-
statusFieldId: 'FIELD_1',
|
|
946
|
-
statusOptions: [
|
|
947
|
-
{ id: 'OPT_BACKLOG', name: 'Backlog' },
|
|
948
|
-
{ id: 'OPT_DONE', name: 'Done' },
|
|
949
|
-
],
|
|
950
|
-
columns: [],
|
|
951
|
-
totalItems: 0,
|
|
952
|
-
}),
|
|
953
|
-
addProjectItem: vi.fn().mockResolvedValue({ success: true, itemId: 'PVTITEM_NEW' }),
|
|
954
|
-
moveProjectItem: vi.fn().mockResolvedValue({ success: true }),
|
|
955
|
-
})
|
|
956
|
-
|
|
957
|
-
const result = (await callTool(
|
|
958
|
-
'create_github_issue_with_entry',
|
|
959
|
-
{
|
|
960
|
-
title: 'Project Issue',
|
|
961
|
-
journal_content: 'Added to project',
|
|
962
|
-
project_number: 1,
|
|
963
|
-
initial_status: 'Backlog',
|
|
964
|
-
},
|
|
965
|
-
db,
|
|
966
|
-
undefined,
|
|
967
|
-
github
|
|
968
|
-
)) as {
|
|
969
|
-
success: boolean
|
|
970
|
-
project?: { added: boolean; initialStatus?: { set: boolean } }
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
expect(result.success).toBe(true)
|
|
974
|
-
expect(result.project?.added).toBe(true)
|
|
975
|
-
expect(result.project?.initialStatus?.set).toBe(true)
|
|
976
|
-
})
|
|
977
|
-
|
|
978
|
-
it('should handle project not found when adding issue', async () => {
|
|
979
|
-
const github = createMockGitHub({
|
|
980
|
-
getProjectKanban: vi.fn().mockResolvedValue(null),
|
|
981
|
-
})
|
|
982
|
-
|
|
983
|
-
const result = (await callTool(
|
|
984
|
-
'create_github_issue_with_entry',
|
|
985
|
-
{
|
|
986
|
-
title: 'Issue for missing project',
|
|
987
|
-
journal_content: 'Test',
|
|
988
|
-
project_number: 999,
|
|
989
|
-
},
|
|
990
|
-
db,
|
|
991
|
-
undefined,
|
|
992
|
-
github
|
|
993
|
-
)) as { success: boolean; project?: { added: boolean; error: string } }
|
|
994
|
-
|
|
995
|
-
expect(result.success).toBe(true) // Issue still created
|
|
996
|
-
expect(result.project?.added).toBe(false)
|
|
997
|
-
expect(result.project?.error).toContain('not found')
|
|
998
|
-
})
|
|
999
|
-
|
|
1000
|
-
it('should handle addProjectItem failure', async () => {
|
|
1001
|
-
const github = createMockGitHub({
|
|
1002
|
-
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1003
|
-
projectId: 'PVT_1',
|
|
1004
|
-
projectTitle: 'Board',
|
|
1005
|
-
statusFieldId: 'FIELD_1',
|
|
1006
|
-
statusOptions: [],
|
|
1007
|
-
columns: [],
|
|
1008
|
-
totalItems: 0,
|
|
1009
|
-
}),
|
|
1010
|
-
addProjectItem: vi
|
|
1011
|
-
.fn()
|
|
1012
|
-
.mockResolvedValue({ success: false, error: 'Permission denied' }),
|
|
1013
|
-
})
|
|
1014
|
-
|
|
1015
|
-
const result = (await callTool(
|
|
1016
|
-
'create_github_issue_with_entry',
|
|
1017
|
-
{
|
|
1018
|
-
title: 'Issue add fail',
|
|
1019
|
-
journal_content: 'Test',
|
|
1020
|
-
project_number: 1,
|
|
1021
|
-
},
|
|
1022
|
-
db,
|
|
1023
|
-
undefined,
|
|
1024
|
-
github
|
|
1025
|
-
)) as { success: boolean; project?: { added: boolean; error: string } }
|
|
1026
|
-
|
|
1027
|
-
expect(result.success).toBe(true)
|
|
1028
|
-
expect(result.project?.added).toBe(false)
|
|
1029
|
-
})
|
|
1030
|
-
|
|
1031
|
-
it('should handle initial_status not found on board', async () => {
|
|
1032
|
-
const github = createMockGitHub({
|
|
1033
|
-
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1034
|
-
projectId: 'PVT_1',
|
|
1035
|
-
projectTitle: 'Board',
|
|
1036
|
-
statusFieldId: 'FIELD_1',
|
|
1037
|
-
statusOptions: [{ id: 'OPT_TODO', name: 'Todo' }],
|
|
1038
|
-
columns: [],
|
|
1039
|
-
totalItems: 0,
|
|
1040
|
-
}),
|
|
1041
|
-
addProjectItem: vi.fn().mockResolvedValue({ success: true, itemId: 'PVTITEM_NEW' }),
|
|
1042
|
-
})
|
|
1043
|
-
|
|
1044
|
-
const result = (await callTool(
|
|
1045
|
-
'create_github_issue_with_entry',
|
|
1046
|
-
{
|
|
1047
|
-
title: 'Issue bad status',
|
|
1048
|
-
journal_content: 'Test',
|
|
1049
|
-
project_number: 1,
|
|
1050
|
-
initial_status: 'Nonexistent',
|
|
1051
|
-
},
|
|
1052
|
-
db,
|
|
1053
|
-
undefined,
|
|
1054
|
-
github
|
|
1055
|
-
)) as {
|
|
1056
|
-
success: boolean
|
|
1057
|
-
project?: { added: boolean; initialStatus?: { set: boolean; error: string } }
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
expect(result.success).toBe(true)
|
|
1061
|
-
expect(result.project?.added).toBe(true)
|
|
1062
|
-
expect(result.project?.initialStatus?.set).toBe(false)
|
|
1063
|
-
expect(result.project?.initialStatus?.error).toContain('not found')
|
|
1064
|
-
})
|
|
1065
|
-
|
|
1066
|
-
it('should handle moveProjectItem failure for initial status', async () => {
|
|
1067
|
-
const github = createMockGitHub({
|
|
1068
|
-
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1069
|
-
projectId: 'PVT_1',
|
|
1070
|
-
projectTitle: 'Board',
|
|
1071
|
-
statusFieldId: 'FIELD_1',
|
|
1072
|
-
statusOptions: [{ id: 'OPT_BACKLOG', name: 'Backlog' }],
|
|
1073
|
-
columns: [],
|
|
1074
|
-
totalItems: 0,
|
|
1075
|
-
}),
|
|
1076
|
-
addProjectItem: vi.fn().mockResolvedValue({ success: true, itemId: 'PVTITEM_NEW' }),
|
|
1077
|
-
moveProjectItem: vi
|
|
1078
|
-
.fn()
|
|
1079
|
-
.mockResolvedValue({ success: false, error: 'Move failed' }),
|
|
1080
|
-
})
|
|
1081
|
-
|
|
1082
|
-
const result = (await callTool(
|
|
1083
|
-
'create_github_issue_with_entry',
|
|
1084
|
-
{
|
|
1085
|
-
title: 'Issue move fail',
|
|
1086
|
-
journal_content: 'Test',
|
|
1087
|
-
project_number: 1,
|
|
1088
|
-
initial_status: 'Backlog',
|
|
1089
|
-
},
|
|
1090
|
-
db,
|
|
1091
|
-
undefined,
|
|
1092
|
-
github
|
|
1093
|
-
)) as {
|
|
1094
|
-
success: boolean
|
|
1095
|
-
project?: { added: boolean; initialStatus?: { set: boolean; error: string } }
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
expect(result.success).toBe(true)
|
|
1099
|
-
expect(result.project?.initialStatus?.set).toBe(false)
|
|
1100
|
-
})
|
|
1101
|
-
|
|
1102
|
-
it('should handle createIssue returning null', async () => {
|
|
1103
|
-
const github = createMockGitHub({
|
|
1104
|
-
createIssue: vi.fn().mockResolvedValue(null),
|
|
1105
|
-
})
|
|
1106
|
-
|
|
1107
|
-
const result = (await callTool(
|
|
1108
|
-
'create_github_issue_with_entry',
|
|
1109
|
-
{ title: 'Will fail', journal_content: 'Test' },
|
|
1110
|
-
db,
|
|
1111
|
-
undefined,
|
|
1112
|
-
github
|
|
1113
|
-
)) as { error: string }
|
|
1114
|
-
|
|
1115
|
-
expect(result.error).toContain('Failed to create')
|
|
1116
|
-
})
|
|
1117
|
-
})
|
|
1118
|
-
|
|
1119
|
-
// ========================================================================
|
|
1120
|
-
// close_github_issue_with_entry - Kanban edge cases
|
|
1121
|
-
// ========================================================================
|
|
1122
|
-
|
|
1123
|
-
describe('close_github_issue_with_entry - Kanban edge cases', () => {
|
|
1124
|
-
it('should return kanban error when move_to_done with no project_number', async () => {
|
|
1125
|
-
const github = createMockGitHub()
|
|
1126
|
-
|
|
1127
|
-
const result = (await callTool(
|
|
1128
|
-
'close_github_issue_with_entry',
|
|
1129
|
-
{
|
|
1130
|
-
issue_number: 1,
|
|
1131
|
-
resolution_notes: 'Done!',
|
|
1132
|
-
move_to_done: true,
|
|
1133
|
-
// No project_number and no defaultProjectNumber
|
|
1134
|
-
},
|
|
1135
|
-
db,
|
|
1136
|
-
undefined,
|
|
1137
|
-
github
|
|
1138
|
-
)) as { success: boolean; kanban?: { moved: boolean; error: string } }
|
|
1139
|
-
|
|
1140
|
-
expect(result.success).toBe(true) // Issue still closes
|
|
1141
|
-
expect(result.kanban?.moved).toBe(false)
|
|
1142
|
-
expect(result.kanban?.error).toContain('project_number required')
|
|
1143
|
-
})
|
|
1144
|
-
|
|
1145
|
-
it('should handle kanban board not found', async () => {
|
|
1146
|
-
const github = createMockGitHub({
|
|
1147
|
-
getProjectKanban: vi.fn().mockResolvedValue(null),
|
|
1148
|
-
})
|
|
1149
|
-
|
|
1150
|
-
const result = (await callTool(
|
|
1151
|
-
'close_github_issue_with_entry',
|
|
1152
|
-
{
|
|
1153
|
-
issue_number: 1,
|
|
1154
|
-
resolution_notes: 'Done!',
|
|
1155
|
-
move_to_done: true,
|
|
1156
|
-
project_number: 999,
|
|
1157
|
-
},
|
|
1158
|
-
db,
|
|
1159
|
-
undefined,
|
|
1160
|
-
github
|
|
1161
|
-
)) as { success: boolean; kanban?: { moved: boolean; error: string } }
|
|
1162
|
-
|
|
1163
|
-
expect(result.success).toBe(true)
|
|
1164
|
-
expect(result.kanban?.moved).toBe(false)
|
|
1165
|
-
expect(result.kanban?.error).toContain('not found')
|
|
1166
|
-
})
|
|
1167
|
-
|
|
1168
|
-
it('should handle addProjectItem failure during move_to_done', async () => {
|
|
1169
|
-
const github = createMockGitHub({
|
|
1170
|
-
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1171
|
-
projectId: 'PVT_1',
|
|
1172
|
-
projectTitle: 'Board',
|
|
1173
|
-
statusFieldId: 'FIELD_1',
|
|
1174
|
-
statusOptions: [{ id: 'OPT_DONE', name: 'Done' }],
|
|
1175
|
-
columns: [],
|
|
1176
|
-
totalItems: 0,
|
|
1177
|
-
}),
|
|
1178
|
-
addProjectItem: vi
|
|
1179
|
-
.fn()
|
|
1180
|
-
.mockResolvedValue({ success: false, error: 'Item add failed' }),
|
|
1181
|
-
})
|
|
1182
|
-
|
|
1183
|
-
const result = (await callTool(
|
|
1184
|
-
'close_github_issue_with_entry',
|
|
1185
|
-
{
|
|
1186
|
-
issue_number: 1,
|
|
1187
|
-
resolution_notes: 'Done!',
|
|
1188
|
-
move_to_done: true,
|
|
1189
|
-
project_number: 1,
|
|
1190
|
-
},
|
|
1191
|
-
db,
|
|
1192
|
-
undefined,
|
|
1193
|
-
github
|
|
1194
|
-
)) as { success: boolean; kanban?: { moved: boolean; error: string } }
|
|
1195
|
-
|
|
1196
|
-
expect(result.success).toBe(true)
|
|
1197
|
-
expect(result.kanban?.moved).toBe(false)
|
|
1198
|
-
expect(result.kanban?.error).toContain('Item add failed')
|
|
1199
|
-
})
|
|
1200
|
-
|
|
1201
|
-
it('should handle "Done" column not found on board', async () => {
|
|
1202
|
-
const github = createMockGitHub({
|
|
1203
|
-
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1204
|
-
projectId: 'PVT_1',
|
|
1205
|
-
projectTitle: 'Board',
|
|
1206
|
-
statusFieldId: 'FIELD_1',
|
|
1207
|
-
statusOptions: [{ id: 'OPT_TODO', name: 'Todo' }], // No "Done" status
|
|
1208
|
-
columns: [
|
|
1209
|
-
{
|
|
1210
|
-
status: 'Todo',
|
|
1211
|
-
items: [
|
|
1212
|
-
{
|
|
1213
|
-
id: 'PVTITEM_ISSUE1',
|
|
1214
|
-
title: 'Test Issue',
|
|
1215
|
-
type: 'ISSUE',
|
|
1216
|
-
number: 1,
|
|
1217
|
-
},
|
|
1218
|
-
],
|
|
1219
|
-
},
|
|
1220
|
-
],
|
|
1221
|
-
totalItems: 1,
|
|
1222
|
-
}),
|
|
1223
|
-
})
|
|
1224
|
-
|
|
1225
|
-
const result = (await callTool(
|
|
1226
|
-
'close_github_issue_with_entry',
|
|
1227
|
-
{
|
|
1228
|
-
issue_number: 1,
|
|
1229
|
-
resolution_notes: 'Done!',
|
|
1230
|
-
move_to_done: true,
|
|
1231
|
-
project_number: 1,
|
|
1232
|
-
},
|
|
1233
|
-
db,
|
|
1234
|
-
undefined,
|
|
1235
|
-
github
|
|
1236
|
-
)) as { success: boolean; kanban?: { moved: boolean; error: string } }
|
|
1237
|
-
|
|
1238
|
-
expect(result.success).toBe(true)
|
|
1239
|
-
expect(result.kanban?.moved).toBe(false)
|
|
1240
|
-
expect(result.kanban?.error).toContain('"Done" status column not found')
|
|
1241
|
-
})
|
|
1242
|
-
|
|
1243
|
-
it('should handle closeIssue returning null (API failure)', async () => {
|
|
1244
|
-
const github = createMockGitHub({
|
|
1245
|
-
closeIssue: vi.fn().mockResolvedValue(null),
|
|
1246
|
-
})
|
|
1247
|
-
|
|
1248
|
-
const result = (await callTool(
|
|
1249
|
-
'close_github_issue_with_entry',
|
|
1250
|
-
{ issue_number: 1, resolution_notes: 'Will fail' },
|
|
1251
|
-
db,
|
|
1252
|
-
undefined,
|
|
1253
|
-
github
|
|
1254
|
-
)) as { error: string }
|
|
1255
|
-
|
|
1256
|
-
expect(result.error).toContain('Failed to close')
|
|
1257
|
-
})
|
|
1258
|
-
|
|
1259
|
-
it('should handle no-repo detection on close', async () => {
|
|
1260
|
-
const github = createMockGitHub({
|
|
1261
|
-
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1262
|
-
owner: null,
|
|
1263
|
-
repo: null,
|
|
1264
|
-
branch: null,
|
|
1265
|
-
}),
|
|
1266
|
-
})
|
|
1267
|
-
|
|
1268
|
-
const result = (await callTool(
|
|
1269
|
-
'close_github_issue_with_entry',
|
|
1270
|
-
{ issue_number: 1 },
|
|
1271
|
-
db,
|
|
1272
|
-
undefined,
|
|
1273
|
-
github
|
|
1274
|
-
)) as { error: string; requiresUserInput: boolean }
|
|
1275
|
-
|
|
1276
|
-
expect(result.error).toContain('Could not auto-detect')
|
|
1277
|
-
expect(result.requiresUserInput).toBe(true)
|
|
1278
|
-
})
|
|
1279
|
-
})
|
|
1280
|
-
|
|
1281
|
-
// ========================================================================
|
|
1282
|
-
// Milestone tools - no-github and no-repo error paths
|
|
1283
|
-
// ========================================================================
|
|
1284
|
-
|
|
1285
|
-
describe('milestone tools - additional error paths', () => {
|
|
1286
|
-
it('get_github_milestone should return error when no github', async () => {
|
|
1287
|
-
const result = (await callTool(
|
|
1288
|
-
'get_github_milestone',
|
|
1289
|
-
{ milestone_number: 1 },
|
|
1290
|
-
db,
|
|
1291
|
-
undefined,
|
|
1292
|
-
undefined
|
|
1293
|
-
)) as { error: string }
|
|
1294
|
-
|
|
1295
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
1296
|
-
})
|
|
1297
|
-
|
|
1298
|
-
it('get_github_milestone should return error when no repo detected', async () => {
|
|
1299
|
-
const github = createMockGitHub({
|
|
1300
|
-
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1301
|
-
owner: null,
|
|
1302
|
-
repo: null,
|
|
1303
|
-
branch: null,
|
|
1304
|
-
}),
|
|
1305
|
-
})
|
|
1306
|
-
|
|
1307
|
-
const result = (await callTool(
|
|
1308
|
-
'get_github_milestone',
|
|
1309
|
-
{ milestone_number: 1 },
|
|
1310
|
-
db,
|
|
1311
|
-
undefined,
|
|
1312
|
-
github
|
|
1313
|
-
)) as { error: string; requiresUserInput: boolean }
|
|
1314
|
-
|
|
1315
|
-
expect(result.error).toContain('Could not auto-detect')
|
|
1316
|
-
expect(result.requiresUserInput).toBe(true)
|
|
1317
|
-
})
|
|
1318
|
-
|
|
1319
|
-
it('create_github_milestone should return error when no repo detected', async () => {
|
|
1320
|
-
const github = createMockGitHub({
|
|
1321
|
-
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1322
|
-
owner: null,
|
|
1323
|
-
repo: null,
|
|
1324
|
-
branch: null,
|
|
1325
|
-
}),
|
|
1326
|
-
})
|
|
1327
|
-
|
|
1328
|
-
const result = (await callTool(
|
|
1329
|
-
'create_github_milestone',
|
|
1330
|
-
{ title: 'No repo' },
|
|
1331
|
-
db,
|
|
1332
|
-
undefined,
|
|
1333
|
-
github
|
|
1334
|
-
)) as { error: string; requiresUserInput: boolean }
|
|
1335
|
-
|
|
1336
|
-
expect(result.error).toContain('Could not auto-detect')
|
|
1337
|
-
expect(result.requiresUserInput).toBe(true)
|
|
1338
|
-
})
|
|
1339
|
-
|
|
1340
|
-
it('create_github_milestone should return error when no github', async () => {
|
|
1341
|
-
const result = (await callTool(
|
|
1342
|
-
'create_github_milestone',
|
|
1343
|
-
{ title: 'No github' },
|
|
1344
|
-
db,
|
|
1345
|
-
undefined,
|
|
1346
|
-
undefined
|
|
1347
|
-
)) as { error: string }
|
|
1348
|
-
|
|
1349
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
1350
|
-
})
|
|
1351
|
-
|
|
1352
|
-
it('update_github_milestone should return error when no github', async () => {
|
|
1353
|
-
const result = (await callTool(
|
|
1354
|
-
'update_github_milestone',
|
|
1355
|
-
{ milestone_number: 1, title: 'No github' },
|
|
1356
|
-
db,
|
|
1357
|
-
undefined,
|
|
1358
|
-
undefined
|
|
1359
|
-
)) as { error: string }
|
|
1360
|
-
|
|
1361
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
1362
|
-
})
|
|
1363
|
-
|
|
1364
|
-
it('update_github_milestone should return error when no repo detected', async () => {
|
|
1365
|
-
const github = createMockGitHub({
|
|
1366
|
-
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1367
|
-
owner: null,
|
|
1368
|
-
repo: null,
|
|
1369
|
-
branch: null,
|
|
1370
|
-
}),
|
|
1371
|
-
})
|
|
1372
|
-
|
|
1373
|
-
const result = (await callTool(
|
|
1374
|
-
'update_github_milestone',
|
|
1375
|
-
{ milestone_number: 1, title: 'No repo' },
|
|
1376
|
-
db,
|
|
1377
|
-
undefined,
|
|
1378
|
-
github
|
|
1379
|
-
)) as { error: string; requiresUserInput: boolean }
|
|
1380
|
-
|
|
1381
|
-
expect(result.error).toContain('Could not auto-detect')
|
|
1382
|
-
expect(result.requiresUserInput).toBe(true)
|
|
1383
|
-
})
|
|
1384
|
-
|
|
1385
|
-
it('delete_github_milestone should return error when no github', async () => {
|
|
1386
|
-
const result = (await callTool(
|
|
1387
|
-
'delete_github_milestone',
|
|
1388
|
-
{ milestone_number: 1, confirm: true },
|
|
1389
|
-
db,
|
|
1390
|
-
undefined,
|
|
1391
|
-
undefined
|
|
1392
|
-
)) as { error: string }
|
|
1393
|
-
|
|
1394
|
-
expect(result.error).toContain('GitHub integration not available')
|
|
1395
|
-
})
|
|
1396
|
-
|
|
1397
|
-
it('delete_github_milestone should return error when no repo detected', async () => {
|
|
1398
|
-
const github = createMockGitHub({
|
|
1399
|
-
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1400
|
-
owner: null,
|
|
1401
|
-
repo: null,
|
|
1402
|
-
branch: null,
|
|
1403
|
-
}),
|
|
1404
|
-
})
|
|
1405
|
-
|
|
1406
|
-
const result = (await callTool(
|
|
1407
|
-
'delete_github_milestone',
|
|
1408
|
-
{ milestone_number: 1, confirm: true },
|
|
1409
|
-
db,
|
|
1410
|
-
undefined,
|
|
1411
|
-
github
|
|
1412
|
-
)) as { error: string; requiresUserInput: boolean }
|
|
1413
|
-
|
|
1414
|
-
expect(result.error).toContain('Could not auto-detect')
|
|
1415
|
-
expect(result.requiresUserInput).toBe(true)
|
|
1416
|
-
})
|
|
1417
|
-
})
|
|
1418
|
-
|
|
1419
|
-
// ========================================================================
|
|
1420
|
-
// restore_backup
|
|
1421
|
-
// ========================================================================
|
|
1422
|
-
|
|
1423
|
-
describe('restore_backup', () => {
|
|
1424
|
-
it('should restore from a backup file', async () => {
|
|
1425
|
-
// First create a backup to restore from
|
|
1426
|
-
const backupResult = (await callTool(
|
|
1427
|
-
'backup_journal',
|
|
1428
|
-
{ name: 'restore-test' },
|
|
1429
|
-
db
|
|
1430
|
-
)) as { success: boolean; filename: string }
|
|
1431
|
-
|
|
1432
|
-
expect(backupResult.success).toBe(true)
|
|
1433
|
-
|
|
1434
|
-
const result = (await callTool(
|
|
1435
|
-
'restore_backup',
|
|
1436
|
-
{ filename: backupResult.filename, confirm: true },
|
|
1437
|
-
db
|
|
1438
|
-
)) as {
|
|
1439
|
-
success: boolean
|
|
1440
|
-
message: string
|
|
1441
|
-
restoredFrom: string
|
|
1442
|
-
previousEntryCount: number
|
|
1443
|
-
newEntryCount: number
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
expect(result.success).toBe(true)
|
|
1447
|
-
expect(result.message).toContain('restored')
|
|
1448
|
-
expect(typeof result.previousEntryCount).toBe('number')
|
|
1449
|
-
expect(typeof result.newEntryCount).toBe('number')
|
|
1450
|
-
})
|
|
1451
|
-
})
|
|
1452
|
-
})
|