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,690 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DatabaseAdapter Tests
|
|
3
|
-
*
|
|
4
|
-
* Functional tests for database adapter methods not covered by
|
|
5
|
-
* tests/security/sql-injection.test.ts.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
9
|
-
import { DatabaseAdapter } from '../../src/database/sqlite-adapter/index.js'
|
|
10
|
-
import type { RelationshipType } from '../../src/types/index.js'
|
|
11
|
-
|
|
12
|
-
describe('DatabaseAdapter', () => {
|
|
13
|
-
let db: DatabaseAdapter
|
|
14
|
-
const testDbPath = './test-adapter.db'
|
|
15
|
-
|
|
16
|
-
beforeAll(async () => {
|
|
17
|
-
db = new DatabaseAdapter(testDbPath)
|
|
18
|
-
await db.initialize()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
afterAll(() => {
|
|
22
|
-
db.close()
|
|
23
|
-
try {
|
|
24
|
-
const fs = require('node:fs')
|
|
25
|
-
if (fs.existsSync(testDbPath)) {
|
|
26
|
-
fs.unlinkSync(testDbPath)
|
|
27
|
-
}
|
|
28
|
-
} catch {
|
|
29
|
-
// Ignore cleanup errors
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
// ========================================================================
|
|
34
|
-
// Initialize
|
|
35
|
-
// ========================================================================
|
|
36
|
-
|
|
37
|
-
describe('initialize', () => {
|
|
38
|
-
it('should be idempotent on re-init', async () => {
|
|
39
|
-
// Second init should not throw
|
|
40
|
-
await db.initialize()
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('should throw when accessing uninitalized db', async () => {
|
|
44
|
-
const uninit = new DatabaseAdapter('./uninit-test.db')
|
|
45
|
-
expect(() => uninit.getActiveEntryCount()).toThrow('Database not initialized')
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
// ========================================================================
|
|
50
|
-
// Entry CRUD
|
|
51
|
-
// ========================================================================
|
|
52
|
-
|
|
53
|
-
describe('createEntry', () => {
|
|
54
|
-
it('should create an entry with defaults', () => {
|
|
55
|
-
const entry = db.createEntry({ content: 'Test entry' })
|
|
56
|
-
|
|
57
|
-
expect(entry.id).toBeGreaterThan(0)
|
|
58
|
-
expect(entry.content).toBe('Test entry')
|
|
59
|
-
expect(entry.entryType).toBe('personal_reflection')
|
|
60
|
-
expect(entry.isPersonal).toBe(true)
|
|
61
|
-
expect(entry.tags).toEqual([])
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('should create an entry with all fields', () => {
|
|
65
|
-
const entry = db.createEntry({
|
|
66
|
-
content: 'Full entry',
|
|
67
|
-
entryType: 'project_decision',
|
|
68
|
-
tags: ['tag-a', 'tag-b'],
|
|
69
|
-
isPersonal: false,
|
|
70
|
-
significanceType: 'milestone',
|
|
71
|
-
autoContext: 'test-context',
|
|
72
|
-
projectNumber: 42,
|
|
73
|
-
issueNumber: 7,
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
expect(entry.entryType).toBe('project_decision')
|
|
77
|
-
expect(entry.isPersonal).toBe(false)
|
|
78
|
-
expect(entry.tags).toContain('tag-a')
|
|
79
|
-
expect(entry.tags).toContain('tag-b')
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe('getEntryById', () => {
|
|
84
|
-
it('should return entry by ID', () => {
|
|
85
|
-
const created = db.createEntry({ content: 'Find me' })
|
|
86
|
-
const found = db.getEntryById(created.id)
|
|
87
|
-
|
|
88
|
-
expect(found).not.toBeNull()
|
|
89
|
-
expect(found?.content).toBe('Find me')
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('should return null for nonexistent ID', () => {
|
|
93
|
-
expect(db.getEntryById(99999)).toBeNull()
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('should exclude soft-deleted entries', () => {
|
|
97
|
-
const entry = db.createEntry({ content: 'Will be deleted' })
|
|
98
|
-
db.deleteEntry(entry.id)
|
|
99
|
-
|
|
100
|
-
expect(db.getEntryById(entry.id)).toBeNull()
|
|
101
|
-
})
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
describe('getEntryByIdIncludeDeleted', () => {
|
|
105
|
-
it('should return soft-deleted entries', () => {
|
|
106
|
-
const entry = db.createEntry({ content: 'Soft deleted' })
|
|
107
|
-
db.deleteEntry(entry.id)
|
|
108
|
-
|
|
109
|
-
const found = db.getEntryByIdIncludeDeleted(entry.id)
|
|
110
|
-
expect(found).not.toBeNull()
|
|
111
|
-
expect(found?.content).toBe('Soft deleted')
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('should return null for nonexistent ID', () => {
|
|
115
|
-
expect(db.getEntryByIdIncludeDeleted(99999)).toBeNull()
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
// ========================================================================
|
|
120
|
-
// calculateImportance
|
|
121
|
-
// ========================================================================
|
|
122
|
-
|
|
123
|
-
describe('calculateImportance', () => {
|
|
124
|
-
it('should return 0 for nonexistent entry', () => {
|
|
125
|
-
const result = db.calculateImportance(99999)
|
|
126
|
-
expect(result.score).toBe(0)
|
|
127
|
-
expect(result.breakdown.significance).toBe(0)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('should include recency component for fresh entries', () => {
|
|
131
|
-
const entry = db.createEntry({ content: 'Fresh entry' })
|
|
132
|
-
const result = db.calculateImportance(entry.id)
|
|
133
|
-
|
|
134
|
-
// Fresh entry should have non-zero recency
|
|
135
|
-
expect(result.breakdown.recency).toBeGreaterThan(0)
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
it('should include significance component when set', () => {
|
|
139
|
-
const entry = db.createEntry({
|
|
140
|
-
content: 'Important entry',
|
|
141
|
-
significanceType: 'milestone',
|
|
142
|
-
})
|
|
143
|
-
const result = db.calculateImportance(entry.id)
|
|
144
|
-
|
|
145
|
-
expect(result.breakdown.significance).toBe(0.3)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
it('should include relationship component', () => {
|
|
149
|
-
const entry1 = db.createEntry({ content: 'Entry A' })
|
|
150
|
-
const entry2 = db.createEntry({ content: 'Entry B' })
|
|
151
|
-
db.linkEntries(entry1.id, entry2.id, 'references')
|
|
152
|
-
|
|
153
|
-
const result = db.calculateImportance(entry1.id)
|
|
154
|
-
expect(result.breakdown.relationships).toBeGreaterThan(0)
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('should include causal component for causal relationships', () => {
|
|
158
|
-
const entry1 = db.createEntry({ content: 'Blocker' })
|
|
159
|
-
const entry2 = db.createEntry({ content: 'Resolution' })
|
|
160
|
-
db.linkEntries(entry1.id, entry2.id, 'blocked_by')
|
|
161
|
-
|
|
162
|
-
const result = db.calculateImportance(entry1.id)
|
|
163
|
-
expect(result.breakdown.causal).toBeGreaterThan(0)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it('should have score between 0 and 1', () => {
|
|
167
|
-
const entry = db.createEntry({
|
|
168
|
-
content: 'Scored entry',
|
|
169
|
-
significanceType: 'milestone',
|
|
170
|
-
})
|
|
171
|
-
const result = db.calculateImportance(entry.id)
|
|
172
|
-
|
|
173
|
-
expect(result.score).toBeGreaterThanOrEqual(0)
|
|
174
|
-
expect(result.score).toBeLessThanOrEqual(1)
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
// ========================================================================
|
|
179
|
-
// getRecentEntries / pagination
|
|
180
|
-
// ========================================================================
|
|
181
|
-
|
|
182
|
-
describe('getRecentEntries', () => {
|
|
183
|
-
it('should respect limit', () => {
|
|
184
|
-
const entries = db.getRecentEntries(2)
|
|
185
|
-
expect(entries.length).toBeLessThanOrEqual(2)
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
it('should filter by isPersonal', () => {
|
|
189
|
-
db.createEntry({ content: 'Personal entry', isPersonal: true })
|
|
190
|
-
db.createEntry({ content: 'Non-personal entry', isPersonal: false })
|
|
191
|
-
|
|
192
|
-
const personal = db.getRecentEntries(100, true)
|
|
193
|
-
const nonPersonal = db.getRecentEntries(100, false)
|
|
194
|
-
|
|
195
|
-
expect(personal.every((e) => e.isPersonal)).toBe(true)
|
|
196
|
-
expect(nonPersonal.every((e) => !e.isPersonal)).toBe(true)
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
describe('getEntriesPage / getActiveEntryCount', () => {
|
|
201
|
-
it('should return correct active count', () => {
|
|
202
|
-
const count = db.getActiveEntryCount()
|
|
203
|
-
expect(count).toBeGreaterThan(0)
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
it('should paginate through entries', () => {
|
|
207
|
-
const page1 = db.getEntriesPage(0, 2)
|
|
208
|
-
const page2 = db.getEntriesPage(2, 2)
|
|
209
|
-
|
|
210
|
-
expect(page1.length).toBeLessThanOrEqual(2)
|
|
211
|
-
// Pages should not overlap (different IDs)
|
|
212
|
-
if (page2.length > 0) {
|
|
213
|
-
expect(page1[0]?.id).not.toBe(page2[0]?.id)
|
|
214
|
-
}
|
|
215
|
-
})
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
// ========================================================================
|
|
219
|
-
// deleteEntry
|
|
220
|
-
// ========================================================================
|
|
221
|
-
|
|
222
|
-
describe('deleteEntry', () => {
|
|
223
|
-
it('should soft delete an entry', () => {
|
|
224
|
-
const entry = db.createEntry({ content: 'To soft delete' })
|
|
225
|
-
const result = db.deleteEntry(entry.id)
|
|
226
|
-
|
|
227
|
-
expect(result).toBe(true)
|
|
228
|
-
expect(db.getEntryById(entry.id)).toBeNull()
|
|
229
|
-
expect(db.getEntryByIdIncludeDeleted(entry.id)).not.toBeNull()
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
it('should permanently delete an entry', () => {
|
|
233
|
-
const entry = db.createEntry({ content: 'To permanently delete' })
|
|
234
|
-
const result = db.deleteEntry(entry.id, true)
|
|
235
|
-
|
|
236
|
-
expect(result).toBe(true)
|
|
237
|
-
expect(db.getEntryByIdIncludeDeleted(entry.id)).toBeNull()
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
it('should return false for nonexistent entry', () => {
|
|
241
|
-
expect(db.deleteEntry(99999)).toBe(false)
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
it('should permanently delete a soft-deleted entry', () => {
|
|
245
|
-
const entry = db.createEntry({ content: 'Soft then hard' })
|
|
246
|
-
db.deleteEntry(entry.id) // soft delete
|
|
247
|
-
const result = db.deleteEntry(entry.id, true) // permanent delete
|
|
248
|
-
|
|
249
|
-
expect(result).toBe(true)
|
|
250
|
-
expect(db.getEntryByIdIncludeDeleted(entry.id)).toBeNull()
|
|
251
|
-
})
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
// ========================================================================
|
|
255
|
-
// Search
|
|
256
|
-
// ========================================================================
|
|
257
|
-
|
|
258
|
-
describe('searchEntries', () => {
|
|
259
|
-
it('should find entries by content', () => {
|
|
260
|
-
db.createEntry({ content: 'Unique search term xyz123' })
|
|
261
|
-
const results = db.searchEntries('xyz123')
|
|
262
|
-
|
|
263
|
-
expect(results.length).toBeGreaterThan(0)
|
|
264
|
-
expect(results[0]?.content).toContain('xyz123')
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
it('should respect limit', () => {
|
|
268
|
-
const results = db.searchEntries('entry', { limit: 1 })
|
|
269
|
-
expect(results.length).toBeLessThanOrEqual(1)
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
it('should filter by projectNumber', () => {
|
|
273
|
-
db.createEntry({ content: 'Project specific qwer', projectNumber: 99 })
|
|
274
|
-
const results = db.searchEntries('qwer', { projectNumber: 99 })
|
|
275
|
-
|
|
276
|
-
expect(results.length).toBeGreaterThan(0)
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
it('should return empty for non-matching query', () => {
|
|
280
|
-
const results = db.searchEntries('nonexistent_term_that_has_no_matches')
|
|
281
|
-
expect(results).toEqual([])
|
|
282
|
-
})
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
describe('searchByDateRange', () => {
|
|
286
|
-
it('should find entries within date range', () => {
|
|
287
|
-
db.createEntry({ content: 'Date range entry' })
|
|
288
|
-
const now = new Date()
|
|
289
|
-
const start = now.toISOString().split('T')[0]!
|
|
290
|
-
const end = start
|
|
291
|
-
|
|
292
|
-
const results = db.searchByDateRange(start, end)
|
|
293
|
-
expect(results.length).toBeGreaterThan(0)
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
it('should return empty for future date range', () => {
|
|
297
|
-
const results = db.searchByDateRange('2099-01-01', '2099-12-31')
|
|
298
|
-
expect(results).toEqual([])
|
|
299
|
-
})
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
// ========================================================================
|
|
303
|
-
// Relationships
|
|
304
|
-
// ========================================================================
|
|
305
|
-
|
|
306
|
-
describe('linkEntries / getRelationships', () => {
|
|
307
|
-
it('should create and retrieve a relationship', () => {
|
|
308
|
-
const e1 = db.createEntry({ content: 'Rel source' })
|
|
309
|
-
const e2 = db.createEntry({ content: 'Rel target' })
|
|
310
|
-
|
|
311
|
-
const rel = db.linkEntries(e1.id, e2.id, 'references', 'Test link')
|
|
312
|
-
|
|
313
|
-
expect(rel.id).toBeGreaterThan(0)
|
|
314
|
-
expect(rel.relationshipType).toBe('references')
|
|
315
|
-
expect(rel.description).toBe('Test link')
|
|
316
|
-
})
|
|
317
|
-
|
|
318
|
-
it('should retrieve relationships for an entry', () => {
|
|
319
|
-
const e1 = db.createEntry({ content: 'Has rels' })
|
|
320
|
-
const e2 = db.createEntry({ content: 'Also has rels' })
|
|
321
|
-
db.linkEntries(e1.id, e2.id, 'evolves_from')
|
|
322
|
-
|
|
323
|
-
const rels = db.getRelationships(e1.id)
|
|
324
|
-
expect(rels.length).toBeGreaterThan(0)
|
|
325
|
-
expect(rels.some((r) => r.relationshipType === 'evolves_from')).toBe(true)
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
it('should throw for nonexistent source entry', () => {
|
|
329
|
-
const e2 = db.createEntry({ content: 'Target exists' })
|
|
330
|
-
expect(() => db.linkEntries(99999, e2.id, 'references')).toThrow()
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
it('should throw for nonexistent target entry', () => {
|
|
334
|
-
const e1 = db.createEntry({ content: 'Source exists' })
|
|
335
|
-
expect(() => db.linkEntries(e1.id, 99999, 'references')).toThrow()
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
it('should support all relationship types', () => {
|
|
339
|
-
const types: RelationshipType[] = [
|
|
340
|
-
'evolves_from',
|
|
341
|
-
'references',
|
|
342
|
-
'implements',
|
|
343
|
-
'blocked_by',
|
|
344
|
-
'resolved',
|
|
345
|
-
'caused',
|
|
346
|
-
]
|
|
347
|
-
|
|
348
|
-
for (const type of types) {
|
|
349
|
-
const e1 = db.createEntry({ content: `From ${type}` })
|
|
350
|
-
const e2 = db.createEntry({ content: `To ${type}` })
|
|
351
|
-
const rel = db.linkEntries(e1.id, e2.id, type)
|
|
352
|
-
expect(rel.relationshipType).toBe(type)
|
|
353
|
-
}
|
|
354
|
-
})
|
|
355
|
-
})
|
|
356
|
-
|
|
357
|
-
// ========================================================================
|
|
358
|
-
// Tags
|
|
359
|
-
// ========================================================================
|
|
360
|
-
|
|
361
|
-
describe('tag operations', () => {
|
|
362
|
-
it('should list tags with usage', () => {
|
|
363
|
-
db.createEntry({ content: 'Tagged', tags: ['unique-tag-abc'] })
|
|
364
|
-
const tags = db.listTags()
|
|
365
|
-
|
|
366
|
-
const found = tags.find((t) => t.name === 'unique-tag-abc')
|
|
367
|
-
expect(found).toBeDefined()
|
|
368
|
-
expect(found?.usageCount).toBeGreaterThan(0)
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
it('should get tags for an entry', () => {
|
|
372
|
-
const entry = db.createEntry({ content: 'Multi tag', tags: ['mt-1', 'mt-2'] })
|
|
373
|
-
const tags = db.getTagsForEntry(entry.id)
|
|
374
|
-
|
|
375
|
-
expect(tags).toContain('mt-1')
|
|
376
|
-
expect(tags).toContain('mt-2')
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
it('should merge tags', () => {
|
|
380
|
-
db.createEntry({ content: 'Merge source', tags: ['old-tag'] })
|
|
381
|
-
const result = db.mergeTags('old-tag', 'new-tag')
|
|
382
|
-
|
|
383
|
-
expect(result.sourceDeleted).toBe(true)
|
|
384
|
-
expect(result.entriesUpdated).toBeGreaterThanOrEqual(0)
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
it('should throw when merging nonexistent source tag', () => {
|
|
388
|
-
expect(() => db.mergeTags('nonexistent-tag-xyz', 'any-target')).toThrow(
|
|
389
|
-
'Tag not found: nonexistent-tag-xyz'
|
|
390
|
-
)
|
|
391
|
-
})
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
// ========================================================================
|
|
395
|
-
// Statistics
|
|
396
|
-
// ========================================================================
|
|
397
|
-
|
|
398
|
-
describe('getStatistics', () => {
|
|
399
|
-
it('should return statistics with day grouping', () => {
|
|
400
|
-
const stats = db.getStatistics('day')
|
|
401
|
-
|
|
402
|
-
expect(stats.totalEntries).toBeGreaterThan(0)
|
|
403
|
-
expect(stats.entriesByType).toBeDefined()
|
|
404
|
-
expect(stats.entriesByPeriod).toBeDefined()
|
|
405
|
-
})
|
|
406
|
-
|
|
407
|
-
it('should return statistics with week grouping', () => {
|
|
408
|
-
const stats = db.getStatistics('week')
|
|
409
|
-
expect(stats.totalEntries).toBeGreaterThan(0)
|
|
410
|
-
})
|
|
411
|
-
|
|
412
|
-
it('should return statistics with month grouping', () => {
|
|
413
|
-
const stats = db.getStatistics('month')
|
|
414
|
-
expect(stats.totalEntries).toBeGreaterThan(0)
|
|
415
|
-
})
|
|
416
|
-
|
|
417
|
-
it('should include causal metrics', () => {
|
|
418
|
-
const stats = db.getStatistics()
|
|
419
|
-
expect(stats.causalMetrics).toBeDefined()
|
|
420
|
-
expect(typeof stats.causalMetrics.blocked_by).toBe('number')
|
|
421
|
-
expect(typeof stats.causalMetrics.resolved).toBe('number')
|
|
422
|
-
expect(typeof stats.causalMetrics.caused).toBe('number')
|
|
423
|
-
})
|
|
424
|
-
|
|
425
|
-
it('should filter by date range', () => {
|
|
426
|
-
const allStats = db.getStatistics('day')
|
|
427
|
-
const today = new Date().toISOString().split('T')[0]!
|
|
428
|
-
const filteredStats = db.getStatistics('day', today, today)
|
|
429
|
-
|
|
430
|
-
expect(filteredStats.totalEntries).toBeLessThanOrEqual(allStats.totalEntries)
|
|
431
|
-
expect(filteredStats.dateRange).toBeDefined()
|
|
432
|
-
expect(filteredStats.dateRange!.startDate).toBe(today)
|
|
433
|
-
expect(filteredStats.dateRange!.endDate).toBe(today)
|
|
434
|
-
})
|
|
435
|
-
|
|
436
|
-
it('should return 0 entries for future date range', () => {
|
|
437
|
-
const stats = db.getStatistics('day', '2099-01-01', '2099-12-31')
|
|
438
|
-
|
|
439
|
-
expect(stats.totalEntries).toBe(0)
|
|
440
|
-
expect(stats.entriesByPeriod).toEqual([])
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
it('should not include dateRange when no dates provided', () => {
|
|
444
|
-
const stats = db.getStatistics('day')
|
|
445
|
-
expect(stats.dateRange).toBeUndefined()
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
it('should return project breakdown when requested', () => {
|
|
449
|
-
db.createEntry({ content: 'Stats project test', projectNumber: 555 })
|
|
450
|
-
const stats = db.getStatistics('day', undefined, undefined, true)
|
|
451
|
-
|
|
452
|
-
expect(stats.projectBreakdown).toBeDefined()
|
|
453
|
-
expect(Array.isArray(stats.projectBreakdown)).toBe(true)
|
|
454
|
-
const proj = stats.projectBreakdown!.find((p) => p.project_number === 555)
|
|
455
|
-
expect(proj).toBeDefined()
|
|
456
|
-
expect(proj!.entry_count).toBeGreaterThanOrEqual(1)
|
|
457
|
-
})
|
|
458
|
-
|
|
459
|
-
it('should not include projectBreakdown when not requested', () => {
|
|
460
|
-
const stats = db.getStatistics('day')
|
|
461
|
-
expect(stats.projectBreakdown).toBeUndefined()
|
|
462
|
-
})
|
|
463
|
-
})
|
|
464
|
-
|
|
465
|
-
// ========================================================================
|
|
466
|
-
// Health Status
|
|
467
|
-
// ========================================================================
|
|
468
|
-
|
|
469
|
-
describe('getHealthStatus', () => {
|
|
470
|
-
it('should return health status', () => {
|
|
471
|
-
const health = db.getHealthStatus()
|
|
472
|
-
|
|
473
|
-
expect(health.database.path).toBe(testDbPath)
|
|
474
|
-
expect(health.database.entryCount).toBeGreaterThan(0)
|
|
475
|
-
expect(typeof health.database.sizeBytes).toBe('number')
|
|
476
|
-
expect(typeof health.database.deletedEntryCount).toBe('number')
|
|
477
|
-
expect(typeof health.database.relationshipCount).toBe('number')
|
|
478
|
-
expect(typeof health.database.tagCount).toBe('number')
|
|
479
|
-
})
|
|
480
|
-
})
|
|
481
|
-
|
|
482
|
-
// ========================================================================
|
|
483
|
-
// Backup operations
|
|
484
|
-
// ========================================================================
|
|
485
|
-
|
|
486
|
-
describe('backup operations', () => {
|
|
487
|
-
it('should export to backup file', async () => {
|
|
488
|
-
const backup = await db.exportToFile('test-backup')
|
|
489
|
-
|
|
490
|
-
expect(backup.filename).toContain('test-backup')
|
|
491
|
-
expect(backup.sizeBytes).toBeGreaterThan(0)
|
|
492
|
-
|
|
493
|
-
// Cleanup
|
|
494
|
-
const fs = require('node:fs')
|
|
495
|
-
if (fs.existsSync(backup.path)) {
|
|
496
|
-
fs.unlinkSync(backup.path)
|
|
497
|
-
}
|
|
498
|
-
})
|
|
499
|
-
|
|
500
|
-
it('should list backup files', async () => {
|
|
501
|
-
const backup = await db.exportToFile('list-test')
|
|
502
|
-
const backups = db.listBackups()
|
|
503
|
-
|
|
504
|
-
expect(backups.length).toBeGreaterThan(0)
|
|
505
|
-
expect(backups.some((b) => b.filename.includes('list-test'))).toBe(true)
|
|
506
|
-
|
|
507
|
-
// Cleanup
|
|
508
|
-
const fs = require('node:fs')
|
|
509
|
-
if (fs.existsSync(backup.path)) {
|
|
510
|
-
fs.unlinkSync(backup.path)
|
|
511
|
-
}
|
|
512
|
-
})
|
|
513
|
-
|
|
514
|
-
it('should delete old backups keeping only keepCount', async () => {
|
|
515
|
-
const fs = require('node:fs')
|
|
516
|
-
|
|
517
|
-
// Clean up any pre-existing backups from other tests
|
|
518
|
-
const preExisting = db.listBackups()
|
|
519
|
-
for (const backup of preExisting) {
|
|
520
|
-
if (fs.existsSync(backup.path)) fs.unlinkSync(backup.path)
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// Create 3 backups
|
|
524
|
-
const b1 = await db.exportToFile('cleanup-1')
|
|
525
|
-
const b2 = await db.exportToFile('cleanup-2')
|
|
526
|
-
const b3 = await db.exportToFile('cleanup-3')
|
|
527
|
-
|
|
528
|
-
// Keep only 1 newest
|
|
529
|
-
db.deleteOldBackups(1)
|
|
530
|
-
|
|
531
|
-
const remaining = db.listBackups()
|
|
532
|
-
// Should have exactly 1 backup remaining (newest)
|
|
533
|
-
expect(remaining.length).toBe(1)
|
|
534
|
-
|
|
535
|
-
// Cleanup any remaining
|
|
536
|
-
for (const path of [b1.path, b2.path, b3.path]) {
|
|
537
|
-
if (fs.existsSync(path)) fs.unlinkSync(path)
|
|
538
|
-
}
|
|
539
|
-
})
|
|
540
|
-
|
|
541
|
-
it('should restore from a backup file', async () => {
|
|
542
|
-
const fs = require('node:fs')
|
|
543
|
-
// Create an entry and backup
|
|
544
|
-
db.createEntry({ content: 'Before restore test' })
|
|
545
|
-
const countBefore = db.getActiveEntryCount()
|
|
546
|
-
const backup = await db.exportToFile('restore-test')
|
|
547
|
-
|
|
548
|
-
// Create more entries after backup
|
|
549
|
-
db.createEntry({ content: 'After backup 1' })
|
|
550
|
-
db.createEntry({ content: 'After backup 2' })
|
|
551
|
-
const countAfterAdding = db.getActiveEntryCount()
|
|
552
|
-
expect(countAfterAdding).toBeGreaterThan(countBefore)
|
|
553
|
-
|
|
554
|
-
// Restore should revert to backup state
|
|
555
|
-
const result = await db.restoreFromFile(backup.filename)
|
|
556
|
-
expect(result.previousEntryCount).toBe(countAfterAdding)
|
|
557
|
-
expect(result.newEntryCount).toBe(countBefore)
|
|
558
|
-
|
|
559
|
-
// Cleanup
|
|
560
|
-
const backups = db.listBackups()
|
|
561
|
-
for (const b of backups) {
|
|
562
|
-
const path = require('node:path').join('backups', b.filename)
|
|
563
|
-
if (fs.existsSync(path)) fs.unlinkSync(path)
|
|
564
|
-
}
|
|
565
|
-
})
|
|
566
|
-
|
|
567
|
-
it('should get raw database handle', () => {
|
|
568
|
-
const rawDb = db.getRawDb()
|
|
569
|
-
expect(rawDb).toBeDefined()
|
|
570
|
-
expect(typeof rawDb.exec).toBe('function')
|
|
571
|
-
})
|
|
572
|
-
})
|
|
573
|
-
|
|
574
|
-
// ========================================================================
|
|
575
|
-
// Close
|
|
576
|
-
// ========================================================================
|
|
577
|
-
|
|
578
|
-
describe('close', () => {
|
|
579
|
-
it('should close without error', () => {
|
|
580
|
-
const tempDb = new DatabaseAdapter('./test-close.db')
|
|
581
|
-
// Close without init should not throw
|
|
582
|
-
tempDb.close()
|
|
583
|
-
})
|
|
584
|
-
})
|
|
585
|
-
|
|
586
|
-
// ========================================================================
|
|
587
|
-
// Additional branch coverage
|
|
588
|
-
// ========================================================================
|
|
589
|
-
|
|
590
|
-
describe('updateEntry', () => {
|
|
591
|
-
it('should update entry content', () => {
|
|
592
|
-
const entry = db.createEntry({ content: 'Original content' })
|
|
593
|
-
const updated = db.updateEntry(entry.id, { content: 'Updated content' })
|
|
594
|
-
|
|
595
|
-
expect(updated).not.toBeNull()
|
|
596
|
-
expect(updated?.content).toBe('Updated content')
|
|
597
|
-
})
|
|
598
|
-
|
|
599
|
-
it('should update tags', () => {
|
|
600
|
-
const entry = db.createEntry({ content: 'Tag update', tags: ['initial'] })
|
|
601
|
-
const updated = db.updateEntry(entry.id, { tags: ['updated-tag'] })
|
|
602
|
-
|
|
603
|
-
expect(updated).not.toBeNull()
|
|
604
|
-
const tags = db.getTagsForEntry(entry.id)
|
|
605
|
-
expect(tags).toContain('updated-tag')
|
|
606
|
-
})
|
|
607
|
-
|
|
608
|
-
it('should return null for nonexistent entry', () => {
|
|
609
|
-
const result = db.updateEntry(99999, { content: 'Nope' })
|
|
610
|
-
expect(result).toBeNull()
|
|
611
|
-
})
|
|
612
|
-
})
|
|
613
|
-
|
|
614
|
-
describe('searchEntries - advanced filters', () => {
|
|
615
|
-
it('should filter by issueNumber', () => {
|
|
616
|
-
db.createEntry({ content: 'Issue filter test', issueNumber: 888 })
|
|
617
|
-
const results = db.searchEntries('', { issueNumber: 888 })
|
|
618
|
-
expect(results.length).toBeGreaterThan(0)
|
|
619
|
-
})
|
|
620
|
-
|
|
621
|
-
it('should filter by prNumber', () => {
|
|
622
|
-
db.createEntry({ content: 'PR filter test', prNumber: 77 })
|
|
623
|
-
const results = db.searchEntries('', { prNumber: 77 })
|
|
624
|
-
expect(results.length).toBeGreaterThan(0)
|
|
625
|
-
})
|
|
626
|
-
|
|
627
|
-
it('should filter by isPersonal', () => {
|
|
628
|
-
const results = db.searchEntries('', { isPersonal: false })
|
|
629
|
-
expect(results.every((e) => !e.isPersonal)).toBe(true)
|
|
630
|
-
})
|
|
631
|
-
})
|
|
632
|
-
|
|
633
|
-
describe('searchByDateRange - with type filter', () => {
|
|
634
|
-
it('should filter by entry type', () => {
|
|
635
|
-
const today = new Date().toISOString().split('T')[0]!
|
|
636
|
-
const results = db.searchByDateRange(today, today, {
|
|
637
|
-
entryType: 'project_decision',
|
|
638
|
-
})
|
|
639
|
-
for (const r of results) {
|
|
640
|
-
expect(r.entryType).toBe('project_decision')
|
|
641
|
-
}
|
|
642
|
-
})
|
|
643
|
-
|
|
644
|
-
it('should filter by tags', () => {
|
|
645
|
-
db.createEntry({ content: 'Tag date range', tags: ['daterange-tag'] })
|
|
646
|
-
const today = new Date().toISOString().split('T')[0]!
|
|
647
|
-
const results = db.searchByDateRange(today, today, {
|
|
648
|
-
tags: ['daterange-tag'],
|
|
649
|
-
})
|
|
650
|
-
expect(results.length).toBeGreaterThan(0)
|
|
651
|
-
})
|
|
652
|
-
})
|
|
653
|
-
|
|
654
|
-
// ========================================================================
|
|
655
|
-
// Backup edge cases
|
|
656
|
-
// ========================================================================
|
|
657
|
-
|
|
658
|
-
describe('backup edge cases', () => {
|
|
659
|
-
it('should return empty array when backups directory does not exist', () => {
|
|
660
|
-
const fs = require('node:fs')
|
|
661
|
-
const isolatedDir = './test-server/test-isolation-dir'
|
|
662
|
-
if (!fs.existsSync(isolatedDir)) {
|
|
663
|
-
fs.mkdirSync(isolatedDir, { recursive: true })
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Use a fresh adapter in a unique directory so its 'backups' dir doesn't exist
|
|
667
|
-
const tempDb = new DatabaseAdapter(`${isolatedDir}/test-no-backups.db`)
|
|
668
|
-
tempDb.initialize()
|
|
669
|
-
|
|
670
|
-
const backups = tempDb.listBackups()
|
|
671
|
-
expect(backups).toEqual([])
|
|
672
|
-
|
|
673
|
-
tempDb.close()
|
|
674
|
-
// Cleanup
|
|
675
|
-
if (fs.existsSync(isolatedDir)) {
|
|
676
|
-
fs.rmSync(isolatedDir, { recursive: true, force: true })
|
|
677
|
-
}
|
|
678
|
-
})
|
|
679
|
-
|
|
680
|
-
it('should throw when deleteOldBackups keepCount is less than 1', () => {
|
|
681
|
-
expect(() => db.deleteOldBackups(0)).toThrow('keepCount must be at least 1')
|
|
682
|
-
})
|
|
683
|
-
|
|
684
|
-
it('should throw when restoring from non-existent backup file', async () => {
|
|
685
|
-
await expect(db.restoreFromFile('nonexistent-backup.db')).rejects.toThrow(
|
|
686
|
-
'Backup not found: nonexistent-backup.db'
|
|
687
|
-
)
|
|
688
|
-
})
|
|
689
|
-
})
|
|
690
|
-
})
|