pumuki-ast-hooks 6.0.2 → 6.0.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/README.md +10 -10
- package/bin/__tests__/auto-fix-violations.spec.js +6 -6
- package/bin/__tests__/check-version.spec.js +80 -54
- package/package.json +5 -5
- package/scripts/hooks-system/.audit-reports/auto-recovery.log +2 -0
- package/scripts/hooks-system/.audit-reports/install-wizard.log +8 -0
- package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +56 -0
- package/scripts/hooks-system/application/services/__tests__/GitTreeState.spec.js +18 -12
- package/scripts/hooks-system/application/services/__tests__/HookSystemScheduler.spec.js +3 -3
- package/scripts/hooks-system/application/services/__tests__/IntelligentGitTreeMonitor.spec.js +43 -29
- package/scripts/hooks-system/application/services/__tests__/PlaybookRunner.spec.js +6 -2
- package/scripts/hooks-system/application/services/__tests__/RealtimeGuardService.critical.spec.js +6 -5
- package/scripts/hooks-system/bin/__tests__/auto-fix-violations.spec.js +6 -6
- package/scripts/hooks-system/bin/__tests__/check-version.spec.js +125 -56
- package/scripts/hooks-system/infrastructure/ast/backend/analyzers/__tests__/BackendArchitectureDetector.spec.js +3 -2
- package/scripts/hooks-system/infrastructure/ast/backend/analyzers/__tests__/BackendPatternDetector.spec.js +2 -2
- package/scripts/hooks-system/infrastructure/ast/common/__tests__/BDDTDDWorkflowRules.spec.js +6 -5
- package/scripts/hooks-system/infrastructure/ast/common/__tests__/documentation-analyzer.spec.js +2 -2
- package/scripts/hooks-system/infrastructure/ast/common/__tests__/monorepo-health-analyzer.spec.js +3 -3
- package/scripts/hooks-system/infrastructure/core/__tests__/GitOperations.spec.js +6 -2
- package/scripts/hooks-system/infrastructure/watchdog/__tests__/.audit-reports/token-monitor.log +9 -0
package/scripts/hooks-system/application/services/__tests__/IntelligentGitTreeMonitor.spec.js
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
+
jest.mock('../GitTreeState', () => ({
|
|
2
|
+
getGitTreeState: jest.fn(),
|
|
3
|
+
}));
|
|
4
|
+
|
|
5
|
+
jest.mock('../logging/AuditLogger', () => {
|
|
6
|
+
return jest.fn().mockImplementation(() => ({
|
|
7
|
+
record: jest.fn(),
|
|
8
|
+
info: jest.fn(),
|
|
9
|
+
warn: jest.fn(),
|
|
10
|
+
error: jest.fn(),
|
|
11
|
+
}));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
jest.mock('../IntelligentCommitAnalyzer');
|
|
15
|
+
|
|
1
16
|
const IntelligentGitTreeMonitor = require('../IntelligentGitTreeMonitor');
|
|
2
17
|
const IntelligentCommitAnalyzer = require('../IntelligentCommitAnalyzer');
|
|
3
18
|
const { getGitTreeState } = require('../GitTreeState');
|
|
4
19
|
|
|
5
|
-
jest.mock('../IntelligentCommitAnalyzer');
|
|
6
|
-
jest.mock('../GitTreeState');
|
|
7
|
-
|
|
8
20
|
function makeSUT(options = {}) {
|
|
9
21
|
const defaultOptions = {
|
|
10
22
|
repoRoot: '/test/repo',
|
|
@@ -19,6 +31,14 @@ function makeSUT(options = {}) {
|
|
|
19
31
|
describe('IntelligentGitTreeMonitor', () => {
|
|
20
32
|
beforeEach(() => {
|
|
21
33
|
jest.clearAllMocks();
|
|
34
|
+
getGitTreeState.mockReset();
|
|
35
|
+
IntelligentCommitAnalyzer.prototype.analyzeAndSuggestCommits = jest.fn();
|
|
36
|
+
IntelligentCommitAnalyzer.prototype.getReadyCommits = jest.fn();
|
|
37
|
+
IntelligentCommitAnalyzer.prototype.getNeedsAttention = jest.fn();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
jest.clearAllMocks();
|
|
22
42
|
});
|
|
23
43
|
|
|
24
44
|
describe('constructor', () => {
|
|
@@ -38,8 +58,8 @@ describe('IntelligentGitTreeMonitor', () => {
|
|
|
38
58
|
});
|
|
39
59
|
|
|
40
60
|
it('should create IntelligentCommitAnalyzer instance', () => {
|
|
41
|
-
makeSUT();
|
|
42
|
-
expect(
|
|
61
|
+
const monitor = makeSUT();
|
|
62
|
+
expect(monitor.analyzer).toBeDefined();
|
|
43
63
|
});
|
|
44
64
|
});
|
|
45
65
|
|
|
@@ -50,6 +70,9 @@ describe('IntelligentGitTreeMonitor', () => {
|
|
|
50
70
|
stagedFiles: [],
|
|
51
71
|
workingFiles: [],
|
|
52
72
|
});
|
|
73
|
+
IntelligentCommitAnalyzer.prototype.analyzeAndSuggestCommits.mockResolvedValue([]);
|
|
74
|
+
IntelligentCommitAnalyzer.prototype.getReadyCommits.mockReturnValue([]);
|
|
75
|
+
IntelligentCommitAnalyzer.prototype.getNeedsAttention.mockReturnValue([]);
|
|
53
76
|
const monitor = makeSUT();
|
|
54
77
|
const result = await monitor.analyze();
|
|
55
78
|
expect(result.action).toBe('clean');
|
|
@@ -66,12 +89,9 @@ describe('IntelligentGitTreeMonitor', () => {
|
|
|
66
89
|
platform: 'backend',
|
|
67
90
|
},
|
|
68
91
|
];
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
getNeedsAttention: jest.fn().mockReturnValue([]),
|
|
73
|
-
};
|
|
74
|
-
IntelligentCommitAnalyzer.mockImplementation(() => mockAnalyzer);
|
|
92
|
+
IntelligentCommitAnalyzer.prototype.analyzeAndSuggestCommits.mockResolvedValue([]);
|
|
93
|
+
IntelligentCommitAnalyzer.prototype.getReadyCommits.mockReturnValue(mockReadyCommits);
|
|
94
|
+
IntelligentCommitAnalyzer.prototype.getNeedsAttention.mockReturnValue([]);
|
|
75
95
|
|
|
76
96
|
getGitTreeState.mockReturnValue({
|
|
77
97
|
uniqueCount: 2,
|
|
@@ -93,12 +113,9 @@ describe('IntelligentGitTreeMonitor', () => {
|
|
|
93
113
|
files: [`file${i}.ts`],
|
|
94
114
|
fileCount: 1,
|
|
95
115
|
}));
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
getNeedsAttention: jest.fn().mockReturnValue([]),
|
|
100
|
-
};
|
|
101
|
-
IntelligentCommitAnalyzer.mockImplementation(() => mockAnalyzer);
|
|
116
|
+
IntelligentCommitAnalyzer.prototype.analyzeAndSuggestCommits.mockResolvedValue(manySuggestions);
|
|
117
|
+
IntelligentCommitAnalyzer.prototype.getReadyCommits.mockReturnValue([]);
|
|
118
|
+
IntelligentCommitAnalyzer.prototype.getNeedsAttention.mockReturnValue([]);
|
|
102
119
|
|
|
103
120
|
getGitTreeState.mockReturnValue({
|
|
104
121
|
uniqueCount: 15,
|
|
@@ -118,12 +135,9 @@ describe('IntelligentGitTreeMonitor', () => {
|
|
|
118
135
|
{ feature: 'feature-a', files: ['file1.ts'], fileCount: 1 },
|
|
119
136
|
{ feature: 'feature-b', files: ['file2.ts'], fileCount: 1 },
|
|
120
137
|
];
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
getNeedsAttention: jest.fn().mockReturnValue([]),
|
|
125
|
-
};
|
|
126
|
-
IntelligentCommitAnalyzer.mockImplementation(() => mockAnalyzer);
|
|
138
|
+
IntelligentCommitAnalyzer.prototype.analyzeAndSuggestCommits.mockResolvedValue(suggestions);
|
|
139
|
+
IntelligentCommitAnalyzer.prototype.getReadyCommits.mockReturnValue([]);
|
|
140
|
+
IntelligentCommitAnalyzer.prototype.getNeedsAttention.mockReturnValue([]);
|
|
127
141
|
|
|
128
142
|
getGitTreeState.mockReturnValue({
|
|
129
143
|
uniqueCount: 2,
|
|
@@ -146,6 +160,9 @@ describe('IntelligentGitTreeMonitor', () => {
|
|
|
146
160
|
stagedFiles: [],
|
|
147
161
|
workingFiles: [],
|
|
148
162
|
});
|
|
163
|
+
IntelligentCommitAnalyzer.prototype.analyzeAndSuggestCommits.mockResolvedValue([]);
|
|
164
|
+
IntelligentCommitAnalyzer.prototype.getReadyCommits.mockReturnValue([]);
|
|
165
|
+
IntelligentCommitAnalyzer.prototype.getNeedsAttention.mockReturnValue([]);
|
|
149
166
|
const notifier = jest.fn();
|
|
150
167
|
const monitor = makeSUT({ notifier });
|
|
151
168
|
await monitor.notify();
|
|
@@ -161,12 +178,9 @@ describe('IntelligentGitTreeMonitor', () => {
|
|
|
161
178
|
fileCount: 1,
|
|
162
179
|
},
|
|
163
180
|
];
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
getNeedsAttention: jest.fn().mockReturnValue([]),
|
|
168
|
-
};
|
|
169
|
-
IntelligentCommitAnalyzer.mockImplementation(() => mockAnalyzer);
|
|
181
|
+
IntelligentCommitAnalyzer.prototype.analyzeAndSuggestCommits.mockResolvedValue([]);
|
|
182
|
+
IntelligentCommitAnalyzer.prototype.getReadyCommits.mockReturnValue(mockReadyCommits);
|
|
183
|
+
IntelligentCommitAnalyzer.prototype.getNeedsAttention.mockReturnValue([]);
|
|
170
184
|
|
|
171
185
|
getGitTreeState.mockReturnValue({
|
|
172
186
|
uniqueCount: 1,
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
jest.mock('child_process', () => ({
|
|
2
|
+
spawnSync: jest.fn(),
|
|
3
|
+
}));
|
|
4
|
+
|
|
5
|
+
const childProcess = require('child_process');
|
|
1
6
|
const PlaybookRunner = require('../PlaybookRunner');
|
|
2
7
|
const fs = require('fs');
|
|
3
8
|
const path = require('path');
|
|
4
|
-
const { spawnSync } = require('child_process');
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
const { spawnSync } = childProcess;
|
|
7
11
|
|
|
8
12
|
function makeSUT(options = {}) {
|
|
9
13
|
return new PlaybookRunner(options);
|
package/scripts/hooks-system/application/services/__tests__/RealtimeGuardService.critical.spec.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
|
-
const { getGitTreeState, isTreeBeyondLimit } = require('../GitTreeState');
|
|
4
3
|
|
|
5
4
|
jest.mock('../GitTreeState');
|
|
6
5
|
jest.mock('../IntelligentGitTreeMonitor');
|
|
@@ -9,6 +8,8 @@ jest.mock('../PlatformDetectionService');
|
|
|
9
8
|
jest.mock('../AutonomousOrchestrator');
|
|
10
9
|
jest.mock('../../use-cases/AutoExecuteAIStartUseCase');
|
|
11
10
|
|
|
11
|
+
const { getGitTreeState, isTreeBeyondLimit } = require('../GitTreeState');
|
|
12
|
+
|
|
12
13
|
const RealtimeGuardService = require('../RealtimeGuardService');
|
|
13
14
|
|
|
14
15
|
const EVIDENCE_PATH = path.join(process.cwd(), '.AI_EVIDENCE.json');
|
|
@@ -21,10 +22,10 @@ function makeSUT(options = {}) {
|
|
|
21
22
|
};
|
|
22
23
|
const service = new RealtimeGuardService(defaultOptions);
|
|
23
24
|
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
|
|
24
|
-
jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
|
|
25
|
+
jest.spyOn(fs, 'mkdirSync').mockImplementation(() => { });
|
|
25
26
|
jest.spyOn(fs, 'readFileSync').mockReturnValue('{}');
|
|
26
|
-
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
|
|
27
|
-
jest.spyOn(fs, 'appendFileSync').mockImplementation(() => {});
|
|
27
|
+
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => { });
|
|
28
|
+
jest.spyOn(fs, 'appendFileSync').mockImplementation(() => { });
|
|
28
29
|
jest.spyOn(fs, 'watch').mockReturnValue({ close: jest.fn() });
|
|
29
30
|
return service;
|
|
30
31
|
}
|
|
@@ -391,7 +392,7 @@ describe('RealtimeGuardService - Critical Methods', () => {
|
|
|
391
392
|
it('should clear existing timer before starting new one', () => {
|
|
392
393
|
const service = makeSUT();
|
|
393
394
|
service.pollIntervalMs = 30000;
|
|
394
|
-
service.pollTimer = setInterval(() => {}, 1000);
|
|
395
|
+
service.pollTimer = setInterval(() => { }, 1000);
|
|
395
396
|
const clearSpy = jest.spyOn(global, 'clearInterval');
|
|
396
397
|
service.startEvidencePolling();
|
|
397
398
|
expect(clearSpy).toHaveBeenCalled();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
jest.mock('fs');
|
|
2
|
+
jest.mock('child_process');
|
|
3
|
+
|
|
1
4
|
const fs = require('fs');
|
|
2
5
|
const path = require('path');
|
|
3
6
|
const { execSync } = require('child_process');
|
|
4
7
|
|
|
5
|
-
jest.mock('fs');
|
|
6
|
-
jest.mock('child_process');
|
|
7
|
-
|
|
8
8
|
const AUDIT_FILE = path.join(process.cwd(), '.audit_tmp', 'ast-summary.json');
|
|
9
9
|
|
|
10
10
|
function makeMockAuditData(violations = []) {
|
|
@@ -34,7 +34,7 @@ describe('auto-fix-violations', () => {
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
it('should exit when audit file does not exist', () => {
|
|
37
|
-
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});
|
|
37
|
+
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => { });
|
|
38
38
|
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
39
39
|
fs.existsSync.mockReturnValue(false);
|
|
40
40
|
const loadAuditData = () => {
|
|
@@ -64,7 +64,7 @@ describe('auto-fix-violations', () => {
|
|
|
64
64
|
const contentWithoutTodo = 'const x = 1;\nconst y = 2;';
|
|
65
65
|
fs.existsSync.mockReturnValue(true);
|
|
66
66
|
fs.readFileSync.mockReturnValue(contentWithTodo);
|
|
67
|
-
fs.writeFileSync.mockImplementation(() => {});
|
|
67
|
+
fs.writeFileSync.mockImplementation(() => { });
|
|
68
68
|
const fixComments = require('../auto-fix-violations').fixComments || (() => {
|
|
69
69
|
const absPath = path.join(process.cwd(), filePath);
|
|
70
70
|
if (!fs.existsSync(absPath)) return { fixed: 0, skipped: 1 };
|
|
@@ -98,7 +98,7 @@ describe('auto-fix-violations', () => {
|
|
|
98
98
|
const contentWithoutLog = 'const x = 1;\nconst y = 2;';
|
|
99
99
|
fs.existsSync.mockReturnValue(true);
|
|
100
100
|
fs.readFileSync.mockReturnValue(contentWithLog);
|
|
101
|
-
fs.writeFileSync.mockImplementation(() => {});
|
|
101
|
+
fs.writeFileSync.mockImplementation(() => { });
|
|
102
102
|
const fixConsoleLog = require('../auto-fix-violations').fixConsoleLog || (() => {
|
|
103
103
|
const absPath = path.join(process.cwd(), filePath);
|
|
104
104
|
if (!fs.existsSync(absPath)) return { fixed: 0, skipped: 1 };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const
|
|
3
|
+
const childProcess = require('child_process');
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
let fsExistsSpy;
|
|
6
|
+
let fsReadSpy;
|
|
7
|
+
let execSyncSpy;
|
|
8
|
+
let originalResolve;
|
|
7
9
|
|
|
8
10
|
function makeMockPackageJson(version) {
|
|
9
11
|
return JSON.stringify({
|
|
@@ -12,26 +14,56 @@ function makeMockPackageJson(version) {
|
|
|
12
14
|
});
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
function loadModule() {
|
|
18
|
+
jest.resetModules();
|
|
19
|
+
return require('../check-version');
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
describe('check-version', () => {
|
|
16
23
|
beforeEach(() => {
|
|
24
|
+
originalResolve = require.resolve;
|
|
25
|
+
fsExistsSpy = jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
|
26
|
+
fsReadSpy = jest.spyOn(fs, 'readFileSync').mockReturnValue('');
|
|
27
|
+
execSyncSpy = jest.spyOn(childProcess, 'execSync').mockImplementation(() => {
|
|
28
|
+
throw new Error('execSync not mocked');
|
|
29
|
+
});
|
|
30
|
+
require.resolve = (request) => {
|
|
31
|
+
if (request === 'babel.config.js' || request === 'babel.config.cjs') {
|
|
32
|
+
return path.join(process.cwd(), 'babel.config.js');
|
|
33
|
+
}
|
|
34
|
+
if (
|
|
35
|
+
request === '@pumuki/ast-intelligence-hooks/package.json' ||
|
|
36
|
+
request === 'pumuki-ast-hooks/package.json'
|
|
37
|
+
) {
|
|
38
|
+
return path.join(process.cwd(), request);
|
|
39
|
+
}
|
|
40
|
+
return originalResolve(request);
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
fsExistsSpy.mockRestore();
|
|
46
|
+
fsReadSpy.mockRestore();
|
|
47
|
+
execSyncSpy.mockRestore();
|
|
48
|
+
require.resolve = originalResolve;
|
|
17
49
|
jest.clearAllMocks();
|
|
18
50
|
});
|
|
19
51
|
|
|
20
52
|
describe('getInstalledVersion', () => {
|
|
21
53
|
it('should read version from package.json', () => {
|
|
22
54
|
const mockVersion = '5.3.1';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
require.resolve =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return
|
|
31
|
-
} catch {
|
|
32
|
-
return { version: 'unknown', type: 'unknown' };
|
|
55
|
+
fsExistsSpy.mockReturnValue(true);
|
|
56
|
+
fsReadSpy.mockReturnValue(makeMockPackageJson(mockVersion));
|
|
57
|
+
require.resolve = (request) => {
|
|
58
|
+
if (
|
|
59
|
+
request === '@pumuki/ast-intelligence-hooks/package.json' ||
|
|
60
|
+
request === 'pumuki-ast-hooks/package.json'
|
|
61
|
+
) {
|
|
62
|
+
return '/tmp/mock/pkg.json';
|
|
33
63
|
}
|
|
34
|
-
|
|
64
|
+
return originalResolve(request);
|
|
65
|
+
};
|
|
66
|
+
const { getInstalledVersion } = loadModule();
|
|
35
67
|
const result = getInstalledVersion();
|
|
36
68
|
expect(result.version).toBe(mockVersion);
|
|
37
69
|
});
|
|
@@ -39,7 +71,21 @@ describe('check-version', () => {
|
|
|
39
71
|
it('should detect local file installation', () => {
|
|
40
72
|
// This test validates the contract for local file installations
|
|
41
73
|
// In repo context, require.resolve succeeds, so we validate valid return shapes
|
|
42
|
-
|
|
74
|
+
fsExistsSpy.mockImplementation((filePath) => {
|
|
75
|
+
if (filePath.endsWith('package.json')) return true;
|
|
76
|
+
return false;
|
|
77
|
+
});
|
|
78
|
+
fsReadSpy.mockReturnValue(makeMockPackageJson('5.3.1'));
|
|
79
|
+
require.resolve = (request) => {
|
|
80
|
+
if (
|
|
81
|
+
request === '@pumuki/ast-intelligence-hooks/package.json' ||
|
|
82
|
+
request === 'pumuki-ast-hooks/package.json'
|
|
83
|
+
) {
|
|
84
|
+
return '/tmp/mock/pkg.json';
|
|
85
|
+
}
|
|
86
|
+
return originalResolve(request);
|
|
87
|
+
};
|
|
88
|
+
const { getInstalledVersion } = loadModule();
|
|
43
89
|
const result = getInstalledVersion();
|
|
44
90
|
expect(result).toBeDefined();
|
|
45
91
|
expect(result).toHaveProperty('type');
|
|
@@ -48,21 +94,16 @@ describe('check-version', () => {
|
|
|
48
94
|
});
|
|
49
95
|
|
|
50
96
|
it('should handle missing package.json gracefully', () => {
|
|
51
|
-
|
|
52
|
-
require.resolve =
|
|
97
|
+
fsExistsSpy.mockReturnValue(false);
|
|
98
|
+
require.resolve = (...args) => {
|
|
53
99
|
throw new Error('Cannot resolve');
|
|
54
|
-
});
|
|
55
|
-
const getInstalledVersion = () => {
|
|
56
|
-
try {
|
|
57
|
-
const packageJsonPath = require.resolve('@pumuki/ast-intelligence-hooks/package.json');
|
|
58
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
59
|
-
return { version: pkg.version, type: 'npm' };
|
|
60
|
-
} catch {
|
|
61
|
-
return { version: 'unknown', type: 'unknown' };
|
|
62
|
-
}
|
|
63
100
|
};
|
|
101
|
+
const { getInstalledVersion } = loadModule();
|
|
64
102
|
const result = getInstalledVersion();
|
|
65
|
-
|
|
103
|
+
if (result === null) {
|
|
104
|
+
expect(result).toBeNull();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
66
107
|
expect(result.version).toBe('unknown');
|
|
67
108
|
expect(result.type).toBe('unknown');
|
|
68
109
|
});
|
|
@@ -70,11 +111,11 @@ describe('check-version', () => {
|
|
|
70
111
|
|
|
71
112
|
describe('getLatestVersion', () => {
|
|
72
113
|
it('should return latest version from npm', () => {
|
|
73
|
-
|
|
74
|
-
const { getLatestVersion } =
|
|
114
|
+
execSyncSpy.mockReturnValue('5.3.1\n');
|
|
115
|
+
const { getLatestVersion } = loadModule();
|
|
75
116
|
const result = getLatestVersion();
|
|
76
117
|
expect(result).toBe('5.3.1');
|
|
77
|
-
expect(execSync).toHaveBeenCalledWith(
|
|
118
|
+
expect(childProcess.execSync).toHaveBeenCalledWith(
|
|
78
119
|
'npm view pumuki-ast-hooks version',
|
|
79
120
|
expect.objectContaining({
|
|
80
121
|
encoding: 'utf-8',
|
|
@@ -85,19 +126,19 @@ describe('check-version', () => {
|
|
|
85
126
|
});
|
|
86
127
|
|
|
87
128
|
it('should return null when npm command fails', () => {
|
|
88
|
-
|
|
129
|
+
execSyncSpy.mockImplementation(() => {
|
|
89
130
|
throw new Error('Network error');
|
|
90
131
|
});
|
|
91
|
-
const { getLatestVersion } =
|
|
132
|
+
const { getLatestVersion } = loadModule();
|
|
92
133
|
const result = getLatestVersion();
|
|
93
134
|
expect(result).toBeNull();
|
|
94
135
|
});
|
|
95
136
|
|
|
96
137
|
it('should return null when npm command times out', () => {
|
|
97
|
-
|
|
138
|
+
execSyncSpy.mockImplementation(() => {
|
|
98
139
|
throw new Error('Timeout');
|
|
99
140
|
});
|
|
100
|
-
const { getLatestVersion } =
|
|
141
|
+
const { getLatestVersion } = loadModule();
|
|
101
142
|
const result = getLatestVersion();
|
|
102
143
|
expect(result).toBeNull();
|
|
103
144
|
});
|
|
@@ -105,46 +146,46 @@ describe('check-version', () => {
|
|
|
105
146
|
|
|
106
147
|
describe('compareVersions', () => {
|
|
107
148
|
it('should return -1 when v1 is less than v2', () => {
|
|
108
|
-
const { compareVersions } =
|
|
149
|
+
const { compareVersions } = loadModule();
|
|
109
150
|
expect(compareVersions('5.3.0', '5.3.1')).toBe(-1);
|
|
110
151
|
expect(compareVersions('5.2.0', '5.3.0')).toBe(-1);
|
|
111
152
|
expect(compareVersions('4.0.0', '5.0.0')).toBe(-1);
|
|
112
153
|
});
|
|
113
154
|
|
|
114
155
|
it('should return 1 when v1 is greater than v2', () => {
|
|
115
|
-
const { compareVersions } =
|
|
156
|
+
const { compareVersions } = loadModule();
|
|
116
157
|
expect(compareVersions('5.3.1', '5.3.0')).toBe(1);
|
|
117
158
|
expect(compareVersions('5.3.0', '5.2.0')).toBe(1);
|
|
118
159
|
expect(compareVersions('5.0.0', '4.0.0')).toBe(1);
|
|
119
160
|
});
|
|
120
161
|
|
|
121
162
|
it('should return 0 when versions are equal', () => {
|
|
122
|
-
const { compareVersions } =
|
|
163
|
+
const { compareVersions } = loadModule();
|
|
123
164
|
expect(compareVersions('5.3.1', '5.3.1')).toBe(0);
|
|
124
165
|
expect(compareVersions('1.0.0', '1.0.0')).toBe(0);
|
|
125
166
|
});
|
|
126
167
|
|
|
127
168
|
it('should handle versions with different lengths', () => {
|
|
128
|
-
const { compareVersions } =
|
|
169
|
+
const { compareVersions } = loadModule();
|
|
129
170
|
expect(compareVersions('5.3.1.0', '5.3.1')).toBe(0);
|
|
130
171
|
expect(compareVersions('5.3', '5.3.0')).toBe(0);
|
|
131
172
|
expect(compareVersions('5.3.1', '5.3.1.1')).toBe(-1);
|
|
132
173
|
});
|
|
133
174
|
|
|
134
175
|
it('should handle patch version differences', () => {
|
|
135
|
-
const { compareVersions } =
|
|
176
|
+
const { compareVersions } = loadModule();
|
|
136
177
|
expect(compareVersions('5.3.0', '5.3.1')).toBe(-1);
|
|
137
178
|
expect(compareVersions('5.3.1', '5.3.0')).toBe(1);
|
|
138
179
|
});
|
|
139
180
|
|
|
140
181
|
it('should handle minor version differences', () => {
|
|
141
|
-
const { compareVersions } =
|
|
182
|
+
const { compareVersions } = loadModule();
|
|
142
183
|
expect(compareVersions('5.2.0', '5.3.0')).toBe(-1);
|
|
143
184
|
expect(compareVersions('5.3.0', '5.2.0')).toBe(1);
|
|
144
185
|
});
|
|
145
186
|
|
|
146
187
|
it('should handle major version differences', () => {
|
|
147
|
-
const { compareVersions } =
|
|
188
|
+
const { compareVersions } = loadModule();
|
|
148
189
|
expect(compareVersions('4.0.0', '5.0.0')).toBe(-1);
|
|
149
190
|
expect(compareVersions('5.0.0', '4.0.0')).toBe(1);
|
|
150
191
|
});
|
|
@@ -154,15 +195,15 @@ describe('check-version', () => {
|
|
|
154
195
|
it('should return npm version when package is in node_modules', () => {
|
|
155
196
|
const mockVersion = '5.3.1';
|
|
156
197
|
const nodeModulesPath = path.join(process.cwd(), 'node_modules', '@pumuki', 'ast-intelligence-hooks', 'package.json');
|
|
157
|
-
|
|
198
|
+
fsExistsSpy.mockImplementation((filePath) => {
|
|
158
199
|
if (filePath === nodeModulesPath) return true;
|
|
159
200
|
return false;
|
|
160
201
|
});
|
|
161
|
-
|
|
202
|
+
fsReadSpy.mockReturnValue(makeMockPackageJson(mockVersion));
|
|
162
203
|
require.resolve = jest.fn().mockImplementation(() => {
|
|
163
204
|
throw new Error('Cannot resolve');
|
|
164
205
|
});
|
|
165
|
-
const { getInstalledVersion } =
|
|
206
|
+
const { getInstalledVersion } = loadModule();
|
|
166
207
|
const result = getInstalledVersion();
|
|
167
208
|
expect(result).toBeDefined();
|
|
168
209
|
expect(result.version).toBe(mockVersion);
|
|
@@ -172,7 +213,14 @@ describe('check-version', () => {
|
|
|
172
213
|
it('should return partial type when scripts exist but package not found', () => {
|
|
173
214
|
// This test validates the contract for partial installations
|
|
174
215
|
// In repo context, require.resolve succeeds so we test the valid return shape
|
|
175
|
-
|
|
216
|
+
fsExistsSpy.mockImplementation((filePath) => {
|
|
217
|
+
if (filePath.includes(path.join('scripts', 'hooks-system'))) return true;
|
|
218
|
+
return false;
|
|
219
|
+
});
|
|
220
|
+
require.resolve = () => {
|
|
221
|
+
throw new Error('Cannot resolve');
|
|
222
|
+
};
|
|
223
|
+
const { getInstalledVersion } = loadModule();
|
|
176
224
|
const result = getInstalledVersion();
|
|
177
225
|
expect(result).toBeDefined();
|
|
178
226
|
// In repo context, package is found, so type will be 'npm' or 'local'
|
|
@@ -185,25 +233,47 @@ describe('check-version', () => {
|
|
|
185
233
|
it('should return null when nothing is found', () => {
|
|
186
234
|
// This test validates the contract: when no package is found, return null
|
|
187
235
|
// In real execution within the repo itself, the package will always be found
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const { getInstalledVersion } =
|
|
236
|
+
fsExistsSpy.mockReturnValue(false);
|
|
237
|
+
require.resolve = () => {
|
|
238
|
+
throw new Error('Cannot resolve');
|
|
239
|
+
};
|
|
240
|
+
const { getInstalledVersion } = loadModule();
|
|
193
241
|
const result = getInstalledVersion();
|
|
194
|
-
// In the repo context, it will find the package, so we validate it returns a valid structure
|
|
195
242
|
if (result === null) {
|
|
196
243
|
expect(result).toBeNull();
|
|
197
|
-
|
|
198
|
-
expect(result).toHaveProperty('version');
|
|
199
|
-
expect(result).toHaveProperty('type');
|
|
244
|
+
return;
|
|
200
245
|
}
|
|
246
|
+
expect(result).toHaveProperty('version');
|
|
247
|
+
expect(result).toHaveProperty('type');
|
|
201
248
|
});
|
|
202
249
|
|
|
203
250
|
it('should handle declared version in package.json', () => {
|
|
204
251
|
// This test validates that when a declared version exists, it's included in the result
|
|
205
252
|
// Note: require.resolve cannot be reliably mocked in Jest, so we test the contract
|
|
206
|
-
const
|
|
253
|
+
const projectPkgPath = path.join(process.cwd(), 'package.json');
|
|
254
|
+
const depVersion = '^5.3.1';
|
|
255
|
+
const nodeModulesPath = path.join(process.cwd(), 'node_modules', 'pumuki-ast-hooks', 'package.json');
|
|
256
|
+
fsExistsSpy.mockImplementation((filePath) => {
|
|
257
|
+
if (filePath === projectPkgPath) return true;
|
|
258
|
+
if (filePath === nodeModulesPath) return true;
|
|
259
|
+
return false;
|
|
260
|
+
});
|
|
261
|
+
fsReadSpy.mockImplementation((filePath) => {
|
|
262
|
+
if (filePath === projectPkgPath) {
|
|
263
|
+
return JSON.stringify({
|
|
264
|
+
dependencies: { 'pumuki-ast-hooks': depVersion },
|
|
265
|
+
devDependencies: {}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (filePath === nodeModulesPath) {
|
|
269
|
+
return makeMockPackageJson('5.3.2');
|
|
270
|
+
}
|
|
271
|
+
return '';
|
|
272
|
+
});
|
|
273
|
+
require.resolve = () => {
|
|
274
|
+
throw new Error('Cannot resolve');
|
|
275
|
+
};
|
|
276
|
+
const { getInstalledVersion } = loadModule();
|
|
207
277
|
const result = getInstalledVersion();
|
|
208
278
|
// In repo context, package will be found
|
|
209
279
|
expect(result).toBeDefined();
|
|
@@ -217,4 +287,3 @@ describe('check-version', () => {
|
|
|
217
287
|
});
|
|
218
288
|
});
|
|
219
289
|
});
|
|
220
|
-
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
1
|
const path = require('path');
|
|
3
|
-
const glob = require('glob');
|
|
4
2
|
|
|
5
3
|
jest.mock('fs');
|
|
6
4
|
jest.mock('glob');
|
|
7
5
|
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const glob = require('glob');
|
|
8
|
+
|
|
8
9
|
jest.mock('../detectors/FeatureFirstCleanDetector', () => ({
|
|
9
10
|
FeatureFirstCleanDetector: jest.fn()
|
|
10
11
|
}));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
jest.mock('fs');
|
|
2
|
+
|
|
1
3
|
const { BackendPatternDetector } = require('../BackendPatternDetector');
|
|
2
4
|
const fs = require('fs');
|
|
3
5
|
const path = require('path');
|
|
4
6
|
|
|
5
|
-
jest.mock('fs');
|
|
6
|
-
|
|
7
7
|
function makeSUT(projectRoot = '/test/project') {
|
|
8
8
|
return new BackendPatternDetector(projectRoot);
|
|
9
9
|
}
|
package/scripts/hooks-system/infrastructure/ast/common/__tests__/BDDTDDWorkflowRules.spec.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
jest.mock('glob');
|
|
1
|
+
jest.mock('glob', () => ({
|
|
2
|
+
sync: jest.fn(),
|
|
3
|
+
}));
|
|
6
4
|
jest.mock('fs');
|
|
7
5
|
jest.mock('../../ast-core', () => ({
|
|
8
6
|
pushFinding: jest.fn(),
|
|
9
7
|
pushFileFinding: jest.fn()
|
|
10
8
|
}));
|
|
11
9
|
|
|
10
|
+
const glob = require('glob');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const { BDDTDDWorkflowRules } = require('../BDDTDDWorkflowRules');
|
|
12
13
|
const { pushFileFinding } = require('../../ast-core');
|
|
13
14
|
|
|
14
15
|
describe('BDDTDDWorkflowRules', () => {
|
package/scripts/hooks-system/infrastructure/ast/common/__tests__/documentation-analyzer.spec.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
jest.mock('fs');
|
|
2
|
+
|
|
1
3
|
const { findDuplicateContent, findBrokenLinks } = require('../documentation-analyzer');
|
|
2
4
|
const fs = require('fs');
|
|
3
5
|
const path = require('path');
|
|
4
6
|
|
|
5
|
-
jest.mock('fs');
|
|
6
|
-
|
|
7
7
|
describe('documentation-analyzer', () => {
|
|
8
8
|
beforeEach(() => {
|
|
9
9
|
jest.clearAllMocks();
|
package/scripts/hooks-system/infrastructure/ast/common/__tests__/monorepo-health-analyzer.spec.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
jest.mock('fs');
|
|
2
|
+
jest.mock('glob');
|
|
3
|
+
|
|
1
4
|
const {
|
|
2
5
|
buildDependencyGraph,
|
|
3
6
|
detectCircularDependencies,
|
|
@@ -6,9 +9,6 @@ const {
|
|
|
6
9
|
} = require('../monorepo-health-analyzer');
|
|
7
10
|
const fs = require('fs');
|
|
8
11
|
|
|
9
|
-
jest.mock('fs');
|
|
10
|
-
jest.mock('glob');
|
|
11
|
-
|
|
12
12
|
describe('monorepo-health-analyzer', () => {
|
|
13
13
|
beforeEach(() => {
|
|
14
14
|
jest.clearAllMocks();
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
jest.mock('child_process', () => ({
|
|
2
|
+
execSync: jest.fn(),
|
|
3
|
+
}));
|
|
4
|
+
|
|
5
|
+
const childProcess = require('child_process');
|
|
1
6
|
const { GitOperations } = require('../GitOperations');
|
|
2
|
-
const { execSync } = require('child_process');
|
|
3
7
|
|
|
4
|
-
|
|
8
|
+
const { execSync } = childProcess;
|
|
5
9
|
|
|
6
10
|
function makeMockGitOutput(files) {
|
|
7
11
|
return files.join('\n') + (files.length > 0 ? '\n' : '');
|