@agents-at-scale/ark 0.1.52 → 0.1.55

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.
Files changed (116) hide show
  1. package/dist/arkServices.js +11 -7
  2. package/dist/commands/export/index.js +6 -4
  3. package/dist/commands/generate/generators/agent.js +2 -0
  4. package/dist/commands/generate/generators/marketplace.js +2 -0
  5. package/dist/commands/generate/generators/mcpserver.js +2 -0
  6. package/dist/commands/generate/generators/project.js +9 -2
  7. package/dist/commands/generate/generators/query.js +2 -0
  8. package/dist/commands/generate/generators/team.js +2 -0
  9. package/dist/commands/generate/templateDiscovery.js +1 -0
  10. package/dist/commands/generate/templateEngine.js +1 -3
  11. package/dist/commands/import/index.js +1 -1
  12. package/dist/commands/install/index.js +2 -1
  13. package/dist/commands/models/kubernetes/manifest-builder.js +27 -10
  14. package/dist/commands/models/providers/azure.d.ts +10 -7
  15. package/dist/commands/models/providers/azure.js +83 -21
  16. package/dist/commands/uninstall/index.js +1 -1
  17. package/dist/components/ChatUI.js +17 -16
  18. package/dist/components/statusChecker.js +3 -3
  19. package/dist/lib/arkApiClient.js +11 -9
  20. package/dist/lib/arkApiProxy.js +1 -0
  21. package/dist/lib/arkServiceProxy.js +5 -1
  22. package/dist/lib/chatClient.js +9 -0
  23. package/dist/lib/config.js +8 -3
  24. package/dist/lib/errors.js +3 -0
  25. package/dist/ui/asyncOperations/connectingToArk.js +2 -2
  26. package/package.json +17 -13
  27. package/dist/arkServices.spec.d.ts +0 -1
  28. package/dist/arkServices.spec.js +0 -138
  29. package/dist/commands/agents/index.spec.d.ts +0 -1
  30. package/dist/commands/agents/index.spec.js +0 -67
  31. package/dist/commands/cluster/get.spec.d.ts +0 -1
  32. package/dist/commands/cluster/get.spec.js +0 -92
  33. package/dist/commands/cluster/index.spec.d.ts +0 -1
  34. package/dist/commands/cluster/index.spec.js +0 -24
  35. package/dist/commands/completion/index.spec.d.ts +0 -1
  36. package/dist/commands/completion/index.spec.js +0 -34
  37. package/dist/commands/config/index.spec.d.ts +0 -1
  38. package/dist/commands/config/index.spec.js +0 -78
  39. package/dist/commands/evaluation/index.spec.d.ts +0 -1
  40. package/dist/commands/evaluation/index.spec.js +0 -161
  41. package/dist/commands/export/index.spec.d.ts +0 -1
  42. package/dist/commands/export/index.spec.js +0 -145
  43. package/dist/commands/import/index.spec.d.ts +0 -1
  44. package/dist/commands/import/index.spec.js +0 -46
  45. package/dist/commands/install/index.spec.d.ts +0 -1
  46. package/dist/commands/install/index.spec.js +0 -286
  47. package/dist/commands/marketplace/index.spec.d.ts +0 -1
  48. package/dist/commands/marketplace/index.spec.js +0 -88
  49. package/dist/commands/memory/index.spec.d.ts +0 -1
  50. package/dist/commands/memory/index.spec.js +0 -124
  51. package/dist/commands/models/create.spec.d.ts +0 -1
  52. package/dist/commands/models/create.spec.js +0 -167
  53. package/dist/commands/models/index.spec.d.ts +0 -1
  54. package/dist/commands/models/index.spec.js +0 -96
  55. package/dist/commands/models/providers/azure.spec.d.ts +0 -1
  56. package/dist/commands/models/providers/azure.spec.js +0 -232
  57. package/dist/commands/models/providers/bedrock.spec.d.ts +0 -1
  58. package/dist/commands/models/providers/bedrock.spec.js +0 -241
  59. package/dist/commands/models/providers/openai.spec.d.ts +0 -1
  60. package/dist/commands/models/providers/openai.spec.js +0 -180
  61. package/dist/commands/queries/delete.spec.d.ts +0 -1
  62. package/dist/commands/queries/delete.spec.js +0 -74
  63. package/dist/commands/queries/index.spec.d.ts +0 -1
  64. package/dist/commands/queries/index.spec.js +0 -167
  65. package/dist/commands/queries/list.spec.d.ts +0 -1
  66. package/dist/commands/queries/list.spec.js +0 -170
  67. package/dist/commands/queries/validation.spec.d.ts +0 -1
  68. package/dist/commands/queries/validation.spec.js +0 -27
  69. package/dist/commands/query/index.spec.d.ts +0 -1
  70. package/dist/commands/query/index.spec.js +0 -104
  71. package/dist/commands/targets/index.spec.d.ts +0 -1
  72. package/dist/commands/targets/index.spec.js +0 -154
  73. package/dist/commands/teams/index.spec.d.ts +0 -1
  74. package/dist/commands/teams/index.spec.js +0 -70
  75. package/dist/commands/tools/index.spec.d.ts +0 -1
  76. package/dist/commands/tools/index.spec.js +0 -70
  77. package/dist/commands/uninstall/index.spec.d.ts +0 -1
  78. package/dist/commands/uninstall/index.spec.js +0 -125
  79. package/dist/lib/arkServiceProxy.spec.d.ts +0 -1
  80. package/dist/lib/arkServiceProxy.spec.js +0 -100
  81. package/dist/lib/arkStatus.spec.d.ts +0 -1
  82. package/dist/lib/arkStatus.spec.js +0 -49
  83. package/dist/lib/chatClient.spec.d.ts +0 -1
  84. package/dist/lib/chatClient.spec.js +0 -108
  85. package/dist/lib/cluster.spec.d.ts +0 -1
  86. package/dist/lib/cluster.spec.js +0 -338
  87. package/dist/lib/commands.spec.d.ts +0 -1
  88. package/dist/lib/commands.spec.js +0 -146
  89. package/dist/lib/config.spec.d.ts +0 -1
  90. package/dist/lib/config.spec.js +0 -202
  91. package/dist/lib/duration.spec.d.ts +0 -1
  92. package/dist/lib/duration.spec.js +0 -13
  93. package/dist/lib/errors.spec.d.ts +0 -1
  94. package/dist/lib/errors.spec.js +0 -221
  95. package/dist/lib/executeQuery.spec.d.ts +0 -1
  96. package/dist/lib/executeQuery.spec.js +0 -325
  97. package/dist/lib/kubectl.spec.d.ts +0 -1
  98. package/dist/lib/kubectl.spec.js +0 -192
  99. package/dist/lib/marketplaceFetcher.spec.d.ts +0 -1
  100. package/dist/lib/marketplaceFetcher.spec.js +0 -225
  101. package/dist/lib/nextSteps.spec.d.ts +0 -1
  102. package/dist/lib/nextSteps.spec.js +0 -59
  103. package/dist/lib/output.spec.d.ts +0 -1
  104. package/dist/lib/output.spec.js +0 -123
  105. package/dist/lib/startup.spec.d.ts +0 -1
  106. package/dist/lib/startup.spec.js +0 -152
  107. package/dist/lib/stdin.spec.d.ts +0 -1
  108. package/dist/lib/stdin.spec.js +0 -82
  109. package/dist/lib/timeout.spec.d.ts +0 -1
  110. package/dist/lib/timeout.spec.js +0 -14
  111. package/dist/lib/waitForReady.spec.d.ts +0 -1
  112. package/dist/lib/waitForReady.spec.js +0 -104
  113. package/dist/marketplaceServices.spec.d.ts +0 -1
  114. package/dist/marketplaceServices.spec.js +0 -74
  115. package/dist/ui/statusFormatter.spec.d.ts +0 -1
  116. package/dist/ui/statusFormatter.spec.js +0 -58
