claude-flow-novice 2.0.3 → 2.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/dist/src/cli/commands/guidance.js +487 -668
- package/dist/src/cli/commands/index-validate.js +18 -29
- package/dist/src/cli/commands/mcp-troubleshoot.js +230 -282
- package/dist/src/cli/commands/neural-goal-init.js +92 -125
- package/dist/src/cli/commands/swarm-exec.js +317 -393
- package/dist/src/cli/commands/swarm.js +1 -1
- package/dist/src/cli/commands/validate-framework.js +983 -1100
- package/dist/src/cli/commands/validate.js +144 -223
- package/dist/src/cli/simple-commands/__tests__/agent.test.js +265 -277
- package/dist/src/cli/simple-commands/__tests__/memory.test.js +6 -7
- package/dist/src/cli/simple-commands/__tests__/swarm.test.js +373 -356
- package/dist/src/cli/simple-commands/__tests__/task.test.js +6 -7
- package/dist/src/cli/simple-commands/agent.js +157 -193
- package/dist/src/cli/simple-commands/analysis.js +336 -446
- package/dist/src/cli/simple-commands/automation-executor.js +1095 -1339
- package/dist/src/cli/simple-commands/automation.js +481 -469
- package/dist/src/cli/simple-commands/batch-manager.js +261 -313
- package/dist/src/cli/simple-commands/claude-telemetry.js +241 -267
- package/dist/src/cli/simple-commands/claude-track.js +68 -90
- package/dist/src/cli/simple-commands/concurrent-display.js +266 -320
- package/dist/src/cli/simple-commands/config.js +245 -290
- package/dist/src/cli/simple-commands/coordination.js +182 -234
- package/dist/src/cli/simple-commands/enhanced-ui-views.js +812 -615
- package/dist/src/cli/simple-commands/enhanced-webui-complete.js +922 -981
- package/dist/src/cli/simple-commands/fix-hook-variables.js +274 -294
- package/dist/src/cli/simple-commands/github/gh-coordinator.js +378 -457
- package/dist/src/cli/simple-commands/github/github-api.js +535 -574
- package/dist/src/cli/simple-commands/github/init.js +276 -303
- package/dist/src/cli/simple-commands/github.js +222 -247
- package/dist/src/cli/simple-commands/goal.js +51 -63
- package/dist/src/cli/simple-commands/hive-mind/auto-save-middleware.js +208 -278
- package/dist/src/cli/simple-commands/hive-mind/communication.js +601 -696
- package/dist/src/cli/simple-commands/hive-mind/core.js +907 -979
- package/dist/src/cli/simple-commands/hive-mind/db-optimizer.js +406 -655
- package/dist/src/cli/simple-commands/hive-mind/mcp-wrapper.js +1125 -1245
- package/dist/src/cli/simple-commands/hive-mind/memory.js +854 -1090
- package/dist/src/cli/simple-commands/hive-mind/performance-optimizer.js +459 -574
- package/dist/src/cli/simple-commands/hive-mind/performance-test.js +263 -347
- package/dist/src/cli/simple-commands/hive-mind/queen.js +727 -768
- package/dist/src/cli/simple-commands/hive-mind/session-manager.js +745 -1049
- package/dist/src/cli/simple-commands/hive-mind-optimize.js +227 -283
- package/dist/src/cli/simple-commands/hive-mind-wizard.js +174 -217
- package/dist/src/cli/simple-commands/hive-mind.js +1842 -2283
- package/dist/src/cli/simple-commands/hive.js +90 -79
- package/dist/src/cli/simple-commands/hook-safety.js +431 -521
- package/dist/src/cli/simple-commands/hooks/session-start-soul.js +203 -254
- package/dist/src/cli/simple-commands/hooks.js +1064 -1204
- package/dist/src/cli/simple-commands/init/agent-copier.js +294 -319
- package/dist/src/cli/simple-commands/init/batch-init.js +496 -562
- package/dist/src/cli/simple-commands/init/claude-commands/claude-flow-commands.js +13 -19
- package/dist/src/cli/simple-commands/init/claude-commands/optimized-claude-flow-commands.js +13 -19
- package/dist/src/cli/simple-commands/init/claude-commands/optimized-slash-commands.js +61 -88
- package/dist/src/cli/simple-commands/init/claude-commands/optimized-sparc-commands.js +125 -150
- package/dist/src/cli/simple-commands/init/claude-commands/slash-commands.js +42 -49
- package/dist/src/cli/simple-commands/init/claude-commands/sparc-commands.js +43 -61
- package/dist/src/cli/simple-commands/init/copy-revised-templates.js +141 -147
- package/dist/src/cli/simple-commands/init/executable-wrapper.js +31 -44
- package/dist/src/cli/simple-commands/init/gitignore-updater.js +64 -90
- package/dist/src/cli/simple-commands/init/help.js +104 -107
- package/dist/src/cli/simple-commands/init/hive-mind-init.js +509 -528
- package/dist/src/cli/simple-commands/init/index.js +1510 -1759
- package/dist/src/cli/simple-commands/init/performance-monitor.js +234 -317
- package/dist/src/cli/simple-commands/init/rollback/backup-manager.js +441 -504
- package/dist/src/cli/simple-commands/init/rollback/index.js +289 -364
- package/dist/src/cli/simple-commands/init/rollback/recovery-manager.js +652 -728
- package/dist/src/cli/simple-commands/init/rollback/rollback-executor.js +416 -481
- package/dist/src/cli/simple-commands/init/rollback/state-tracker.js +369 -448
- package/dist/src/cli/simple-commands/init/sparc/roo-readme.js +1 -2
- package/dist/src/cli/simple-commands/init/sparc/roomodes-config.js +122 -99
- package/dist/src/cli/simple-commands/init/sparc/workflows.js +32 -37
- package/dist/src/cli/simple-commands/init/sparc-structure.js +55 -62
- package/dist/src/cli/simple-commands/init/template-copier.js +421 -533
- package/dist/src/cli/simple-commands/init/templates/coordination-md.js +3 -6
- package/dist/src/cli/simple-commands/init/templates/enhanced-templates.js +344 -318
- package/dist/src/cli/simple-commands/init/templates/github-safe-enhanced.js +173 -218
- package/dist/src/cli/simple-commands/init/templates/github-safe.js +65 -75
- package/dist/src/cli/simple-commands/init/templates/memory-bank-md.js +3 -6
- package/dist/src/cli/simple-commands/init/templates/readme-files.js +2 -4
- package/dist/src/cli/simple-commands/init/templates/safe-hook-patterns.js +187 -230
- package/dist/src/cli/simple-commands/init/templates/sparc-modes.js +53 -80
- package/dist/src/cli/simple-commands/init/templates/verification-claude-md.js +101 -85
- package/dist/src/cli/simple-commands/init/validation/config-validator.js +283 -330
- package/dist/src/cli/simple-commands/init/validation/health-checker.js +495 -561
- package/dist/src/cli/simple-commands/init/validation/index.js +302 -358
- package/dist/src/cli/simple-commands/init/validation/mode-validator.js +308 -359
- package/dist/src/cli/simple-commands/init/validation/post-init-validator.js +389 -366
- package/dist/src/cli/simple-commands/init/validation/pre-init-validator.js +270 -268
- package/dist/src/cli/simple-commands/init/validation/test-runner.js +427 -447
- package/dist/src/cli/simple-commands/init.js +1 -2
- package/dist/src/cli/simple-commands/mcp-health.js +131 -158
- package/dist/src/cli/simple-commands/mcp-integration-layer.js +533 -634
- package/dist/src/cli/simple-commands/mcp.js +345 -400
- package/dist/src/cli/simple-commands/memory-consolidation.js +426 -537
- package/dist/src/cli/simple-commands/memory.js +247 -311
- package/dist/src/cli/simple-commands/migrate-hooks.js +39 -46
- package/dist/src/cli/simple-commands/monitor.js +294 -363
- package/dist/src/cli/simple-commands/neural.js +51 -65
- package/dist/src/cli/simple-commands/pair-autofix-only.js +538 -662
- package/dist/src/cli/simple-commands/pair-basic.js +528 -656
- package/dist/src/cli/simple-commands/pair-old.js +430 -543
- package/dist/src/cli/simple-commands/pair-working.js +615 -751
- package/dist/src/cli/simple-commands/pair.js +615 -751
- package/dist/src/cli/simple-commands/performance-hooks.js +83 -111
- package/dist/src/cli/simple-commands/performance-metrics.js +348 -433
- package/dist/src/cli/simple-commands/process-ui-enhanced.js +708 -787
- package/dist/src/cli/simple-commands/process-ui.js +230 -254
- package/dist/src/cli/simple-commands/realtime-update-system.js +525 -611
- package/dist/src/cli/simple-commands/sparc/architecture.js +1704 -1530
- package/dist/src/cli/simple-commands/sparc/commands.js +438 -516
- package/dist/src/cli/simple-commands/sparc/completion.js +1224 -1481
- package/dist/src/cli/simple-commands/sparc/coordinator.js +913 -978
- package/dist/src/cli/simple-commands/sparc/index.js +241 -298
- package/dist/src/cli/simple-commands/sparc/phase-base.js +314 -390
- package/dist/src/cli/simple-commands/sparc/pseudocode.js +965 -869
- package/dist/src/cli/simple-commands/sparc/refinement.js +980 -1273
- package/dist/src/cli/simple-commands/sparc/specification.js +559 -645
- package/dist/src/cli/simple-commands/sparc-modes/architect.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/ask.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/code.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/debug.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/devops.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/docs-writer.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/generic.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/index.js +47 -55
- package/dist/src/cli/simple-commands/sparc-modes/integration.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/mcp.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/monitoring.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/optimization.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/security-review.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/sparc-orchestrator.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/spec-pseudocode.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/supabase-admin.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/swarm.js +101 -87
- package/dist/src/cli/simple-commands/sparc-modes/tdd.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/tutorial.js +1 -1
- package/dist/src/cli/simple-commands/sparc.js +465 -493
- package/dist/src/cli/simple-commands/start-ui.js +108 -132
- package/dist/src/cli/simple-commands/start-wrapper.js +240 -268
- package/dist/src/cli/simple-commands/start.js +1 -1
- package/dist/src/cli/simple-commands/status.js +254 -275
- package/dist/src/cli/simple-commands/stream-chain-clean.js +128 -171
- package/dist/src/cli/simple-commands/stream-chain-fixed.js +61 -82
- package/dist/src/cli/simple-commands/stream-chain-real.js +267 -331
- package/dist/src/cli/simple-commands/stream-chain-working.js +211 -263
- package/dist/src/cli/simple-commands/stream-chain.js +260 -318
- package/dist/src/cli/simple-commands/stream-processor.js +290 -315
- package/dist/src/cli/simple-commands/swarm-executor.js +189 -222
- package/dist/src/cli/simple-commands/swarm-metrics-integration.js +208 -300
- package/dist/src/cli/simple-commands/swarm-ui.js +623 -703
- package/dist/src/cli/simple-commands/swarm-webui-integration.js +258 -286
- package/dist/src/cli/simple-commands/swarm.js +887 -1082
- package/dist/src/cli/simple-commands/task.js +161 -206
- package/dist/src/cli/simple-commands/timestamp-fix.js +59 -89
- package/dist/src/cli/simple-commands/token-tracker.js +258 -316
- package/dist/src/cli/simple-commands/tool-execution-framework.js +433 -519
- package/dist/src/cli/simple-commands/train-and-stream.js +275 -331
- package/dist/src/cli/simple-commands/training-pipeline.js +619 -725
- package/dist/src/cli/simple-commands/training.js +170 -227
- package/dist/src/cli/simple-commands/verification-hooks.js +261 -284
- package/dist/src/cli/simple-commands/verification-integration.js +389 -417
- package/dist/src/cli/simple-commands/verification-training-integration.js +486 -606
- package/dist/src/cli/simple-commands/verification.js +493 -513
- package/dist/src/cli/simple-commands/web-server.js +766 -836
- package/dist/src/cli/simple-commands/webui-validator.js +106 -124
- package/dist/src/coordination/event-bus/demo-wasm-integration.js +212 -251
- package/dist/src/coordination/event-bus/qe-event-bus.js +608 -748
- package/dist/src/coordination/event-bus/qe-event-bus.test.js +379 -454
- package/dist/src/coordination/iteration-tracker.js +363 -454
- package/dist/src/enterprise/analytics-manager.js +1135 -0
- package/dist/src/enterprise/audit-manager.js +1115 -0
- package/dist/src/enterprise/cloud-manager.js +891 -0
- package/dist/src/enterprise/deployment-manager.js +966 -0
- package/dist/src/enterprise/index.js +6 -0
- package/dist/src/enterprise/project-manager.js +584 -0
- package/dist/src/enterprise/security-manager.js +991 -0
- package/dist/src/index.js +1 -1
- package/dist/src/mcp/DEPRECATED.js +46 -60
- package/dist/src/mcp/fixes/mcp-error-fixes.js +115 -134
- package/dist/src/mcp/implementations/agent-tracker.js +114 -128
- package/dist/src/mcp/implementations/daa-tools.js +292 -350
- package/dist/src/mcp/implementations/workflow-tools.js +329 -361
- package/dist/src/mcp/mcp-config-manager.js +1183 -1331
- package/dist/src/mcp/mcp-server-novice-simplified.js +11 -17
- package/dist/src/mcp/mcp-server-novice.js +11 -17
- package/dist/src/mcp/mcp-server-sdk.js +11 -17
- package/dist/src/mcp/mcp-server.js +1620 -1484
- package/dist/src/mcp/ruv-swarm-wrapper.js +209 -239
- package/dist/src/memory/advanced-serializer.js +609 -589
- package/dist/src/memory/enhanced-examples.js +220 -305
- package/dist/src/memory/enhanced-memory.js +295 -336
- package/dist/src/memory/enhanced-session-serializer.js +408 -492
- package/dist/src/memory/fallback-memory-system.js +900 -1021
- package/dist/src/memory/fallback-store.js +93 -131
- package/dist/src/memory/high-performance-serialization.js +592 -730
- package/dist/src/memory/in-memory-store.js +161 -213
- package/dist/src/memory/index.js +123 -157
- package/dist/src/memory/lock-free-structures.js +578 -764
- package/dist/src/memory/memory-mapped-persistence.js +585 -766
- package/dist/src/memory/memory-pressure-manager.js +569 -707
- package/dist/src/memory/migration.js +358 -445
- package/dist/src/memory/shared-memory.js +641 -768
- package/dist/src/memory/sqlite-store.js +245 -325
- package/dist/src/memory/sqlite-wrapper.js +122 -151
- package/dist/src/memory/swarm-memory.js +470 -603
- package/dist/src/memory/test-example.js +126 -134
- package/dist/src/memory/ultra-fast-memory-store.js +622 -821
- package/dist/src/memory/unified-memory-manager.js +356 -437
- package/dist/src/migration/index.js +92 -0
- package/dist/src/migration/logger.js +121 -0
- package/dist/src/migration/migration-analyzer.js +268 -0
- package/dist/src/migration/migration-runner.js +522 -0
- package/dist/src/migration/migration-validator.js +285 -0
- package/dist/src/migration/progress-reporter.js +150 -0
- package/dist/src/migration/rollback-manager.js +321 -0
- package/dist/src/migration/tests/migration-system.test.js +7 -0
- package/dist/src/migration/types.js +3 -0
- package/dist/src/swarm/CodeRefactoringSwarm.js +777 -952
- package/dist/src/swarm/__tests__/integration.test.js +227 -0
- package/dist/src/swarm/__tests__/prompt-copier.test.js +344 -0
- package/dist/src/swarm/advanced-orchestrator.js +1095 -0
- package/dist/src/swarm/claude-code-interface.js +961 -0
- package/dist/src/swarm/claude-flow-executor.js +229 -0
- package/dist/src/swarm/consensus-coordinator.js +475 -0
- package/dist/src/swarm/coordinator.js +2993 -0
- package/dist/src/swarm/direct-executor.js +1180 -0
- package/dist/src/swarm/error-recovery/advanced-error-detection.js +691 -0
- package/dist/src/swarm/error-recovery/automated-recovery-workflows.js +998 -0
- package/dist/src/swarm/error-recovery/error-recovery-coordinator.js +1197 -0
- package/dist/src/swarm/error-recovery/recovery-monitoring.js +772 -0
- package/dist/src/swarm/error-recovery/resilience-architecture.js +714 -0
- package/dist/src/swarm/error-recovery/self-healing-mechanisms.js +1319 -0
- package/dist/src/swarm/error-recovery/test-error-recovery-effectiveness.js +808 -0
- package/dist/src/swarm/executor-v2.js +322 -0
- package/dist/src/swarm/executor.js +815 -0
- package/dist/src/swarm/hive-mind-integration.js +703 -0
- package/dist/src/swarm/index.js +41 -0
- package/dist/src/swarm/json-output-aggregator.js +267 -0
- package/dist/src/swarm/large-scale-coordinator.js +542 -0
- package/dist/src/swarm/mcp-integration-wrapper.js +628 -0
- package/dist/src/swarm/memory.js +1117 -0
- package/dist/src/swarm/optimizations/__tests__/optimization.test.js +348 -0
- package/dist/src/swarm/optimizations/async-file-manager.js +285 -0
- package/dist/src/swarm/optimizations/circular-buffer.js +162 -0
- package/dist/src/swarm/optimizations/connection-pool.js +244 -0
- package/dist/src/swarm/optimizations/index.js +28 -0
- package/dist/src/swarm/optimizations/optimized-executor.js +320 -0
- package/dist/src/swarm/optimizations/ttl-map.js +234 -0
- package/dist/src/swarm/prompt-cli.js +200 -0
- package/dist/src/swarm/prompt-copier-enhanced.js +202 -0
- package/dist/src/swarm/prompt-copier.js +381 -0
- package/dist/src/swarm/prompt-manager.js +295 -0
- package/dist/src/swarm/prompt-utils.js +310 -0
- package/dist/src/swarm/result-aggregator.js +718 -0
- package/dist/src/swarm/sparc-executor.js +1568 -0
- package/dist/src/swarm/strategies/auto.js +758 -0
- package/dist/src/swarm/strategies/base.js +128 -0
- package/dist/src/swarm/strategies/research.js +914 -0
- package/dist/src/swarm/strategies/strategy-metrics-patch.js +2 -0
- package/dist/src/swarm/types.js +52 -0
- package/dist/src/swarm/workers/copy-worker.js +56 -0
- package/dist/src/utils/__tests__/github-cli-safety-wrapper.test.js +332 -400
- package/dist/src/utils/github-cli-safe.js +56 -64
- package/dist/src/utils/github-cli-safety-wrapper.js +451 -546
- package/dist/src/utils/npx-isolated-cache.js +104 -119
- package/dist/src/utils/preference-manager.js +622 -652
- package/dist/src/utils/timezone-utils.js +86 -105
- package/dist/src/validators/epic-config-schema.js +214 -0
- package/dist/src/validators/index.js +10 -0
- package/dist/src/validators/swarm-init-validator.js +259 -0
- package/dist/src/validators/todowrite-batching-validator.js +215 -0
- package/dist/src/validators/todowrite-integration.js +187 -0
- package/package.json +2 -2
|
@@ -2,422 +2,354 @@
|
|
|
2
2
|
* Test suite for GitHub CLI Safety Wrapper
|
|
3
3
|
*
|
|
4
4
|
* Tests all security features, error handling, and functionality
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
GitHubCliSafe,
|
|
10
|
-
createGitHubCliSafe,
|
|
11
|
-
githubCli,
|
|
12
|
-
GitHubCliError,
|
|
13
|
-
GitHubCliTimeoutError,
|
|
14
|
-
GitHubCliValidationError,
|
|
15
|
-
GitHubCliRateLimitError,
|
|
16
|
-
} from '../github-cli-safety-wrapper.js';
|
|
17
|
-
import { spawn } from 'child_process';
|
|
18
|
-
import { promises as fs } from 'fs';
|
|
19
|
-
|
|
5
|
+
*/ import { jest } from "@jest/globals";
|
|
6
|
+
import { GitHubCliSafe, createGitHubCliSafe, githubCli, GitHubCliError, GitHubCliTimeoutError, GitHubCliValidationError, GitHubCliRateLimitError } from "../github-cli-safety-wrapper.js";
|
|
7
|
+
import { spawn } from "child_process";
|
|
8
|
+
import { promises as fs } from "fs";
|
|
20
9
|
// Mock child_process and fs
|
|
21
10
|
jest.mock('child_process');
|
|
22
|
-
jest.mock('fs', ()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
describe('GitHubCliSafe', () => {
|
|
30
|
-
let ghSafe;
|
|
31
|
-
let mockSpawn;
|
|
32
|
-
let mockChild;
|
|
33
|
-
|
|
34
|
-
beforeEach(() => {
|
|
35
|
-
ghSafe = new GitHubCliSafe({
|
|
36
|
-
timeout: 5000,
|
|
37
|
-
enableRateLimit: false,
|
|
38
|
-
enableLogging: false,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
mockChild = {
|
|
42
|
-
kill: jest.fn(),
|
|
43
|
-
on: jest.fn(),
|
|
44
|
-
stdout: { on: jest.fn() },
|
|
45
|
-
stderr: { on: jest.fn() },
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
mockSpawn = spawn.mockReturnValue(mockChild);
|
|
49
|
-
fs.writeFile.mockResolvedValue();
|
|
50
|
-
fs.unlink.mockResolvedValue();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
afterEach(() => {
|
|
54
|
-
jest.clearAllMocks();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe('Input Validation', () => {
|
|
58
|
-
test('should validate allowed commands', () => {
|
|
59
|
-
expect(() => ghSafe.validateCommand('issue create')).not.toThrow();
|
|
60
|
-
expect(() => ghSafe.validateCommand('pr comment')).not.toThrow();
|
|
61
|
-
|
|
62
|
-
expect(() => ghSafe.validateCommand('rm -rf /')).toThrow(GitHubCliValidationError);
|
|
63
|
-
expect(() => ghSafe.validateCommand('maliciouscommand')).toThrow(GitHubCliValidationError);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('should reject empty or invalid commands', () => {
|
|
67
|
-
expect(() => ghSafe.validateCommand('')).toThrow(GitHubCliValidationError);
|
|
68
|
-
expect(() => ghSafe.validateCommand(null)).toThrow(GitHubCliValidationError);
|
|
69
|
-
expect(() => ghSafe.validateCommand(undefined)).toThrow(GitHubCliValidationError);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('should sanitize dangerous input patterns', () => {
|
|
73
|
-
const dangerousInputs = [
|
|
74
|
-
'$(rm -rf /)',
|
|
75
|
-
'`rm -rf /`',
|
|
76
|
-
'test && rm -rf /',
|
|
77
|
-
'test || rm -rf /',
|
|
78
|
-
'test; rm -rf /',
|
|
79
|
-
'test <(echo malicious)',
|
|
80
|
-
'test > /dev/null',
|
|
81
|
-
'test | sh',
|
|
82
|
-
'eval("malicious")',
|
|
83
|
-
'exec("malicious")',
|
|
84
|
-
];
|
|
85
|
-
|
|
86
|
-
dangerousInputs.forEach((input) => {
|
|
87
|
-
expect(() => ghSafe.sanitizeInput(input)).toThrow(GitHubCliValidationError);
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('should allow safe input', () => {
|
|
92
|
-
const safeInputs = [
|
|
93
|
-
'This is a normal comment',
|
|
94
|
-
'Code example: console.log("hello")',
|
|
95
|
-
'File path: /src/components/Button.jsx',
|
|
96
|
-
'Numbers: 123, versions: v1.2.3',
|
|
97
|
-
'Special chars: @#$%^&*()_+-=[]{}|;:,.<>?',
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
safeInputs.forEach((input) => {
|
|
101
|
-
expect(() => ghSafe.sanitizeInput(input)).not.toThrow();
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('should validate body size limits', () => {
|
|
106
|
-
const largeBody = 'x'.repeat(1024 * 1024 + 1); // > 1MB
|
|
107
|
-
expect(() => ghSafe.validateBodySize(largeBody)).toThrow(GitHubCliValidationError);
|
|
108
|
-
|
|
109
|
-
const normalBody = 'x'.repeat(1000);
|
|
110
|
-
expect(() => ghSafe.validateBodySize(normalBody)).not.toThrow();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('Process Management', () => {
|
|
115
|
-
test('should spawn process with correct arguments', async () => {
|
|
116
|
-
const mockProcess = setupMockProcess(0, 'success', '');
|
|
117
|
-
|
|
118
|
-
await ghSafe.execute('issue create', {
|
|
119
|
-
title: 'Test Issue',
|
|
120
|
-
body: 'Test body',
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
expect(spawn).toHaveBeenCalledWith(
|
|
124
|
-
'gh',
|
|
125
|
-
expect.arrayContaining(['issue', 'create', '--title', 'Test Issue']),
|
|
126
|
-
expect.objectContaining({
|
|
127
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
128
|
-
shell: false,
|
|
129
|
-
}),
|
|
130
|
-
);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test('should handle process timeout', async () => {
|
|
134
|
-
setupMockProcess(null, '', '', true); // Never completes
|
|
135
|
-
|
|
136
|
-
await expect(ghSafe.execute('issue create', { title: 'Test' })).rejects.toThrow(
|
|
137
|
-
GitHubCliTimeoutError,
|
|
138
|
-
);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test('should handle process failure', async () => {
|
|
142
|
-
setupMockProcess(1, '', 'Command failed');
|
|
143
|
-
|
|
144
|
-
await expect(ghSafe.execute('issue create', { title: 'Test' })).rejects.toThrow(
|
|
145
|
-
GitHubCliError,
|
|
146
|
-
);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('should cleanup processes on timeout', async () => {
|
|
150
|
-
setupMockProcess(null, '', '', true); // Never completes
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
await ghSafe.execute('issue create', { title: 'Test' });
|
|
154
|
-
} catch (error) {
|
|
155
|
-
expect(mockChild.kill).toHaveBeenCalledWith('SIGTERM');
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('Retry Logic', () => {
|
|
161
|
-
test('should retry on transient failures', async () => {
|
|
162
|
-
let attemptCount = 0;
|
|
163
|
-
const operation = jest.fn(() => {
|
|
164
|
-
attemptCount++;
|
|
165
|
-
if (attemptCount < 3) {
|
|
166
|
-
throw new GitHubCliError('Transient error');
|
|
11
|
+
jest.mock('fs', ()=>({
|
|
12
|
+
promises: {
|
|
13
|
+
writeFile: jest.fn(),
|
|
14
|
+
unlink: jest.fn()
|
|
167
15
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe('Rate Limiting', () => {
|
|
196
|
-
test('should enforce rate limits', async () => {
|
|
197
|
-
const rateLimitedGh = new GitHubCliSafe({
|
|
198
|
-
enableRateLimit: true,
|
|
199
|
-
maxRequestsPerWindow: 2,
|
|
200
|
-
rateLimitWindow: 1000,
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// Mock successful executions
|
|
204
|
-
setupMockProcess(0, 'success', '');
|
|
205
|
-
|
|
206
|
-
// First two requests should succeed
|
|
207
|
-
await rateLimitedGh.execute('auth status');
|
|
208
|
-
await rateLimitedGh.execute('auth status');
|
|
209
|
-
|
|
210
|
-
// Third request should be rate limited
|
|
211
|
-
await expect(rateLimitedGh.execute('auth status')).rejects.toThrow(GitHubCliRateLimitError);
|
|
16
|
+
}));
|
|
17
|
+
describe('GitHubCliSafe', ()=>{
|
|
18
|
+
let ghSafe;
|
|
19
|
+
let mockSpawn;
|
|
20
|
+
let mockChild;
|
|
21
|
+
beforeEach(()=>{
|
|
22
|
+
ghSafe = new GitHubCliSafe({
|
|
23
|
+
timeout: 5000,
|
|
24
|
+
enableRateLimit: false,
|
|
25
|
+
enableLogging: false
|
|
26
|
+
});
|
|
27
|
+
mockChild = {
|
|
28
|
+
kill: jest.fn(),
|
|
29
|
+
on: jest.fn(),
|
|
30
|
+
stdout: {
|
|
31
|
+
on: jest.fn()
|
|
32
|
+
},
|
|
33
|
+
stderr: {
|
|
34
|
+
on: jest.fn()
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
mockSpawn = spawn.mockReturnValue(mockChild);
|
|
38
|
+
fs.writeFile.mockResolvedValue();
|
|
39
|
+
fs.unlink.mockResolvedValue();
|
|
212
40
|
});
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
describe('Temp File Handling', () => {
|
|
216
|
-
test('should create secure temp files for body content', async () => {
|
|
217
|
-
setupMockProcess(0, 'success', '');
|
|
218
|
-
|
|
219
|
-
await ghSafe.execute('issue create', {
|
|
220
|
-
title: 'Test',
|
|
221
|
-
body: 'Test body',
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
225
|
-
expect.stringMatching(/gh-safe-.*\.tmp$/),
|
|
226
|
-
'Test body',
|
|
227
|
-
{ mode: 0o600 },
|
|
228
|
-
);
|
|
41
|
+
afterEach(()=>{
|
|
42
|
+
jest.clearAllMocks();
|
|
229
43
|
});
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
44
|
+
describe('Input Validation', ()=>{
|
|
45
|
+
test('should validate allowed commands', ()=>{
|
|
46
|
+
expect(()=>ghSafe.validateCommand('issue create')).not.toThrow();
|
|
47
|
+
expect(()=>ghSafe.validateCommand('pr comment')).not.toThrow();
|
|
48
|
+
expect(()=>ghSafe.validateCommand('rm -rf /')).toThrow(GitHubCliValidationError);
|
|
49
|
+
expect(()=>ghSafe.validateCommand('maliciouscommand')).toThrow(GitHubCliValidationError);
|
|
50
|
+
});
|
|
51
|
+
test('should reject empty or invalid commands', ()=>{
|
|
52
|
+
expect(()=>ghSafe.validateCommand('')).toThrow(GitHubCliValidationError);
|
|
53
|
+
expect(()=>ghSafe.validateCommand(null)).toThrow(GitHubCliValidationError);
|
|
54
|
+
expect(()=>ghSafe.validateCommand(undefined)).toThrow(GitHubCliValidationError);
|
|
55
|
+
});
|
|
56
|
+
test('should sanitize dangerous input patterns', ()=>{
|
|
57
|
+
const dangerousInputs = [
|
|
58
|
+
'$(rm -rf /)',
|
|
59
|
+
'`rm -rf /`',
|
|
60
|
+
'test && rm -rf /',
|
|
61
|
+
'test || rm -rf /',
|
|
62
|
+
'test; rm -rf /',
|
|
63
|
+
'test <(echo malicious)',
|
|
64
|
+
'test > /dev/null',
|
|
65
|
+
'test | sh',
|
|
66
|
+
'eval("malicious")',
|
|
67
|
+
'exec("malicious")'
|
|
68
|
+
];
|
|
69
|
+
dangerousInputs.forEach((input)=>{
|
|
70
|
+
expect(()=>ghSafe.sanitizeInput(input)).toThrow(GitHubCliValidationError);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
test('should allow safe input', ()=>{
|
|
74
|
+
const safeInputs = [
|
|
75
|
+
'This is a normal comment',
|
|
76
|
+
'Code example: console.log("hello")',
|
|
77
|
+
'File path: /src/components/Button.jsx',
|
|
78
|
+
'Numbers: 123, versions: v1.2.3',
|
|
79
|
+
'Special chars: @#$%^&*()_+-=[]{}|;:,.<>?'
|
|
80
|
+
];
|
|
81
|
+
safeInputs.forEach((input)=>{
|
|
82
|
+
expect(()=>ghSafe.sanitizeInput(input)).not.toThrow();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
test('should validate body size limits', ()=>{
|
|
86
|
+
const largeBody = 'x'.repeat(1024 * 1024 + 1); // > 1MB
|
|
87
|
+
expect(()=>ghSafe.validateBodySize(largeBody)).toThrow(GitHubCliValidationError);
|
|
88
|
+
const normalBody = 'x'.repeat(1000);
|
|
89
|
+
expect(()=>ghSafe.validateBodySize(normalBody)).not.toThrow();
|
|
238
90
|
});
|
|
239
|
-
} catch (error) {
|
|
240
|
-
// Expected to fail
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
expect(fs.unlink).toHaveBeenCalled();
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
describe('High-level Operations', () => {
|
|
248
|
-
beforeEach(() => {
|
|
249
|
-
setupMockProcess(0, 'Issue created successfully', '');
|
|
250
91
|
});
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
92
|
+
describe('Process Management', ()=>{
|
|
93
|
+
test('should spawn process with correct arguments', async ()=>{
|
|
94
|
+
const mockProcess = setupMockProcess(0, 'success', '');
|
|
95
|
+
await ghSafe.execute('issue create', {
|
|
96
|
+
title: 'Test Issue',
|
|
97
|
+
body: 'Test body'
|
|
98
|
+
});
|
|
99
|
+
expect(spawn).toHaveBeenCalledWith('gh', expect.arrayContaining([
|
|
100
|
+
'issue',
|
|
101
|
+
'create',
|
|
102
|
+
'--title',
|
|
103
|
+
'Test Issue'
|
|
104
|
+
]), expect.objectContaining({
|
|
105
|
+
stdio: [
|
|
106
|
+
'ignore',
|
|
107
|
+
'pipe',
|
|
108
|
+
'pipe'
|
|
109
|
+
],
|
|
110
|
+
shell: false
|
|
111
|
+
}));
|
|
112
|
+
});
|
|
113
|
+
test('should handle process timeout', async ()=>{
|
|
114
|
+
setupMockProcess(null, '', '', true); // Never completes
|
|
115
|
+
await expect(ghSafe.execute('issue create', {
|
|
116
|
+
title: 'Test'
|
|
117
|
+
})).rejects.toThrow(GitHubCliTimeoutError);
|
|
118
|
+
});
|
|
119
|
+
test('should handle process failure', async ()=>{
|
|
120
|
+
setupMockProcess(1, '', 'Command failed');
|
|
121
|
+
await expect(ghSafe.execute('issue create', {
|
|
122
|
+
title: 'Test'
|
|
123
|
+
})).rejects.toThrow(GitHubCliError);
|
|
124
|
+
});
|
|
125
|
+
test('should cleanup processes on timeout', async ()=>{
|
|
126
|
+
setupMockProcess(null, '', '', true); // Never completes
|
|
127
|
+
try {
|
|
128
|
+
await ghSafe.execute('issue create', {
|
|
129
|
+
title: 'Test'
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
expect(mockChild.kill).toHaveBeenCalledWith('SIGTERM');
|
|
133
|
+
}
|
|
134
|
+
});
|
|
276
135
|
});
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
136
|
+
describe('Retry Logic', ()=>{
|
|
137
|
+
test('should retry on transient failures', async ()=>{
|
|
138
|
+
let attemptCount = 0;
|
|
139
|
+
const operation = jest.fn(()=>{
|
|
140
|
+
attemptCount++;
|
|
141
|
+
if (attemptCount < 3) {
|
|
142
|
+
throw new GitHubCliError('Transient error');
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
success: true
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
const result = await ghSafe.withRetry(operation, 3);
|
|
149
|
+
expect(result.success).toBe(true);
|
|
150
|
+
expect(operation).toHaveBeenCalledTimes(3);
|
|
151
|
+
});
|
|
152
|
+
test('should not retry validation errors', async ()=>{
|
|
153
|
+
const operation = jest.fn(()=>{
|
|
154
|
+
throw new GitHubCliValidationError('Invalid input');
|
|
155
|
+
});
|
|
156
|
+
await expect(ghSafe.withRetry(operation, 3)).rejects.toThrow(GitHubCliValidationError);
|
|
157
|
+
expect(operation).toHaveBeenCalledTimes(1);
|
|
158
|
+
});
|
|
159
|
+
test('should not retry rate limit errors', async ()=>{
|
|
160
|
+
const operation = jest.fn(()=>{
|
|
161
|
+
throw new GitHubCliRateLimitError('Rate limited');
|
|
162
|
+
});
|
|
163
|
+
await expect(ghSafe.withRetry(operation, 3)).rejects.toThrow(GitHubCliRateLimitError);
|
|
164
|
+
expect(operation).toHaveBeenCalledTimes(1);
|
|
165
|
+
});
|
|
304
166
|
});
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
167
|
+
describe('Rate Limiting', ()=>{
|
|
168
|
+
test('should enforce rate limits', async ()=>{
|
|
169
|
+
const rateLimitedGh = new GitHubCliSafe({
|
|
170
|
+
enableRateLimit: true,
|
|
171
|
+
maxRequestsPerWindow: 2,
|
|
172
|
+
rateLimitWindow: 1000
|
|
173
|
+
});
|
|
174
|
+
// Mock successful executions
|
|
175
|
+
setupMockProcess(0, 'success', '');
|
|
176
|
+
// First two requests should succeed
|
|
177
|
+
await rateLimitedGh.execute('auth status');
|
|
178
|
+
await rateLimitedGh.execute('auth status');
|
|
179
|
+
// Third request should be rate limited
|
|
180
|
+
await expect(rateLimitedGh.execute('auth status')).rejects.toThrow(GitHubCliRateLimitError);
|
|
181
|
+
});
|
|
320
182
|
});
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
183
|
+
describe('Temp File Handling', ()=>{
|
|
184
|
+
test('should create secure temp files for body content', async ()=>{
|
|
185
|
+
setupMockProcess(0, 'success', '');
|
|
186
|
+
await ghSafe.execute('issue create', {
|
|
187
|
+
title: 'Test',
|
|
188
|
+
body: 'Test body'
|
|
189
|
+
});
|
|
190
|
+
expect(fs.writeFile).toHaveBeenCalledWith(expect.stringMatching(/gh-safe-.*\.tmp$/), 'Test body', {
|
|
191
|
+
mode: 0o600
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
test('should cleanup temp files even on error', async ()=>{
|
|
195
|
+
setupMockProcess(1, '', 'Error');
|
|
196
|
+
try {
|
|
197
|
+
await ghSafe.execute('issue create', {
|
|
198
|
+
title: 'Test',
|
|
199
|
+
body: 'Test body'
|
|
200
|
+
});
|
|
201
|
+
} catch (error) {
|
|
202
|
+
// Expected to fail
|
|
203
|
+
}
|
|
204
|
+
expect(fs.unlink).toHaveBeenCalled();
|
|
205
|
+
});
|
|
331
206
|
});
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
207
|
+
describe('High-level Operations', ()=>{
|
|
208
|
+
beforeEach(()=>{
|
|
209
|
+
setupMockProcess(0, 'Issue created successfully', '');
|
|
210
|
+
});
|
|
211
|
+
test('should create issue with proper arguments', async ()=>{
|
|
212
|
+
await ghSafe.createIssue({
|
|
213
|
+
title: 'Bug Report',
|
|
214
|
+
body: 'Found a bug',
|
|
215
|
+
labels: [
|
|
216
|
+
'bug',
|
|
217
|
+
'high-priority'
|
|
218
|
+
],
|
|
219
|
+
assignees: [
|
|
220
|
+
'user1',
|
|
221
|
+
'user2'
|
|
222
|
+
]
|
|
223
|
+
});
|
|
224
|
+
expect(spawn).toHaveBeenCalledWith('gh', expect.arrayContaining([
|
|
225
|
+
'issue',
|
|
226
|
+
'create',
|
|
227
|
+
'--title',
|
|
228
|
+
'Bug Report',
|
|
229
|
+
'--body-file',
|
|
230
|
+
expect.stringMatching(/gh-safe-.*\.tmp$/),
|
|
231
|
+
'--label',
|
|
232
|
+
'bug,high-priority',
|
|
233
|
+
'--assignee',
|
|
234
|
+
'user1,user2'
|
|
235
|
+
]), expect.any(Object));
|
|
236
|
+
});
|
|
237
|
+
test('should create PR with proper arguments', async ()=>{
|
|
238
|
+
await ghSafe.createPR({
|
|
239
|
+
title: 'Feature: Add new component',
|
|
240
|
+
body: 'Adds new component',
|
|
241
|
+
base: 'develop',
|
|
242
|
+
head: 'feature/new-component',
|
|
243
|
+
draft: true
|
|
244
|
+
});
|
|
245
|
+
expect(spawn).toHaveBeenCalledWith('gh', expect.arrayContaining([
|
|
246
|
+
'pr',
|
|
247
|
+
'create',
|
|
248
|
+
'--title',
|
|
249
|
+
'Feature: Add new component',
|
|
250
|
+
'--body-file',
|
|
251
|
+
expect.stringMatching(/gh-safe-.*\.tmp$/),
|
|
252
|
+
'--base',
|
|
253
|
+
'develop',
|
|
254
|
+
'--head',
|
|
255
|
+
'feature/new-component',
|
|
256
|
+
'--draft'
|
|
257
|
+
]), expect.any(Object));
|
|
258
|
+
});
|
|
259
|
+
test('should add issue comment', async ()=>{
|
|
260
|
+
await ghSafe.addIssueComment(123, 'This is a comment');
|
|
261
|
+
expect(spawn).toHaveBeenCalledWith('gh', expect.arrayContaining([
|
|
262
|
+
'issue',
|
|
263
|
+
'comment',
|
|
264
|
+
'123',
|
|
265
|
+
'--body-file',
|
|
266
|
+
expect.stringMatching(/gh-safe-.*\.tmp$/)
|
|
267
|
+
]), expect.any(Object));
|
|
268
|
+
});
|
|
338
269
|
});
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
270
|
+
describe('Utility Methods', ()=>{
|
|
271
|
+
test('should check GitHub CLI availability', async ()=>{
|
|
272
|
+
// Mock execSync for version check
|
|
273
|
+
const { execSync } = require('child_process');
|
|
274
|
+
jest.spyOn(require('child_process'), 'execSync').mockImplementation(()=>'gh version 2.0.0');
|
|
275
|
+
const isAvailable = await ghSafe.checkGitHubCli();
|
|
276
|
+
expect(isAvailable).toBe(true);
|
|
277
|
+
});
|
|
278
|
+
test('should get stats', ()=>{
|
|
279
|
+
const stats = ghSafe.getStats();
|
|
280
|
+
expect(stats).toHaveProperty('totalRequests');
|
|
281
|
+
expect(stats).toHaveProperty('successfulRequests');
|
|
282
|
+
expect(stats).toHaveProperty('failedRequests');
|
|
283
|
+
});
|
|
284
|
+
test('should cleanup active processes', async ()=>{
|
|
285
|
+
ghSafe.activeProcesses.set('test-process', mockChild);
|
|
286
|
+
await ghSafe.cleanup();
|
|
287
|
+
expect(mockChild.kill).toHaveBeenCalledWith('SIGTERM');
|
|
288
|
+
expect(ghSafe.activeProcesses.size).toBe(0);
|
|
289
|
+
});
|
|
347
290
|
});
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
expect(instance.options.timeout).toBe(10000);
|
|
359
|
-
expect(instance.options.maxRetries).toBe(5);
|
|
291
|
+
describe('Factory Function', ()=>{
|
|
292
|
+
test('should create configured instance', ()=>{
|
|
293
|
+
const instance = createGitHubCliSafe({
|
|
294
|
+
timeout: 10000,
|
|
295
|
+
maxRetries: 5
|
|
296
|
+
});
|
|
297
|
+
expect(instance).toBeInstanceOf(GitHubCliSafe);
|
|
298
|
+
expect(instance.options.timeout).toBe(10000);
|
|
299
|
+
expect(instance.options.maxRetries).toBe(5);
|
|
300
|
+
});
|
|
360
301
|
});
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
expect(githubCli).toBeInstanceOf(GitHubCliSafe);
|
|
302
|
+
describe('Singleton Instance', ()=>{
|
|
303
|
+
test('should provide default singleton', ()=>{
|
|
304
|
+
expect(githubCli).toBeInstanceOf(GitHubCliSafe);
|
|
305
|
+
});
|
|
366
306
|
});
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
307
|
+
// Helper function to setup mock process behavior
|
|
308
|
+
function setupMockProcess(exitCode, stdout = '', stderr = '', neverComplete = false) {
|
|
309
|
+
let closeCallback, errorCallback;
|
|
310
|
+
let stdoutCallback, stderrCallback;
|
|
311
|
+
mockChild.on.mockImplementation((event, callback)=>{
|
|
312
|
+
if (event === 'close') closeCallback = callback;
|
|
313
|
+
if (event === 'error') errorCallback = callback;
|
|
314
|
+
});
|
|
315
|
+
mockChild.stdout.on.mockImplementation((event, callback)=>{
|
|
316
|
+
if (event === 'data') stdoutCallback = callback;
|
|
317
|
+
});
|
|
318
|
+
mockChild.stderr.on.mockImplementation((event, callback)=>{
|
|
319
|
+
if (event === 'data') stderrCallback = callback;
|
|
320
|
+
});
|
|
321
|
+
// Simulate process execution
|
|
322
|
+
if (!neverComplete) {
|
|
323
|
+
setTimeout(()=>{
|
|
324
|
+
if (stdoutCallback && stdout) stdoutCallback(Buffer.from(stdout));
|
|
325
|
+
if (stderrCallback && stderr) stderrCallback(Buffer.from(stderr));
|
|
326
|
+
if (closeCallback) closeCallback(exitCode);
|
|
327
|
+
}, 10);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
describe('Error Classes', ()=>{
|
|
332
|
+
test('GitHubCliError should contain proper details', ()=>{
|
|
333
|
+
const error = new GitHubCliError('Test error', 'TEST_CODE', {
|
|
334
|
+
test: true
|
|
335
|
+
});
|
|
336
|
+
expect(error.name).toBe('GitHubCliError');
|
|
337
|
+
expect(error.code).toBe('TEST_CODE');
|
|
338
|
+
expect(error.details).toEqual({
|
|
339
|
+
test: true
|
|
340
|
+
});
|
|
341
|
+
expect(error.timestamp).toBeDefined();
|
|
377
342
|
});
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
343
|
+
test('GitHubCliTimeoutError should extend GitHubCliError', ()=>{
|
|
344
|
+
const error = new GitHubCliTimeoutError(5000, 'gh issue create');
|
|
345
|
+
expect(error).toBeInstanceOf(GitHubCliError);
|
|
346
|
+
expect(error.name).toBe('GitHubCliTimeoutError');
|
|
347
|
+
expect(error.code).toBe('TIMEOUT');
|
|
381
348
|
});
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
349
|
+
test('GitHubCliValidationError should contain field info', ()=>{
|
|
350
|
+
const error = new GitHubCliValidationError('Invalid field', 'testField', 'testValue');
|
|
351
|
+
expect(error.name).toBe('GitHubCliValidationError');
|
|
352
|
+
expect(error.details.field).toBe('testField');
|
|
353
|
+
expect(error.details.value).toBe('testValue');
|
|
385
354
|
});
|
|
386
|
-
|
|
387
|
-
// Simulate process execution
|
|
388
|
-
if (!neverComplete) {
|
|
389
|
-
setTimeout(() => {
|
|
390
|
-
if (stdoutCallback && stdout) stdoutCallback(Buffer.from(stdout));
|
|
391
|
-
if (stderrCallback && stderr) stderrCallback(Buffer.from(stderr));
|
|
392
|
-
if (closeCallback) closeCallback(exitCode);
|
|
393
|
-
}, 10);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
describe('Error Classes', () => {
|
|
399
|
-
test('GitHubCliError should contain proper details', () => {
|
|
400
|
-
const error = new GitHubCliError('Test error', 'TEST_CODE', { test: true });
|
|
401
|
-
|
|
402
|
-
expect(error.name).toBe('GitHubCliError');
|
|
403
|
-
expect(error.code).toBe('TEST_CODE');
|
|
404
|
-
expect(error.details).toEqual({ test: true });
|
|
405
|
-
expect(error.timestamp).toBeDefined();
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
test('GitHubCliTimeoutError should extend GitHubCliError', () => {
|
|
409
|
-
const error = new GitHubCliTimeoutError(5000, 'gh issue create');
|
|
410
|
-
|
|
411
|
-
expect(error).toBeInstanceOf(GitHubCliError);
|
|
412
|
-
expect(error.name).toBe('GitHubCliTimeoutError');
|
|
413
|
-
expect(error.code).toBe('TIMEOUT');
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
test('GitHubCliValidationError should contain field info', () => {
|
|
417
|
-
const error = new GitHubCliValidationError('Invalid field', 'testField', 'testValue');
|
|
418
|
-
|
|
419
|
-
expect(error.name).toBe('GitHubCliValidationError');
|
|
420
|
-
expect(error.details.field).toBe('testField');
|
|
421
|
-
expect(error.details.value).toBe('testValue');
|
|
422
|
-
});
|
|
423
355
|
});
|