jettypod 4.1.2 → 4.1.4
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/.nvmrc +1 -0
- package/docs/COMPLETE-TESTING-STRATEGY.md +970 -0
- package/docs/DECISIONS.md +10 -12
- package/docs/NODE_VERSION.md +83 -0
- package/docs/TDD-INFRASTRUCTURE-STRATEGY.md +1374 -0
- package/docs/TESTING-FOR-NON-ENGINEERS.md +1588 -0
- package/docs/TESTING-STRATEGY-AUDIT.md +698 -0
- package/hooks/post-checkout +17 -0
- package/hooks/post-merge +17 -0
- package/hooks/pre-commit +30 -0
- package/jettypod.js +259 -120
- package/lib/coverage-tracker.js +218 -0
- package/lib/database.js +2 -0
- package/lib/db-export.js +192 -0
- package/lib/db-import.js +193 -0
- package/lib/external-transition-handler.js +32 -0
- package/lib/git-hook-helpers.js +174 -0
- package/lib/git-root.js +90 -0
- package/lib/infrastructure-chore-generator.js +45 -0
- package/lib/install-hooks.js +52 -0
- package/lib/jettypod-backup.js +238 -0
- package/lib/merge-lock.js +193 -0
- package/lib/migrations/012-add-worktree-path.js +38 -0
- package/lib/migrations/013-worktrees-table.js +86 -0
- package/lib/migrations/014-migrate-worktree-data.js +161 -0
- package/lib/migrations/015-merge-locks-table.js +67 -0
- package/lib/pattern-finder.js +152 -0
- package/lib/process-manager.js +140 -0
- package/lib/production-standards-reader.js +13 -2
- package/lib/production-standards-writer.js +85 -0
- package/lib/skills/feature-planning/dry-run-validator.js +135 -0
- package/lib/skills/feature-planning/validation-formatter.js +160 -0
- package/lib/smart-conflict-detection.js +168 -0
- package/lib/smart-fetch-rebase.js +614 -0
- package/lib/step-definition-parser.js +76 -0
- package/lib/unit-test-generator.js +232 -0
- package/lib/verification-command-generator.js +66 -0
- package/lib/worktree-diagnostics.js +413 -0
- package/lib/worktree-facade.js +174 -0
- package/lib/worktree-manager.js +636 -0
- package/lib/worktree-reconciler.js +429 -0
- package/package.json +30 -3
- package/skills-templates/external-transition/SKILL.md +34 -3
- package/skills-templates/feature-planning/SKILL.md +190 -24
- package/skills-templates/production-mode/SKILL.md +127 -9
- package/skills-templates/speed-mode/SKILL.md +454 -51
- package/skills-templates/stable-mode/SKILL.md +285 -76
- package/.claude/PROTECT_SKILLS.md +0 -28
- package/.claude/settings.json +0 -24
- package/.claude/settings.local.json +0 -16
- package/.claude/skills/epic-planning/SKILL.md +0 -297
- package/.claude/skills/external-transition/SKILL.md +0 -384
- package/.claude/skills/feature-planning/SKILL.md +0 -464
- package/.claude/skills/production-mode/SKILL.md +0 -369
- package/.claude/skills/speed-mode/SKILL.md +0 -481
- package/.claude/skills/stable-mode/SKILL.md +0 -713
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/stable-mode/SKILL.md +0 -673
- package/.claude/skills.backup-2025-11-11T16-15-10-070Z/epic-discover/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/stable-mode/SKILL.md +0 -673
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/stable-mode/SKILL.md +0 -673
- package/.devpod/current-work.json +0 -10
- package/.devpod/work.db +0 -0
- package/.github/workflows/test-safety.yml +0 -85
- package/.jettypod/config.json +0 -5
- package/.jettypod/current-work.json +0 -10
- package/.jettypod/hooks/README.md +0 -77
- package/.jettypod/hooks/protect-claude-md.js +0 -338
- package/.jettypod/test-work.db +0 -0
- package/.jettypod/work.db +0 -0
- package/CLAUDE.md +0 -49
- package/SPEED-STABLE-AUDIT.md +0 -853
- package/SYSTEM-BEHAVIOR.md +0 -2199
- package/TEST_SAFETY_AUDIT.md +0 -314
- package/TEST_SAFETY_IMPLEMENTATION.md +0 -97
- package/cucumber-report.html +0 -45
- package/dist/devpod-linux +0 -0
- package/dist/devpod-macos +0 -0
- package/dist/devpod-win.exe +0 -0
- package/docs/features/jettypod-standards-explained.md +0 -543
- package/docs/features/standards-inventory.md +0 -257
- package/features/auto-generate-production-chores.feature +0 -13
- package/features/backlog-command.feature +0 -26
- package/features/backlog-filtering-production.feature +0 -10
- package/features/claude-md-protection/steps.js +0 -498
- package/features/decisions/index.js +0 -490
- package/features/decisions/index.test.js +0 -208
- package/features/fix-text-wrapping.feature +0 -42
- package/features/git-hooks/git-hooks.feature +0 -30
- package/features/git-hooks/index.js +0 -93
- package/features/git-hooks/index.test.js +0 -137
- package/features/git-hooks/post-commit +0 -56
- package/features/git-hooks/post-merge +0 -47
- package/features/git-hooks/pre-commit +0 -28
- package/features/git-hooks/simple-steps.js +0 -53
- package/features/git-hooks/simple-test.feature +0 -10
- package/features/git-hooks/steps.js +0 -196
- package/features/jettypod-update-command.feature +0 -46
- package/features/mode-prompts/index.js +0 -95
- package/features/mode-prompts/simple-steps.js +0 -44
- package/features/mode-prompts/simple-test.feature +0 -9
- package/features/mode-prompts/validation.test.js +0 -120
- package/features/multiple-claude-instances.feature +0 -121
- package/features/production-mode-skill.feature +0 -121
- package/features/refactor-mode/steps.js +0 -217
- package/features/refactor-mode.feature +0 -49
- package/features/simplify-external-transition.feature +0 -166
- package/features/skills-update/index.test.js +0 -216
- package/features/step_definitions/backlog-command.steps.js +0 -37
- package/features/step_definitions/fix-text-wrapping.steps.js +0 -271
- package/features/step_definitions/multiple-claude-instances.steps.js +0 -621
- package/features/step_definitions/production-mode-skill.steps.js +0 -862
- package/features/step_definitions/simplify-external-transition.steps.js +0 -370
- package/features/step_definitions/terminal-logo.steps.js +0 -145
- package/features/step_definitions/update-command.steps.js +0 -183
- package/features/support/hooks.js +0 -9
- package/features/terminal-logo/index.js +0 -39
- package/features/terminal-logo/terminal-logo.feature +0 -30
- package/features/update-command/index.js +0 -181
- package/features/update-command/index.test.js +0 -225
- package/features/work-commands/bug-workflow-display.feature +0 -22
- package/features/work-commands/index.js +0 -498
- package/features/work-commands/simple-steps.js +0 -69
- package/features/work-commands/stable-tests.feature +0 -57
- package/features/work-commands/steps.js +0 -1174
- package/features/work-commands/validation.test.js +0 -88
- package/features/work-commands/work-commands.feature +0 -13
- package/features/work-tracking/discovery-validation.test.js +0 -228
- package/features/work-tracking/index.js +0 -1921
- package/features/work-tracking/mode-required.feature +0 -112
- package/features/work-tracking/phase-tracking.test.js +0 -482
- package/features/work-tracking/prototype-tracking.test.js +0 -485
- package/features/work-tracking/tree-view.test.js +0 -310
- package/features/work-tracking/work-set-mode.feature +0 -71
- package/features/work-tracking/work-start-mode.feature +0 -88
- package/full-test.txt +0 -0
- package/lib/bug-workflow.test.js +0 -177
- package/lib/claudemd.test.js +0 -195
- package/lib/config.test.js +0 -511
- package/lib/constants.test.js +0 -164
- package/lib/current-work.test.js +0 -146
- package/lib/database-project-config.test.js +0 -111
- package/lib/database.test.js +0 -106
- package/lib/decisions-generator.test.js +0 -457
- package/lib/decisions-helpers.test.js +0 -310
- package/lib/git-coordinator.js +0 -167
- package/lib/git.test.js +0 -145
- package/lib/migrations/002-default-work-item-modes.test.js +0 -351
- package/lib/production-chore-generator.test.js +0 -432
- package/lib/production-context-detector.test.js +0 -277
- package/lib/production-scenario-appender.test.js +0 -235
- package/lib/production-scenario-validator.test.js +0 -246
- package/lib/production-standards-reader.test.js +0 -270
- package/lib/project-state.test.js +0 -92
- package/lib/push-queue.js +0 -417
- package/lib/queue-processor.js +0 -74
- package/lib/test-helpers.js +0 -202
- package/lib/test-helpers.test.js +0 -255
- package/prototypes/2025-01-11-production-mode-autonomous.js +0 -119
- package/prototypes/2025-01-11-production-mode-collaborative.js +0 -166
- package/prototypes/2025-01-11-production-mode-guided.js +0 -217
- package/prototypes/2025-01-11-production-mode-smart-context.js +0 -347
- package/prototypes/2025-01-11-production-standards-example.md +0 -204
- package/prototypes/2025-11-10-backlog-filtering-tree-aware.js +0 -242
- package/prototypes/test/index.html +0 -1
- package/setup-dist-repo.sh +0 -68
- package/test-production-standards-engine.js +0 -130
- package/test-results.json +0 -2195
- package/test-safety-check.sh +0 -80
- package/work-item-tracking-plan.md +0 -199
- /package/{.jettypod/devpod.db → jettypod.db} +0 -0
package/lib/constants.test.js
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
WORK_TYPES,
|
|
3
|
-
WORK_STATUSES,
|
|
4
|
-
TYPE_EMOJIS,
|
|
5
|
-
STATUS_EMOJIS,
|
|
6
|
-
VALID_STATUSES,
|
|
7
|
-
VALID_TYPES,
|
|
8
|
-
isValidType,
|
|
9
|
-
isValidStatus,
|
|
10
|
-
getTypeEmoji,
|
|
11
|
-
getStatusEmoji
|
|
12
|
-
} = require('./constants');
|
|
13
|
-
|
|
14
|
-
describe('Constants Module', () => {
|
|
15
|
-
describe('WORK_TYPES', () => {
|
|
16
|
-
test('should have all required types', () => {
|
|
17
|
-
expect(WORK_TYPES.EPIC).toBe('epic');
|
|
18
|
-
expect(WORK_TYPES.FEATURE).toBe('feature');
|
|
19
|
-
expect(WORK_TYPES.BUG).toBe('bug');
|
|
20
|
-
expect(WORK_TYPES.CHORE).toBe('chore');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('should be frozen (immutable)', () => {
|
|
24
|
-
expect(Object.isFrozen(WORK_TYPES)).toBe(true);
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('WORK_STATUSES', () => {
|
|
29
|
-
test('should have all required statuses', () => {
|
|
30
|
-
expect(WORK_STATUSES.BACKLOG).toBe('backlog');
|
|
31
|
-
expect(WORK_STATUSES.TODO).toBe('todo');
|
|
32
|
-
expect(WORK_STATUSES.IN_PROGRESS).toBe('in_progress');
|
|
33
|
-
expect(WORK_STATUSES.BLOCKED).toBe('blocked');
|
|
34
|
-
expect(WORK_STATUSES.DONE).toBe('done');
|
|
35
|
-
expect(WORK_STATUSES.CANCELLED).toBe('cancelled');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('should be frozen (immutable)', () => {
|
|
39
|
-
expect(Object.isFrozen(WORK_STATUSES)).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('TYPE_EMOJIS', () => {
|
|
44
|
-
test('should have emojis for all types', () => {
|
|
45
|
-
expect(TYPE_EMOJIS[WORK_TYPES.EPIC]).toBe('🎯');
|
|
46
|
-
expect(TYPE_EMOJIS[WORK_TYPES.FEATURE]).toBe('✨');
|
|
47
|
-
expect(TYPE_EMOJIS[WORK_TYPES.BUG]).toBe('🐛');
|
|
48
|
-
expect(TYPE_EMOJIS[WORK_TYPES.CHORE]).toBe('🔧');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('should be frozen (immutable)', () => {
|
|
52
|
-
expect(Object.isFrozen(TYPE_EMOJIS)).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('STATUS_EMOJIS', () => {
|
|
57
|
-
test('should have emojis for all statuses', () => {
|
|
58
|
-
expect(STATUS_EMOJIS[WORK_STATUSES.BACKLOG]).toBe('⏳');
|
|
59
|
-
expect(STATUS_EMOJIS[WORK_STATUSES.TODO]).toBe('📋');
|
|
60
|
-
expect(STATUS_EMOJIS[WORK_STATUSES.IN_PROGRESS]).toBe('🔄');
|
|
61
|
-
expect(STATUS_EMOJIS[WORK_STATUSES.DONE]).toBe('✅');
|
|
62
|
-
expect(STATUS_EMOJIS[WORK_STATUSES.CANCELLED]).toBe('❌');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('should be frozen (immutable)', () => {
|
|
66
|
-
expect(Object.isFrozen(STATUS_EMOJIS)).toBe(true);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('VALID_TYPES', () => {
|
|
71
|
-
test('should contain all type values', () => {
|
|
72
|
-
expect(VALID_TYPES).toHaveLength(4);
|
|
73
|
-
expect(VALID_TYPES).toContain('epic');
|
|
74
|
-
expect(VALID_TYPES).toContain('feature');
|
|
75
|
-
expect(VALID_TYPES).toContain('bug');
|
|
76
|
-
expect(VALID_TYPES).toContain('chore');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('should be frozen (immutable)', () => {
|
|
80
|
-
expect(Object.isFrozen(VALID_TYPES)).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('VALID_STATUSES', () => {
|
|
85
|
-
test('should contain all status values', () => {
|
|
86
|
-
expect(VALID_STATUSES).toHaveLength(6);
|
|
87
|
-
expect(VALID_STATUSES).toContain('backlog');
|
|
88
|
-
expect(VALID_STATUSES).toContain('todo');
|
|
89
|
-
expect(VALID_STATUSES).toContain('in_progress');
|
|
90
|
-
expect(VALID_STATUSES).toContain('blocked');
|
|
91
|
-
expect(VALID_STATUSES).toContain('done');
|
|
92
|
-
expect(VALID_STATUSES).toContain('cancelled');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('should be frozen (immutable)', () => {
|
|
96
|
-
expect(Object.isFrozen(VALID_STATUSES)).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
describe('isValidType()', () => {
|
|
101
|
-
test('should return true for valid types', () => {
|
|
102
|
-
expect(isValidType('epic')).toBe(true);
|
|
103
|
-
expect(isValidType('feature')).toBe(true);
|
|
104
|
-
expect(isValidType('bug')).toBe(true);
|
|
105
|
-
expect(isValidType('chore')).toBe(true);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test('should return false for invalid types', () => {
|
|
109
|
-
expect(isValidType('invalid')).toBe(false);
|
|
110
|
-
expect(isValidType('')).toBe(false);
|
|
111
|
-
expect(isValidType(null)).toBe(false);
|
|
112
|
-
expect(isValidType(undefined)).toBe(false);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe('isValidStatus()', () => {
|
|
117
|
-
test('should return true for valid statuses', () => {
|
|
118
|
-
expect(isValidStatus('backlog')).toBe(true);
|
|
119
|
-
expect(isValidStatus('todo')).toBe(true);
|
|
120
|
-
expect(isValidStatus('in_progress')).toBe(true);
|
|
121
|
-
expect(isValidStatus('blocked')).toBe(true);
|
|
122
|
-
expect(isValidStatus('done')).toBe(true);
|
|
123
|
-
expect(isValidStatus('cancelled')).toBe(true);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test('should return false for invalid statuses', () => {
|
|
127
|
-
expect(isValidStatus('invalid')).toBe(false);
|
|
128
|
-
expect(isValidStatus('')).toBe(false);
|
|
129
|
-
expect(isValidStatus(null)).toBe(false);
|
|
130
|
-
expect(isValidStatus(undefined)).toBe(false);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
describe('getTypeEmoji()', () => {
|
|
135
|
-
test('should return correct emoji for valid types', () => {
|
|
136
|
-
expect(getTypeEmoji('epic')).toBe('🎯');
|
|
137
|
-
expect(getTypeEmoji('feature')).toBe('✨');
|
|
138
|
-
expect(getTypeEmoji('bug')).toBe('🐛');
|
|
139
|
-
expect(getTypeEmoji('chore')).toBe('🔧');
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test('should return default emoji for invalid types', () => {
|
|
143
|
-
expect(getTypeEmoji('invalid')).toBe('📋');
|
|
144
|
-
expect(getTypeEmoji(null)).toBe('📋');
|
|
145
|
-
expect(getTypeEmoji(undefined)).toBe('📋');
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
describe('getStatusEmoji()', () => {
|
|
150
|
-
test('should return correct emoji for valid statuses', () => {
|
|
151
|
-
expect(getStatusEmoji('backlog')).toBe('⏳');
|
|
152
|
-
expect(getStatusEmoji('todo')).toBe('📋');
|
|
153
|
-
expect(getStatusEmoji('in_progress')).toBe('🔄');
|
|
154
|
-
expect(getStatusEmoji('done')).toBe('✅');
|
|
155
|
-
expect(getStatusEmoji('cancelled')).toBe('❌');
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
test('should return default emoji for invalid statuses', () => {
|
|
159
|
-
expect(getStatusEmoji('invalid')).toBe('❓');
|
|
160
|
-
expect(getStatusEmoji(null)).toBe('❓');
|
|
161
|
-
expect(getStatusEmoji(undefined)).toBe('❓');
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
});
|
package/lib/current-work.test.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { createTestEnvironment } = require('./test-helpers');
|
|
4
|
-
const {
|
|
5
|
-
getCurrentWork,
|
|
6
|
-
setCurrentWork,
|
|
7
|
-
clearCurrentWork,
|
|
8
|
-
getCurrentWorkPath
|
|
9
|
-
} = require('./current-work');
|
|
10
|
-
|
|
11
|
-
describe('Current Work Module', () => {
|
|
12
|
-
let testEnv;
|
|
13
|
-
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
testEnv = createTestEnvironment();
|
|
16
|
-
process.chdir(testEnv.testDir);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
testEnv.cleanup();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const validWorkItem = {
|
|
24
|
-
id: 1,
|
|
25
|
-
title: 'Test Feature',
|
|
26
|
-
type: 'feature',
|
|
27
|
-
status: 'in_progress',
|
|
28
|
-
parent_id: null,
|
|
29
|
-
parent_title: null,
|
|
30
|
-
epic_id: null,
|
|
31
|
-
epic_title: null
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
describe('getCurrentWorkPath()', () => {
|
|
35
|
-
test('should return path to current-work.json', () => {
|
|
36
|
-
const workPath = getCurrentWorkPath();
|
|
37
|
-
expect(workPath).toContain('.jettypod');
|
|
38
|
-
expect(workPath).toContain('current-work.json');
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('getCurrentWork()', () => {
|
|
43
|
-
test('should return null if file does not exist', () => {
|
|
44
|
-
expect(getCurrentWork()).toBeNull();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('should return work item if file exists', () => {
|
|
48
|
-
setCurrentWork(validWorkItem);
|
|
49
|
-
const result = getCurrentWork();
|
|
50
|
-
expect(result).toEqual(validWorkItem);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('should return null for corrupted JSON', () => {
|
|
54
|
-
const jettypodDir = path.join(testEnv.testDir, '.jettypod');
|
|
55
|
-
fs.mkdirSync(jettypodDir, { recursive: true });
|
|
56
|
-
fs.writeFileSync(getCurrentWorkPath(), 'not valid json');
|
|
57
|
-
|
|
58
|
-
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
59
|
-
const result = getCurrentWork();
|
|
60
|
-
|
|
61
|
-
expect(result).toBeNull();
|
|
62
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Corrupted current work file'));
|
|
63
|
-
consoleSpy.mockRestore();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('should return null for work item missing required fields', () => {
|
|
67
|
-
const jettypodDir = path.join(testEnv.testDir, '.jettypod');
|
|
68
|
-
fs.mkdirSync(jettypodDir, { recursive: true });
|
|
69
|
-
fs.writeFileSync(getCurrentWorkPath(), JSON.stringify({ id: 1 })); // Missing title, type, status
|
|
70
|
-
|
|
71
|
-
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
72
|
-
const result = getCurrentWork();
|
|
73
|
-
|
|
74
|
-
expect(result).toBeNull();
|
|
75
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('missing required fields'));
|
|
76
|
-
consoleSpy.mockRestore();
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe('setCurrentWork()', () => {
|
|
81
|
-
test('should create .jettypod directory if it does not exist', () => {
|
|
82
|
-
setCurrentWork(validWorkItem);
|
|
83
|
-
expect(fs.existsSync(path.join(testEnv.testDir, '.jettypod'))).toBe(true);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test('should write work item to file', () => {
|
|
87
|
-
setCurrentWork(validWorkItem);
|
|
88
|
-
|
|
89
|
-
const content = fs.readFileSync(getCurrentWorkPath(), 'utf-8');
|
|
90
|
-
expect(JSON.parse(content)).toEqual(validWorkItem);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test('should throw error for invalid work item - not an object', () => {
|
|
94
|
-
expect(() => setCurrentWork(null)).toThrow('Work item must be an object');
|
|
95
|
-
expect(() => setCurrentWork('string')).toThrow('Work item must be an object');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('should throw error for missing id', () => {
|
|
99
|
-
const invalid = { ...validWorkItem, id: null };
|
|
100
|
-
expect(() => setCurrentWork(invalid)).toThrow('Work item must have a numeric id');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test('should throw error for non-numeric id', () => {
|
|
104
|
-
const invalid = { ...validWorkItem, id: 'not-a-number' };
|
|
105
|
-
expect(() => setCurrentWork(invalid)).toThrow('Work item must have a numeric id');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test('should throw error for missing title', () => {
|
|
109
|
-
const invalid = { ...validWorkItem, title: null };
|
|
110
|
-
expect(() => setCurrentWork(invalid)).toThrow('Work item must have a string title');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('should throw error for missing type', () => {
|
|
114
|
-
const invalid = { ...validWorkItem, type: null };
|
|
115
|
-
expect(() => setCurrentWork(invalid)).toThrow('Work item must have a string type');
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('should throw error for missing status', () => {
|
|
119
|
-
const invalid = { ...validWorkItem, status: null };
|
|
120
|
-
expect(() => setCurrentWork(invalid)).toThrow('Work item must have a string status');
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
describe('clearCurrentWork()', () => {
|
|
125
|
-
test('should do nothing if file does not exist', () => {
|
|
126
|
-
expect(() => clearCurrentWork()).not.toThrow();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test('should delete current work file', () => {
|
|
130
|
-
setCurrentWork(validWorkItem);
|
|
131
|
-
expect(fs.existsSync(getCurrentWorkPath())).toBe(true);
|
|
132
|
-
|
|
133
|
-
clearCurrentWork();
|
|
134
|
-
expect(fs.existsSync(getCurrentWorkPath())).toBe(false);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test('should allow setting work again after clearing', () => {
|
|
138
|
-
setCurrentWork(validWorkItem);
|
|
139
|
-
clearCurrentWork();
|
|
140
|
-
setCurrentWork(validWorkItem);
|
|
141
|
-
|
|
142
|
-
const result = getCurrentWork();
|
|
143
|
-
expect(result).toEqual(validWorkItem);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
});
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
const { getDb, closeDb } = require('./database');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
describe('Project Config Table', () => {
|
|
6
|
-
let db;
|
|
7
|
-
const originalCwd = process.cwd();
|
|
8
|
-
const testDir = path.join('/tmp', 'jettypod-project-config-test-' + Date.now());
|
|
9
|
-
|
|
10
|
-
beforeAll(() => {
|
|
11
|
-
// Use test database
|
|
12
|
-
// SAFETY: Only delete if testDir is in /tmp
|
|
13
|
-
if (fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
|
|
14
|
-
fs.rmSync(testDir, { recursive: true });
|
|
15
|
-
}
|
|
16
|
-
fs.mkdirSync(testDir, { recursive: true });
|
|
17
|
-
process.chdir(testDir);
|
|
18
|
-
db = getDb();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach((done) => {
|
|
22
|
-
// Clean up data between tests
|
|
23
|
-
db.run("DELETE FROM project_config", () => {
|
|
24
|
-
done();
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
afterAll(() => {
|
|
29
|
-
closeDb();
|
|
30
|
-
try {
|
|
31
|
-
process.chdir(originalCwd);
|
|
32
|
-
} catch (err) {
|
|
33
|
-
// Directory may have been deleted by another test, ignore
|
|
34
|
-
}
|
|
35
|
-
// SAFETY: Only delete if testDir is in /tmp
|
|
36
|
-
if (fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
|
|
37
|
-
fs.rmSync(testDir, { recursive: true });
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('Schema', () => {
|
|
42
|
-
it('should create project_config table', (done) => {
|
|
43
|
-
db.get("SELECT name FROM sqlite_master WHERE type='table' AND name='project_config'", (err, row) => {
|
|
44
|
-
expect(err).toBeNull();
|
|
45
|
-
expect(row).toBeDefined();
|
|
46
|
-
expect(row.name).toBe('project_config');
|
|
47
|
-
done();
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should have correct columns', (done) => {
|
|
52
|
-
db.all("PRAGMA table_info(project_config)", (err, columns) => {
|
|
53
|
-
expect(err).toBeNull();
|
|
54
|
-
const columnNames = columns.map(c => c.name);
|
|
55
|
-
expect(columnNames).toContain('id');
|
|
56
|
-
expect(columnNames).toContain('project_state');
|
|
57
|
-
expect(columnNames).toContain('updated_at');
|
|
58
|
-
done();
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should default project_state to "internal"', (done) => {
|
|
63
|
-
db.run("INSERT INTO project_config (id) VALUES (1)", (err) => {
|
|
64
|
-
expect(err).toBeNull();
|
|
65
|
-
db.get("SELECT project_state FROM project_config WHERE id = 1", (err, row) => {
|
|
66
|
-
expect(err).toBeNull();
|
|
67
|
-
expect(row.project_state).toBe('internal');
|
|
68
|
-
done();
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should enforce singleton constraint', (done) => {
|
|
74
|
-
db.run("INSERT INTO project_config (id, project_state) VALUES (1, 'internal')", (err) => {
|
|
75
|
-
expect(err).toBeNull();
|
|
76
|
-
db.run("INSERT INTO project_config (id, project_state) VALUES (2, 'external')", (err) => {
|
|
77
|
-
expect(err).toBeDefined(); // Should fail due to CHECK constraint
|
|
78
|
-
done();
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('Operations', () => {
|
|
85
|
-
it('should insert and read project state', (done) => {
|
|
86
|
-
db.run("INSERT INTO project_config (id, project_state) VALUES (1, 'external')", (err) => {
|
|
87
|
-
expect(err).toBeNull();
|
|
88
|
-
db.get("SELECT * FROM project_config WHERE id = 1", (err, row) => {
|
|
89
|
-
expect(err).toBeNull();
|
|
90
|
-
expect(row.project_state).toBe('external');
|
|
91
|
-
expect(row.id).toBe(1);
|
|
92
|
-
done();
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should update project state', (done) => {
|
|
98
|
-
db.run("INSERT INTO project_config (id, project_state) VALUES (1, 'internal')", (err) => {
|
|
99
|
-
expect(err).toBeNull();
|
|
100
|
-
db.run("UPDATE project_config SET project_state = 'external' WHERE id = 1", (err) => {
|
|
101
|
-
expect(err).toBeNull();
|
|
102
|
-
db.get("SELECT project_state FROM project_config WHERE id = 1", (err, row) => {
|
|
103
|
-
expect(err).toBeNull();
|
|
104
|
-
expect(row.project_state).toBe('external');
|
|
105
|
-
done();
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
});
|
package/lib/database.test.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { createTestEnvironment } = require('./test-helpers');
|
|
4
|
-
const { getDb, closeDb, resetDb, getDbPath, getJettypodDir } = require('./database');
|
|
5
|
-
|
|
6
|
-
describe('Database Module', () => {
|
|
7
|
-
let testEnv;
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
resetDb(); // Clear singleton before test
|
|
11
|
-
testEnv = createTestEnvironment();
|
|
12
|
-
process.chdir(testEnv.testDir);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
afterEach(async () => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
await closeDb();
|
|
19
|
-
testEnv.cleanup();
|
|
20
|
-
resetDb(); // Clear singleton after test
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe('getDb()', () => {
|
|
24
|
-
test('should create .jettypod directory if it does not exist', () => {
|
|
25
|
-
const db = getDb();
|
|
26
|
-
expect(fs.existsSync(getJettypodDir())).toBe(true);
|
|
27
|
-
expect(db).toBeTruthy();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test('should create database file', (done) => {
|
|
31
|
-
const db = getDb();
|
|
32
|
-
// Wait for database to be fully initialized
|
|
33
|
-
db.get("SELECT 1", (err) => {
|
|
34
|
-
expect(err).toBeNull();
|
|
35
|
-
expect(fs.existsSync(getDbPath())).toBe(true);
|
|
36
|
-
expect(db).toBeTruthy();
|
|
37
|
-
done();
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test('should create work_items table', (done) => {
|
|
42
|
-
const db = getDb();
|
|
43
|
-
db.get("SELECT name FROM sqlite_master WHERE type='table' AND name='work_items'", (err, row) => {
|
|
44
|
-
expect(err).toBeNull();
|
|
45
|
-
expect(row).toBeTruthy();
|
|
46
|
-
expect(row.name).toBe('work_items');
|
|
47
|
-
done();
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('should return same database instance (singleton)', () => {
|
|
52
|
-
const db1 = getDb();
|
|
53
|
-
const db2 = getDb();
|
|
54
|
-
expect(db1).toBe(db2);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('should have correct schema columns', (done) => {
|
|
58
|
-
const db = getDb();
|
|
59
|
-
db.all("PRAGMA table_info(work_items)", (err, columns) => {
|
|
60
|
-
expect(err).toBeNull();
|
|
61
|
-
|
|
62
|
-
const columnNames = columns.map(col => col.name);
|
|
63
|
-
expect(columnNames).toContain('id');
|
|
64
|
-
expect(columnNames).toContain('type');
|
|
65
|
-
expect(columnNames).toContain('title');
|
|
66
|
-
expect(columnNames).toContain('description');
|
|
67
|
-
expect(columnNames).toContain('status');
|
|
68
|
-
expect(columnNames).toContain('parent_id');
|
|
69
|
-
expect(columnNames).toContain('epic_id');
|
|
70
|
-
expect(columnNames).toContain('branch_name');
|
|
71
|
-
expect(columnNames).toContain('file_paths');
|
|
72
|
-
expect(columnNames).toContain('commit_sha');
|
|
73
|
-
expect(columnNames).toContain('mode');
|
|
74
|
-
expect(columnNames).toContain('current');
|
|
75
|
-
expect(columnNames).toContain('created_at');
|
|
76
|
-
done();
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe('closeDb()', () => {
|
|
82
|
-
test('should close database connection', async () => {
|
|
83
|
-
const db = getDb();
|
|
84
|
-
expect(db).toBeTruthy();
|
|
85
|
-
|
|
86
|
-
await closeDb();
|
|
87
|
-
|
|
88
|
-
// After close, getDb should create a new instance
|
|
89
|
-
const db2 = getDb();
|
|
90
|
-
expect(db2).toBeTruthy();
|
|
91
|
-
expect(db2).not.toBe(db);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('should not throw when called without active connection', async () => {
|
|
95
|
-
await expect(closeDb()).resolves.not.toThrow();
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('getDbPath() and getJettypodDir()', () => {
|
|
100
|
-
test('should return correct paths', () => {
|
|
101
|
-
expect(getDbPath()).toContain('.jettypod');
|
|
102
|
-
expect(getDbPath()).toContain('work.db');
|
|
103
|
-
expect(getJettypodDir()).toContain('.jettypod');
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
});
|