@@ -1,202 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import path from 'path';
3
- import os from 'os';
4
- const mockFs = {
5
- existsSync: jest.fn(),
6
- readFileSync: jest.fn(),
7
- };
8
- jest.unstable_mockModule('fs', () => ({
9
- default: mockFs,
10
- ...mockFs,
11
- }));
12
- const mockYaml = {
13
- parse: jest.fn(),
14
- stringify: jest.fn(),
15
- };
16
- jest.unstable_mockModule('yaml', () => ({
17
- default: mockYaml,
18
- ...mockYaml,
19
- }));
20
- const { loadConfig, getConfigPaths, formatConfig } = await import('./config.js');
21
- describe('config', () => {
22
- const originalEnv = process.env;
23
- beforeEach(() => {
24
- jest.clearAllMocks();
25
- process.env = { ...originalEnv };
26
- });
27
- afterEach(() => {
28
- process.env = originalEnv;
29
- });
30
- it('returns default config when no files exist', () => {
31
- mockFs.existsSync.mockReturnValue(false);
32
- const config = loadConfig();
33
- expect(config).toEqual({
34
- chat: {
35
- streaming: true,
36
- outputFormat: 'text',
37
- },
38
- marketplace: {
39
- repoUrl: 'https://github.com/mckinsey/agents-at-scale-marketplace',
40
- registry: 'oci://ghcr.io/mckinsey/agents-at-scale-marketplace/charts',
41
- },
42
- services: {
43
- reusePortForwards: false,
44
- },
45
- });
46
- });
47
- it('loads and merges configs in order: defaults, user, project', () => {
48
- mockFs.existsSync.mockReturnValue(true);
49
- mockFs.readFileSync
50
- .mockReturnValueOnce('user yaml')
51
- .mockReturnValueOnce('project yaml');
52
- mockYaml.parse
53
- .mockReturnValueOnce({
54
- chat: {
55
- streaming: false,
56
- outputFormat: 'markdown',
57
- },
58
- })
59
- .mockReturnValueOnce({
60
- chat: {
61
- streaming: true,
62
- },
63
- });
64
- const config = loadConfig();
65
- expect(config.chat?.streaming).toBe(true);
66
- expect(config.chat?.outputFormat).toBe('markdown');
67
- });
68
- it('environment variables override all configs', () => {
69
- mockFs.existsSync.mockReturnValue(false);
70
- process.env.ARK_CHAT_STREAMING = '1';
71
- process.env.ARK_CHAT_OUTPUT_FORMAT = 'MARKDOWN';
72
- const config = loadConfig();
73
- expect(config.chat?.streaming).toBe(true);
74
- expect(config.chat?.outputFormat).toBe('markdown');
75
- });
76
- it('loads queryTimeout from config file', () => {
77
- mockFs.existsSync.mockReturnValue(true);
78
- mockFs.readFileSync.mockReturnValue('yaml');
79
- mockYaml.parse.mockReturnValue({ queryTimeout: '30m' });
80
- const config = loadConfig();
81
- expect(config.queryTimeout).toBe('30m');
82
- });
83
- it('loads defaultExportTypes from config file', () => {
84
- mockFs.existsSync.mockReturnValue(true);
85
- mockFs.readFileSync.mockReturnValue('yaml');
86
- mockYaml.parse.mockReturnValue({ defaultExportTypes: ['agents', 'teams'] });
87
- const config = loadConfig();
88
- expect(config.defaultExportTypes).toEqual(['agents', 'teams']);
89
- });
90
- it('ARK_QUERY_TIMEOUT environment variable overrides config', () => {
91
- mockFs.existsSync.mockReturnValue(true);
92
- mockFs.readFileSync.mockReturnValue('yaml');
93
- mockYaml.parse.mockReturnValue({ queryTimeout: '5m' });
94
- process.env.ARK_QUERY_TIMEOUT = '1h';
95
- const config = loadConfig();
96
- expect(config.queryTimeout).toBe('1h');
97
- });
98
- it('throws error for invalid YAML', () => {
99
- const userConfigPath = path.join(os.homedir(), '.arkrc.yaml');
100
- mockFs.existsSync.mockImplementation((path) => path === userConfigPath);
101
- mockFs.readFileSync.mockReturnValue('invalid yaml');
102
- mockYaml.parse.mockImplementation(() => {
103
- throw new Error('YAML parse error');
104
- });
105
- expect(() => loadConfig()).toThrow(`Invalid YAML in ${userConfigPath}: YAML parse error`);
106
- });
107
- it('handles non-Error exceptions', () => {
108
- const userConfigPath = path.join(os.homedir(), '.arkrc.yaml');
109
- mockFs.existsSync.mockImplementation((path) => path === userConfigPath);
110
- mockFs.readFileSync.mockReturnValue('invalid yaml');
111
- mockYaml.parse.mockImplementation(() => {
112
- throw 'string error';
113
- });
114
- expect(() => loadConfig()).toThrow(`Invalid YAML in ${userConfigPath}: Unknown error`);
115
- });
116
- it('getConfigPaths returns correct paths', () => {
117
- const paths = getConfigPaths();
118
- expect(paths.user).toBe(path.join(os.homedir(), '.arkrc.yaml'));
119
- expect(paths.project).toBe(path.join(process.cwd(), '.arkrc.yaml'));
120
- });
121
- it('formatConfig uses yaml.stringify', () => {
122
- const config = { chat: { streaming: true, outputFormat: 'text' } };
123
- mockYaml.stringify.mockReturnValue('formatted');
124
- const result = formatConfig(config);
125
- expect(mockYaml.stringify).toHaveBeenCalledWith(config);
126
- expect(result).toBe('formatted');
127
- });
128
- it('loads marketplace config from config file', () => {
129
- mockFs.existsSync.mockReturnValue(true);
130
- mockFs.readFileSync.mockReturnValue('yaml');
131
- mockYaml.parse.mockReturnValue({
132
- marketplace: {
133
- repoUrl: 'https://example.com/my-marketplace',
134
- registry: 'oci://example.com/charts',
135
- },
136
- });
137
- const config = loadConfig();
138
- expect(config.marketplace?.repoUrl).toBe('https://example.com/my-marketplace');
139
- expect(config.marketplace?.registry).toBe('oci://example.com/charts');
140
- });
141
- it('marketplace environment variables override config', () => {
142
- mockFs.existsSync.mockReturnValue(true);
143
- mockFs.readFileSync.mockReturnValue('yaml');
144
- mockYaml.parse.mockReturnValue({
145
- marketplace: {
146
- repoUrl: 'https://example.com/my-marketplace',
147
- registry: 'oci://example.com/charts',
148
- },
149
- });
150
- process.env.ARK_MARKETPLACE_REPO_URL = 'https://custom.com/marketplace';
151
- process.env.ARK_MARKETPLACE_REGISTRY = 'oci://custom.com/charts';
152
- const config = loadConfig();
153
- expect(config.marketplace?.repoUrl).toBe('https://custom.com/marketplace');
154
- expect(config.marketplace?.registry).toBe('oci://custom.com/charts');
155
- });
156
- });
157
- describe('marketplace helpers', () => {
158
- const originalEnv = process.env;
159
- beforeEach(() => {
160
- jest.clearAllMocks();
161
- process.env = { ...originalEnv };
162
- });
163
- afterEach(() => {
164
- process.env = originalEnv;
165
- });
166
- it('getMarketplaceRepoUrl returns custom config value', async () => {
167
- mockFs.existsSync.mockReturnValue(true);
168
- mockFs.readFileSync.mockReturnValue('yaml');
169
- mockYaml.parse.mockReturnValue({
170
- marketplace: {
171
- repoUrl: 'https://custom-repo.com/marketplace',
172
- },
173
- });
174
- jest.resetModules();
175
- const { getMarketplaceRepoUrl } = await import('./config.js');
176
- expect(getMarketplaceRepoUrl()).toBe('https://custom-repo.com/marketplace');
177
- });
178
- it('getMarketplaceRepoUrl returns default value', async () => {
179
- mockFs.existsSync.mockReturnValue(false);
180
- jest.resetModules();
181
- const { getMarketplaceRepoUrl } = await import('./config.js');
182
- expect(getMarketplaceRepoUrl()).toBe('https://github.com/mckinsey/agents-at-scale-marketplace');
183
- });
184
- it('getMarketplaceRegistry returns custom config value', async () => {
185
- mockFs.existsSync.mockReturnValue(true);
186
- mockFs.readFileSync.mockReturnValue('yaml');
187
- mockYaml.parse.mockReturnValue({
188
- marketplace: {
189
- registry: 'oci://custom-registry.com/charts',
190
- },
191
- });
192
- jest.resetModules();
193
- const { getMarketplaceRegistry } = await import('./config.js');
194
- expect(getMarketplaceRegistry()).toBe('oci://custom-registry.com/charts');
195
- });
196
- it('getMarketplaceRegistry returns default value', async () => {
197
- mockFs.existsSync.mockReturnValue(false);
198
- jest.resetModules();
199
- const { getMarketplaceRegistry } = await import('./config.js');
200
- expect(getMarketplaceRegistry()).toBe('oci://ghcr.io/mckinsey/agents-at-scale-marketplace/charts');
201
- });
202
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,13 +0,0 @@
1
- import { parseDuration } from './duration.js';
2
- describe('parseDuration', () => {
3
- it('should parse durations correctly', () => {
4
- expect(parseDuration('100ms')).toBe(100);
5
- expect(parseDuration('30s')).toBe(30000);
6
- expect(parseDuration('5m')).toBe(300000);
7
- expect(parseDuration('1h')).toBe(3600000);
8
- });
9
- it('should throw on invalid format', () => {
10
- expect(() => parseDuration('invalid')).toThrow('Invalid duration format');
11
- expect(() => parseDuration('10')).toThrow('Invalid duration format');
12
- });
13
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,221 +0,0 @@
1
- import { describe, it, expect, jest, beforeEach, afterEach } from '@jest/globals';
2
- import { ErrorCode, ArkError, ValidationError, TemplateError, ProjectStructureError, ErrorHandler, InputValidator, } from './errors.js';
3
- jest.mock('fs');
4
- describe('Error Classes', () => {
5
- describe('ArkError', () => {
6
- it('creates error with default code', () => {
7
- const error = new ArkError('test message');
8
- expect(error.message).toBe('test message');
9
- expect(error.code).toBe(ErrorCode.UNKNOWN_ERROR);
10
- expect(error.name).toBe('ArkError');
11
- expect(error.details).toBeUndefined();
12
- expect(error.suggestions).toBeUndefined();
13
- });
14
- it('creates error with all properties', () => {
15
- const error = new ArkError('test error', ErrorCode.INVALID_INPUT, { field: 'name' }, ['Check the input', 'Try again']);
16
- expect(error.message).toBe('test error');
17
- expect(error.code).toBe(ErrorCode.INVALID_INPUT);
18
- expect(error.details).toEqual({ field: 'name' });
19
- expect(error.suggestions).toEqual(['Check the input', 'Try again']);
20
- });
21
- });
22
- describe('ValidationError', () => {
23
- it('creates validation error without field', () => {
24
- const error = new ValidationError('validation failed');
25
- expect(error.message).toBe('validation failed');
26
- expect(error.code).toBe(ErrorCode.VALIDATION_ERROR);
27
- expect(error.name).toBe('ValidationError');
28
- expect(error.details).toBeUndefined();
29
- });
30
- it('creates validation error with field and suggestions', () => {
31
- const error = new ValidationError('invalid email', 'email', [
32
- 'Use valid format',
33
- ]);
34
- expect(error.message).toBe('invalid email');
35
- expect(error.code).toBe(ErrorCode.VALIDATION_ERROR);
36
- expect(error.details).toEqual({ field: 'email' });
37
- expect(error.suggestions).toEqual(['Use valid format']);
38
- });
39
- });
40
- describe('TemplateError', () => {
41
- it('creates template error', () => {
42
- const error = new TemplateError('template failed', 'template.yaml');
43
- expect(error.message).toBe('template failed');
44
- expect(error.code).toBe(ErrorCode.TEMPLATE_ERROR);
45
- expect(error.name).toBe('TemplateError');
46
- expect(error.details).toEqual({ templatePath: 'template.yaml' });
47
- });
48
- });
49
- describe('ProjectStructureError', () => {
50
- it('creates project structure error with defaults', () => {
51
- const error = new ProjectStructureError('project invalid');
52
- expect(error.message).toBe('project invalid');
53
- expect(error.code).toBe(ErrorCode.PROJECT_STRUCTURE_INVALID);
54
- expect(error.name).toBe('ProjectStructureError');
55
- expect(error.suggestions).toEqual([
56
- 'Ensure you are in a valid ARK project directory',
57
- 'Run "ark generate project" to create a new project',
58
- 'Check that Chart.yaml and agents/ directory exist',
59
- ]);
60
- });
61
- it('creates project structure error with path', () => {
62
- const error = new ProjectStructureError('project invalid', '/path/to/project');
63
- expect(error.message).toBe('project invalid');
64
- expect(error.code).toBe(ErrorCode.PROJECT_STRUCTURE_INVALID);
65
- expect(error.details).toEqual({ projectPath: '/path/to/project' });
66
- });
67
- });
68
- describe('ErrorHandler', () => {
69
- it('formats basic error', () => {
70
- const error = new Error('simple error');
71
- const formatted = ErrorHandler.formatError(error);
72
- expect(formatted).toContain('❌ simple error');
73
- });
74
- it('formats ArkError with details and suggestions', () => {
75
- const error = new ArkError('test error', ErrorCode.INVALID_INPUT, { field: 'name', value: 'test' }, ['Fix the input', 'Try again']);
76
- const formatted = ErrorHandler.formatError(error);
77
- expect(formatted).toContain('❌ test error');
78
- expect(formatted).toContain('Details:');
79
- expect(formatted).toContain('field: name');
80
- expect(formatted).toContain('value: test');
81
- expect(formatted).toContain('💡 Suggestions:');
82
- expect(formatted).toContain('• Fix the input');
83
- expect(formatted).toContain('• Try again');
84
- });
85
- it('includes stack trace in debug mode', () => {
86
- process.env.DEBUG = 'true';
87
- const error = new Error('debug error');
88
- const formatted = ErrorHandler.formatError(error);
89
- expect(formatted).toContain('Stack trace:');
90
- delete process.env.DEBUG;
91
- });
92
- it('handles missing stack trace', () => {
93
- process.env.NODE_ENV = 'development';
94
- const error = new Error('no stack');
95
- error.stack = undefined;
96
- const formatted = ErrorHandler.formatError(error);
97
- expect(formatted).toContain('No stack trace available');
98
- delete process.env.NODE_ENV;
99
- });
100
- describe('handleAndExit', () => {
101
- let mockExit;
102
- let mockConsoleError;
103
- beforeEach(() => {
104
- mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {
105
- throw new Error('process.exit');
106
- });
107
- mockConsoleError = jest
108
- .spyOn(console, 'error')
109
- .mockImplementation(() => { });
110
- });
111
- afterEach(() => {
112
- mockExit.mockRestore();
113
- mockConsoleError.mockRestore();
114
- });
115
- it('exits with code 22 for validation errors', () => {
116
- const error = new ValidationError('invalid');
117
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
118
- expect(mockExit).toHaveBeenCalledWith(22);
119
- });
120
- it('exits with code 2 for file not found', () => {
121
- const error = new ArkError('not found', ErrorCode.FILE_NOT_FOUND);
122
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
123
- expect(mockExit).toHaveBeenCalledWith(2);
124
- });
125
- it('exits with code 13 for permission denied', () => {
126
- const error = new ArkError('denied', ErrorCode.PERMISSION_DENIED);
127
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
128
- expect(mockExit).toHaveBeenCalledWith(13);
129
- });
130
- it('exits with code 127 for missing dependency', () => {
131
- const error = new ArkError('missing', ErrorCode.DEPENDENCY_MISSING);
132
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
133
- expect(mockExit).toHaveBeenCalledWith(127);
134
- });
135
- it('exits with code 1 for unknown errors', () => {
136
- const error = new Error('unknown');
137
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
138
- expect(mockExit).toHaveBeenCalledWith(1);
139
- });
140
- it('exits with code 1 for default ArkError', () => {
141
- const error = new ArkError('general', ErrorCode.UNKNOWN_ERROR);
142
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
143
- expect(mockExit).toHaveBeenCalledWith(1);
144
- });
145
- });
146
- describe('catchAndHandle', () => {
147
- it('returns successful promise result', async () => {
148
- const result = await ErrorHandler.catchAndHandle(async () => 'success');
149
- expect(result).toBe('success');
150
- });
151
- it('rethrows ArkError unchanged', async () => {
152
- const arkError = new ValidationError('test');
153
- await expect(ErrorHandler.catchAndHandle(async () => {
154
- throw arkError;
155
- })).rejects.toThrow(arkError);
156
- });
157
- it('wraps generic errors with context', async () => {
158
- const error = new Error('generic');
159
- await expect(ErrorHandler.catchAndHandle(async () => {
160
- throw error;
161
- }, 'context')).rejects.toThrow('context: generic');
162
- });
163
- it('wraps non-Error objects', async () => {
164
- await expect(ErrorHandler.catchAndHandle(async () => {
165
- throw 'string error';
166
- })).rejects.toThrow('string error');
167
- });
168
- });
169
- });
170
- describe('InputValidator', () => {
171
- describe('validateName', () => {
172
- it('accepts valid names', () => {
173
- expect(() => InputValidator.validateName('valid-name')).not.toThrow();
174
- expect(() => InputValidator.validateName('test123')).not.toThrow();
175
- expect(() => InputValidator.validateName('a-b-c-123')).not.toThrow();
176
- });
177
- it('rejects empty names', () => {
178
- expect(() => InputValidator.validateName('')).toThrow('name cannot be empty');
179
- expect(() => InputValidator.validateName(' ')).toThrow('name cannot be empty');
180
- });
181
- it('rejects names over 63 characters', () => {
182
- const longName = 'a'.repeat(64);
183
- expect(() => InputValidator.validateName(longName)).toThrow('must be 63 characters or less');
184
- });
185
- it('rejects invalid characters', () => {
186
- expect(() => InputValidator.validateName('Invalid Name')).toThrow('Invalid name');
187
- expect(() => InputValidator.validateName('test_name')).toThrow('Invalid name');
188
- expect(() => InputValidator.validateName('-start')).toThrow('Invalid name');
189
- expect(() => InputValidator.validateName('end-')).toThrow('Invalid name');
190
- });
191
- it('suggests normalized names', () => {
192
- try {
193
- InputValidator.validateName('TestName');
194
- }
195
- catch (e) {
196
- expect(e.suggestions).toContain('Try: "test-name"');
197
- }
198
- });
199
- });
200
- describe('validatePath', () => {
201
- it('accepts valid paths', () => {
202
- expect(() => InputValidator.validatePath('/valid/path')).not.toThrow();
203
- expect(() => InputValidator.validatePath('./relative')).not.toThrow();
204
- expect(() => InputValidator.validatePath('simple')).not.toThrow();
205
- });
206
- it('rejects empty paths', () => {
207
- expect(() => InputValidator.validatePath('')).toThrow('path cannot be empty');
208
- });
209
- it('rejects dangerous paths', () => {
210
- expect(() => InputValidator.validatePath('../parent')).toThrow('unsafe characters');
211
- expect(() => InputValidator.validatePath('~/home')).toThrow('unsafe characters');
212
- expect(() => InputValidator.validatePath('$HOME/test')).toThrow('unsafe characters');
213
- });
214
- });
215
- describe('validateDirectory', () => {
216
- it('validates path first', () => {
217
- expect(() => InputValidator.validateDirectory('')).toThrow('directory cannot be empty');
218
- });
219
- });
220
- });
221
- });
@@ -1 +0,0 @@
1
- export {};