@vfarcic/dot-ai 0.5.1 → 0.7.0

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 (146) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/{src/cli.ts → dist/cli.js} +19 -26
  4. package/dist/core/claude.d.ts +42 -0
  5. package/dist/core/claude.d.ts.map +1 -0
  6. package/dist/core/claude.js +229 -0
  7. package/dist/core/deploy-operation.d.ts +38 -0
  8. package/dist/core/deploy-operation.d.ts.map +1 -0
  9. package/dist/core/deploy-operation.js +101 -0
  10. package/dist/core/discovery.d.ts +162 -0
  11. package/dist/core/discovery.d.ts.map +1 -0
  12. package/dist/core/discovery.js +758 -0
  13. package/dist/core/error-handling.d.ts +167 -0
  14. package/dist/core/error-handling.d.ts.map +1 -0
  15. package/dist/core/error-handling.js +399 -0
  16. package/dist/core/index.d.ts +42 -0
  17. package/dist/core/index.d.ts.map +1 -0
  18. package/dist/core/index.js +123 -0
  19. package/dist/core/kubernetes-utils.d.ts +38 -0
  20. package/dist/core/kubernetes-utils.d.ts.map +1 -0
  21. package/dist/core/kubernetes-utils.js +177 -0
  22. package/dist/core/memory.d.ts +45 -0
  23. package/dist/core/memory.d.ts.map +1 -0
  24. package/dist/core/memory.js +113 -0
  25. package/dist/core/schema.d.ts +187 -0
  26. package/dist/core/schema.d.ts.map +1 -0
  27. package/dist/core/schema.js +655 -0
  28. package/dist/core/session-utils.d.ts +29 -0
  29. package/dist/core/session-utils.d.ts.map +1 -0
  30. package/dist/core/session-utils.js +121 -0
  31. package/dist/core/workflow.d.ts +70 -0
  32. package/dist/core/workflow.d.ts.map +1 -0
  33. package/dist/core/workflow.js +161 -0
  34. package/dist/index.d.ts +15 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +32 -0
  37. package/dist/interfaces/cli.d.ts +74 -0
  38. package/dist/interfaces/cli.d.ts.map +1 -0
  39. package/dist/interfaces/cli.js +769 -0
  40. package/dist/interfaces/mcp.d.ts +30 -0
  41. package/dist/interfaces/mcp.d.ts.map +1 -0
  42. package/dist/interfaces/mcp.js +105 -0
  43. package/dist/mcp/server.d.ts +9 -0
  44. package/dist/mcp/server.d.ts.map +1 -0
  45. package/dist/mcp/server.js +151 -0
  46. package/dist/tools/answer-question.d.ts +27 -0
  47. package/dist/tools/answer-question.d.ts.map +1 -0
  48. package/dist/tools/answer-question.js +696 -0
  49. package/dist/tools/choose-solution.d.ts +23 -0
  50. package/dist/tools/choose-solution.d.ts.map +1 -0
  51. package/dist/tools/choose-solution.js +171 -0
  52. package/dist/tools/deploy-manifests.d.ts +25 -0
  53. package/dist/tools/deploy-manifests.d.ts.map +1 -0
  54. package/dist/tools/deploy-manifests.js +74 -0
  55. package/dist/tools/generate-manifests.d.ts +23 -0
  56. package/dist/tools/generate-manifests.d.ts.map +1 -0
  57. package/dist/tools/generate-manifests.js +424 -0
  58. package/dist/tools/index.d.ts +11 -0
  59. package/dist/tools/index.d.ts.map +1 -0
  60. package/dist/tools/index.js +34 -0
  61. package/dist/tools/recommend.d.ts +23 -0
  62. package/dist/tools/recommend.d.ts.map +1 -0
  63. package/dist/tools/recommend.js +332 -0
  64. package/package.json +124 -2
  65. package/.claude/commands/context-load.md +0 -11
  66. package/.claude/commands/context-save.md +0 -16
  67. package/.claude/commands/prd-done.md +0 -115
  68. package/.claude/commands/prd-get.md +0 -25
  69. package/.claude/commands/prd-start.md +0 -87
  70. package/.claude/commands/task-done.md +0 -77
  71. package/.claude/commands/tests-reminder.md +0 -32
  72. package/.claude/settings.local.json +0 -20
  73. package/.eslintrc.json +0 -25
  74. package/.github/workflows/ci.yml +0 -170
  75. package/.prettierrc.json +0 -10
  76. package/.teller.yml +0 -8
  77. package/CLAUDE.md +0 -162
  78. package/assets/images/logo.png +0 -0
  79. package/bin/dot-ai.ts +0 -47
  80. package/bin.js +0 -19
  81. package/destroy.sh +0 -45
  82. package/devbox.json +0 -13
  83. package/devbox.lock +0 -225
  84. package/docs/API.md +0 -449
  85. package/docs/CONTEXT.md +0 -49
  86. package/docs/DEVELOPMENT.md +0 -203
  87. package/docs/NEXT_STEPS.md +0 -97
  88. package/docs/STAGE_BASED_API.md +0 -97
  89. package/docs/cli-guide.md +0 -798
  90. package/docs/design.md +0 -750
  91. package/docs/discovery-engine.md +0 -515
  92. package/docs/error-handling.md +0 -429
  93. package/docs/function-registration.md +0 -157
  94. package/docs/mcp-guide.md +0 -416
  95. package/renovate.json +0 -51
  96. package/setup.sh +0 -111
  97. package/src/core/claude.ts +0 -280
  98. package/src/core/deploy-operation.ts +0 -127
  99. package/src/core/discovery.ts +0 -900
  100. package/src/core/error-handling.ts +0 -562
  101. package/src/core/index.ts +0 -143
  102. package/src/core/kubernetes-utils.ts +0 -218
  103. package/src/core/memory.ts +0 -148
  104. package/src/core/schema.ts +0 -830
  105. package/src/core/session-utils.ts +0 -97
  106. package/src/core/workflow.ts +0 -234
  107. package/src/index.ts +0 -18
  108. package/src/interfaces/cli.ts +0 -872
  109. package/src/interfaces/mcp.ts +0 -183
  110. package/src/mcp/server.ts +0 -131
  111. package/src/tools/answer-question.ts +0 -807
  112. package/src/tools/choose-solution.ts +0 -169
  113. package/src/tools/deploy-manifests.ts +0 -94
  114. package/src/tools/generate-manifests.ts +0 -502
  115. package/src/tools/index.ts +0 -41
  116. package/src/tools/recommend.ts +0 -370
  117. package/tests/__mocks__/@kubernetes/client-node.ts +0 -106
  118. package/tests/build-system.test.ts +0 -345
  119. package/tests/configuration.test.ts +0 -226
  120. package/tests/core/deploy-operation.test.ts +0 -38
  121. package/tests/core/discovery.test.ts +0 -1648
  122. package/tests/core/error-handling.test.ts +0 -632
  123. package/tests/core/schema.test.ts +0 -1658
  124. package/tests/core/session-utils.test.ts +0 -245
  125. package/tests/core.test.ts +0 -439
  126. package/tests/fixtures/configmap-no-labels.yaml +0 -8
  127. package/tests/fixtures/crossplane-app-configuration.yaml +0 -6
  128. package/tests/fixtures/crossplane-providers.yaml +0 -45
  129. package/tests/fixtures/crossplane-rbac.yaml +0 -48
  130. package/tests/fixtures/invalid-configmap.yaml +0 -8
  131. package/tests/fixtures/invalid-deployment.yaml +0 -17
  132. package/tests/fixtures/test-deployment.yaml +0 -28
  133. package/tests/fixtures/valid-configmap.yaml +0 -15
  134. package/tests/infrastructure.test.ts +0 -426
  135. package/tests/interfaces/cli.test.ts +0 -1036
  136. package/tests/interfaces/mcp.test.ts +0 -139
  137. package/tests/kubernetes-utils.test.ts +0 -200
  138. package/tests/mcp/server.test.ts +0 -126
  139. package/tests/setup.ts +0 -31
  140. package/tests/tools/answer-question.test.ts +0 -367
  141. package/tests/tools/choose-solution.test.ts +0 -481
  142. package/tests/tools/deploy-manifests.test.ts +0 -185
  143. package/tests/tools/generate-manifests.test.ts +0 -441
  144. package/tests/tools/index.test.ts +0 -111
  145. package/tests/tools/recommend.test.ts +0 -180
  146. package/tsconfig.json +0 -34
@@ -1,245 +0,0 @@
1
- /**
2
- * Session Utils Tests
3
- * Tests for shared session directory utilities including relative path support
4
- */
5
-
6
- import { getSessionDirectory, validateSessionDirectory, getAndValidateSessionDirectory } from '../../src/core/session-utils';
7
- import * as fs from 'fs';
8
- import * as path from 'path';
9
-
10
- describe('Session Utils', () => {
11
- let testDir: string;
12
- let originalCwd: string;
13
- let originalEnv: string | undefined;
14
-
15
- beforeEach(() => {
16
- // Create test directory
17
- testDir = path.join(process.cwd(), 'tmp', 'session-utils-test', Date.now().toString());
18
- fs.mkdirSync(testDir, { recursive: true });
19
-
20
- // Save original state
21
- originalCwd = process.cwd();
22
- originalEnv = process.env.DOT_AI_SESSION_DIR;
23
- });
24
-
25
- afterEach(() => {
26
- // Restore original state
27
- process.chdir(originalCwd);
28
- if (originalEnv) {
29
- process.env.DOT_AI_SESSION_DIR = originalEnv;
30
- } else {
31
- delete process.env.DOT_AI_SESSION_DIR;
32
- }
33
-
34
- // Clean up test directory
35
- try {
36
- fs.rmSync(testDir, { recursive: true, force: true });
37
- } catch (error) {
38
- console.warn('Could not clean up test directory:', error);
39
- }
40
- });
41
-
42
- describe('getSessionDirectory', () => {
43
- it('should prefer CLI args over environment variable', () => {
44
- process.env.DOT_AI_SESSION_DIR = '/env/path';
45
- const args = { sessionDir: '/cli/path' };
46
-
47
- const result = getSessionDirectory(args);
48
-
49
- expect(result).toBe('/cli/path');
50
- });
51
-
52
- it('should use environment variable when CLI args not provided', () => {
53
- process.env.DOT_AI_SESSION_DIR = '/env/path';
54
- const args = {};
55
-
56
- const result = getSessionDirectory(args);
57
-
58
- expect(result).toBe('/env/path');
59
- });
60
-
61
- it('should throw error when neither CLI args nor environment variable provided', () => {
62
- delete process.env.DOT_AI_SESSION_DIR;
63
- const args = {};
64
-
65
- expect(() => getSessionDirectory(args)).toThrow(
66
- 'Session directory must be specified via --session-dir parameter or DOT_AI_SESSION_DIR environment variable'
67
- );
68
- });
69
-
70
- it('should work with relative paths from environment variable', () => {
71
- process.env.DOT_AI_SESSION_DIR = './relative/path';
72
- const args = {};
73
-
74
- const result = getSessionDirectory(args);
75
-
76
- expect(result).toBe('./relative/path');
77
- });
78
- });
79
-
80
- describe('validateSessionDirectory', () => {
81
- it('should validate existing absolute directory', () => {
82
- expect(() => validateSessionDirectory(testDir)).not.toThrow();
83
- });
84
-
85
- it('should validate existing relative directory', () => {
86
- // Create relative directory structure
87
- const relativeTestDir = path.join(testDir, 'relative-test');
88
- const relativeSessionDir = path.join(relativeTestDir, 'sessions');
89
- fs.mkdirSync(relativeSessionDir, { recursive: true });
90
-
91
- // Change to test directory and validate relative path
92
- process.chdir(relativeTestDir);
93
-
94
- expect(() => validateSessionDirectory('./sessions')).not.toThrow();
95
- });
96
-
97
- it('should throw error for non-existent directory', () => {
98
- const nonExistentDir = path.join(testDir, 'does-not-exist');
99
-
100
- expect(() => validateSessionDirectory(nonExistentDir)).toThrow(
101
- `Session directory does not exist: ${nonExistentDir}`
102
- );
103
- });
104
-
105
- it('should throw error when path is not a directory', () => {
106
- const filePath = path.join(testDir, 'test-file.txt');
107
- fs.writeFileSync(filePath, 'test');
108
-
109
- expect(() => validateSessionDirectory(filePath)).toThrow(
110
- `Session directory path is not a directory: ${filePath}`
111
- );
112
- });
113
-
114
- it('should test write permissions when required', () => {
115
- expect(() => validateSessionDirectory(testDir, true)).not.toThrow();
116
- });
117
-
118
- it('should throw error when directory is not writable', () => {
119
- // Create read-only directory (only works on Unix systems)
120
- const readOnlyDir = path.join(testDir, 'readonly');
121
- fs.mkdirSync(readOnlyDir);
122
-
123
- // Make directory read-only
124
- try {
125
- fs.chmodSync(readOnlyDir, 0o444);
126
-
127
- expect(() => validateSessionDirectory(readOnlyDir, true)).toThrow();
128
-
129
- // Restore permissions for cleanup
130
- fs.chmodSync(readOnlyDir, 0o755);
131
- } catch (error) {
132
- // Skip this test on systems where chmod doesn't work (e.g., Windows)
133
- console.warn('Skipping write permission test - chmod not supported');
134
- }
135
- });
136
- });
137
-
138
- describe('getAndValidateSessionDirectory', () => {
139
- it('should get and validate session directory from environment variable', () => {
140
- process.env.DOT_AI_SESSION_DIR = testDir;
141
- const args = {};
142
-
143
- const result = getAndValidateSessionDirectory(args, false);
144
-
145
- expect(result).toBe(testDir);
146
- });
147
-
148
- it('should get and validate session directory with write permissions', () => {
149
- process.env.DOT_AI_SESSION_DIR = testDir;
150
- const args = {};
151
-
152
- const result = getAndValidateSessionDirectory(args, true);
153
-
154
- expect(result).toBe(testDir);
155
- });
156
-
157
- it('should work with relative paths when cwd is set', () => {
158
- // Create test directory structure
159
- const testCwd = path.join(testDir, 'cwd-test');
160
- const relativeSessionDir = './sessions';
161
- const absoluteSessionDir = path.join(testCwd, 'sessions');
162
-
163
- fs.mkdirSync(testCwd, { recursive: true });
164
- fs.mkdirSync(absoluteSessionDir, { recursive: true });
165
-
166
- // Change to test directory and set relative path
167
- process.chdir(testCwd);
168
- process.env.DOT_AI_SESSION_DIR = relativeSessionDir;
169
-
170
- const args = {};
171
- const result = getAndValidateSessionDirectory(args, true);
172
-
173
- expect(result).toBe('./sessions');
174
-
175
- // Verify the relative path actually resolves to the correct absolute path
176
- expect(path.resolve(result)).toBe(absoluteSessionDir);
177
- });
178
-
179
- it('should throw error when session directory not configured', () => {
180
- delete process.env.DOT_AI_SESSION_DIR;
181
- const args = {};
182
-
183
- expect(() => getAndValidateSessionDirectory(args)).toThrow(
184
- 'Session directory must be specified via --session-dir parameter or DOT_AI_SESSION_DIR environment variable'
185
- );
186
- });
187
-
188
- it('should throw error when session directory does not exist', () => {
189
- const nonExistentDir = path.join(testDir, 'does-not-exist');
190
- process.env.DOT_AI_SESSION_DIR = nonExistentDir;
191
- const args = {};
192
-
193
- expect(() => getAndValidateSessionDirectory(args)).toThrow(
194
- `Session directory does not exist: ${nonExistentDir}`
195
- );
196
- });
197
- });
198
-
199
- describe('MCP cwd simulation', () => {
200
- it('should work exactly like MCP server with cwd and relative DOT_AI_SESSION_DIR', () => {
201
- // Simulate MCP configuration:
202
- // {
203
- // "mcpServers": {
204
- // "dot-ai": {
205
- // "command": "npm",
206
- // "args": ["run", "mcp:start"],
207
- // "cwd": "/path/to/dot-ai",
208
- // "env": {
209
- // "DOT_AI_SESSION_DIR": "./tmp/sessions"
210
- // }
211
- // }
212
- // }
213
- // }
214
-
215
- const mcpCwd = path.join(testDir, 'mcp-simulation');
216
- const relativeSessionDir = './tmp/sessions';
217
- const absoluteSessionDir = path.join(mcpCwd, 'tmp', 'sessions');
218
-
219
- // Create directories
220
- fs.mkdirSync(mcpCwd, { recursive: true });
221
- fs.mkdirSync(absoluteSessionDir, { recursive: true });
222
-
223
- // Change to MCP cwd and set relative environment variable
224
- process.chdir(mcpCwd);
225
- process.env.DOT_AI_SESSION_DIR = relativeSessionDir;
226
-
227
- // This simulates what happens when MCP tools are called
228
- const args = {}; // MCP tools don't get sessionDir as args
229
- const context = { requestId: 'mcp-test-123' };
230
-
231
- const result = getAndValidateSessionDirectory(args, true);
232
-
233
- // Should return the relative path as configured
234
- expect(result).toBe('./tmp/sessions');
235
-
236
- // But should resolve to the correct absolute path
237
- expect(path.resolve(result)).toBe(absoluteSessionDir);
238
-
239
- // Should be able to write to it
240
- const testFile = path.join(result, 'test-solution.json');
241
- fs.writeFileSync(testFile, JSON.stringify({ test: 'data' }));
242
- expect(fs.existsSync(testFile)).toBe(true);
243
- });
244
- });
245
- });
@@ -1,439 +0,0 @@
1
- /**
2
- * Core Module Tests
3
- *
4
- * These tests define the API contracts and behavior for our core intelligence modules
5
- * Following TDD approach - these tests define what we SHOULD implement
6
- */
7
-
8
- import {
9
- DotAI,
10
- KubernetesDiscovery,
11
- MemorySystem,
12
- WorkflowEngine,
13
- ClaudeIntegration
14
- } from '../src/core';
15
- import { ErrorClassifier, buildKubectlCommand, executeKubectl } from '../src/core/kubernetes-utils';
16
- import * as path from 'path';
17
-
18
- describe('Core Module Structure', () => {
19
- describe('DotAI Class', () => {
20
- test('should be constructible with configuration options', () => {
21
- const agent = new DotAI({
22
- kubernetesConfig: '/path/to/kubeconfig',
23
- anthropicApiKey: 'test-key'
24
- });
25
-
26
- expect(agent).toBeInstanceOf(DotAI);
27
- expect(agent.getVersion()).toBe('0.1.0');
28
- });
29
-
30
- test('should provide access to all core modules', async () => {
31
- // Use project's working kubeconfig.yaml for integration tests
32
- const projectKubeconfig = path.join(process.cwd(), 'kubeconfig.yaml');
33
- const agent = new DotAI({ kubernetesConfig: projectKubeconfig });
34
- await agent.initialize();
35
-
36
- expect(agent.discovery).toBeInstanceOf(KubernetesDiscovery);
37
- expect(agent.memory).toBeInstanceOf(MemorySystem);
38
- expect(agent.workflow).toBeInstanceOf(WorkflowEngine);
39
- expect(agent.claude).toBeInstanceOf(ClaudeIntegration);
40
- });
41
-
42
- test('should handle initialization errors gracefully', async () => {
43
- // Use project's working kubeconfig.yaml for integration tests
44
- const projectKubeconfig = path.join(process.cwd(), 'kubeconfig.yaml');
45
- const agent = new DotAI({ kubernetesConfig: projectKubeconfig });
46
-
47
- // Mock the discovery connect method to fail
48
- jest.spyOn(agent.discovery, 'connect').mockRejectedValue(new Error('Connection failed'));
49
-
50
- await expect(agent.initialize()).rejects.toThrow();
51
- expect(agent.isInitialized()).toBe(false);
52
- });
53
-
54
- test('should provide configuration validation', () => {
55
- expect(() => {
56
- new DotAI({ anthropicApiKey: '' });
57
- }).toThrow('Invalid configuration');
58
- });
59
- });
60
-
61
- describe('Module Integration', () => {
62
- test('should allow modules to communicate with each other', async () => {
63
- // Use project's working kubeconfig.yaml for integration tests
64
- const projectKubeconfig = path.join(process.cwd(), 'kubeconfig.yaml');
65
- const agent = new DotAI({ kubernetesConfig: projectKubeconfig });
66
- await agent.initialize();
67
-
68
- // Memory should be able to store discovery results
69
- const discoveryData = { resources: ['deployment', 'service'] };
70
- await agent.memory.store('cluster-capabilities', discoveryData);
71
-
72
- // Workflow should be able to access memory
73
- const stored = await agent.memory.retrieve('cluster-capabilities');
74
- expect(stored).toEqual(discoveryData);
75
- });
76
-
77
- test('should handle module dependency failures', async () => {
78
- // Use project's working kubeconfig.yaml for integration tests
79
- const projectKubeconfig = path.join(process.cwd(), 'kubeconfig.yaml');
80
- const agent = new DotAI({ kubernetesConfig: projectKubeconfig });
81
-
82
- // Mock discovery connect to fail, but other modules should still initialize
83
- jest.spyOn(agent.discovery, 'connect').mockRejectedValue(new Error('Discovery failed'));
84
-
85
- try {
86
- await agent.initialize();
87
- } catch (error) {
88
- // Initialization should fail, but modules should still be accessible
89
- expect(agent.memory).toBeDefined();
90
- expect(agent.workflow).toBeDefined();
91
- expect(agent.discovery).toBeDefined();
92
- }
93
- });
94
- });
95
- });
96
-
97
- describe('Memory System Module', () => {
98
- let memory: MemorySystem;
99
-
100
- beforeEach(() => {
101
- memory = new MemorySystem();
102
- });
103
-
104
- describe('Basic Storage Operations', () => {
105
- test('should store and retrieve data', async () => {
106
- const testData = { key: 'value', number: 42 };
107
-
108
- await memory.store('test-key', testData);
109
- const retrieved = await memory.retrieve('test-key');
110
-
111
- expect(retrieved).toEqual(testData);
112
- });
113
-
114
- test('should handle non-existent keys gracefully', async () => {
115
- const result = await memory.retrieve('non-existent');
116
- expect(result).toBeNull();
117
- });
118
-
119
- test('should support different data types', async () => {
120
- await memory.store('string', 'hello');
121
- await memory.store('number', 123);
122
- await memory.store('boolean', true);
123
- await memory.store('array', [1, 2, 3]);
124
- await memory.store('object', { nested: { value: 'test' } });
125
-
126
- expect(await memory.retrieve('string')).toBe('hello');
127
- expect(await memory.retrieve('number')).toBe(123);
128
- expect(await memory.retrieve('boolean')).toBe(true);
129
- expect(await memory.retrieve('array')).toEqual([1, 2, 3]);
130
- expect(await memory.retrieve('object')).toEqual({ nested: { value: 'test' } });
131
- });
132
- });
133
-
134
- describe('Learning and Context', () => {
135
- test('should learn from successful deployments', async () => {
136
- const deployment = {
137
- app: 'nginx',
138
- namespace: 'production',
139
- replicas: 3,
140
- resources: { cpu: '500m', memory: '1Gi' }
141
- };
142
-
143
- await memory.learnSuccess('deployment', deployment);
144
-
145
- const patterns = await memory.getSuccessPatterns('deployment');
146
- expect(patterns).toContainEqual(expect.objectContaining({
147
- config: deployment,
148
- type: 'deployment'
149
- }));
150
- });
151
-
152
- test('should track failure patterns to avoid repetition', async () => {
153
- const failedConfig = {
154
- app: 'broken-app',
155
- issue: 'insufficient resources'
156
- };
157
-
158
- await memory.learnFailure('deployment', failedConfig, 'Pod failed to schedule');
159
-
160
- const failures = await memory.getFailurePatterns('deployment');
161
- expect(failures).toContainEqual(expect.objectContaining({
162
- config: failedConfig,
163
- error: 'Pod failed to schedule'
164
- }));
165
- });
166
-
167
- test('should provide recommendations based on learning', async () => {
168
- // Store successful pattern
169
- await memory.learnSuccess('service', {
170
- type: 'LoadBalancer',
171
- ports: [{ port: 80, targetPort: 8080 }]
172
- });
173
-
174
- const recommendations = await memory.getRecommendations('service', {
175
- type: 'LoadBalancer'
176
- });
177
-
178
- expect(recommendations).toContainEqual(expect.objectContaining({
179
- suggestion: expect.any(String),
180
- confidence: expect.any(Number)
181
- }));
182
- });
183
- });
184
-
185
- describe('Context Management', () => {
186
- test('should maintain session context', async () => {
187
- await memory.setContext('current-namespace', 'my-app');
188
- await memory.setContext('deployment-strategy', 'rolling');
189
-
190
- const context = await memory.getContext();
191
- expect(context).toHaveProperty('current-namespace', 'my-app');
192
- expect(context).toHaveProperty('deployment-strategy', 'rolling');
193
- });
194
-
195
- test('should clear context when needed', async () => {
196
- await memory.setContext('temp-setting', 'value');
197
- await memory.clearContext('temp-setting');
198
-
199
- const context = await memory.getContext();
200
- expect(context).not.toHaveProperty('temp-setting');
201
- });
202
- });
203
- });
204
-
205
- describe('Workflow Engine Module', () => {
206
- let workflow: WorkflowEngine;
207
-
208
- beforeEach(() => {
209
- workflow = new WorkflowEngine();
210
- });
211
-
212
- describe('Workflow Creation', () => {
213
- test('should create deployment workflows', async () => {
214
- const spec = {
215
- app: 'my-app',
216
- image: 'nginx:latest',
217
- replicas: 2
218
- };
219
-
220
- const workflowId = await workflow.createDeploymentWorkflow(spec);
221
- expect(typeof workflowId).toBe('string');
222
- expect(workflowId.length).toBeGreaterThan(0);
223
- });
224
-
225
- test('should validate workflow specifications', async () => {
226
- const invalidSpec = {
227
- // Missing required fields
228
- replicas: 'invalid'
229
- };
230
-
231
- await expect(workflow.createDeploymentWorkflow(invalidSpec)).rejects.toThrow('Invalid workflow specification');
232
- });
233
- });
234
-
235
- describe('Workflow Execution', () => {
236
- test('should execute workflows step by step', async () => {
237
- const spec = { app: 'test-app', image: 'nginx:latest' };
238
- const workflowId = await workflow.createDeploymentWorkflow(spec);
239
-
240
- const execution = await workflow.execute(workflowId);
241
- expect(execution).toHaveProperty('id');
242
- expect(execution).toHaveProperty('status');
243
- expect(execution).toHaveProperty('steps');
244
- });
245
-
246
- test('should handle step failures gracefully', async () => {
247
- const spec = { app: 'failing-app', image: 'invalid:image' };
248
- const workflowId = await workflow.createDeploymentWorkflow(spec);
249
-
250
- const execution = await workflow.execute(workflowId);
251
- expect(execution.status).toBe('failed');
252
- expect(execution).toHaveProperty('error');
253
- });
254
-
255
- test('should support workflow rollback', async () => {
256
- const spec = { app: 'rollback-test', image: 'nginx:latest' };
257
- const workflowId = await workflow.createDeploymentWorkflow(spec);
258
-
259
- const execution = await workflow.execute(workflowId);
260
- const rollbackResult = await workflow.rollback(execution.id);
261
-
262
- expect(rollbackResult).toHaveProperty('success', true);
263
- });
264
- });
265
-
266
- describe('Workflow Templates', () => {
267
- test('should support predefined workflow templates', async () => {
268
- const templates = await workflow.getAvailableTemplates();
269
-
270
- expect(Array.isArray(templates)).toBe(true);
271
- expect(templates).toContainEqual(expect.objectContaining({
272
- name: expect.any(String),
273
- description: expect.any(String),
274
- parameters: expect.any(Array)
275
- }));
276
- });
277
-
278
- test('should create workflows from templates', async () => {
279
- const templateParams = {
280
- template: 'web-app',
281
- parameters: {
282
- appName: 'my-web-app',
283
- image: 'nginx:latest',
284
- domain: 'my-app.example.com'
285
- }
286
- };
287
-
288
- const workflowId = await workflow.createFromTemplate(templateParams);
289
- expect(typeof workflowId).toBe('string');
290
- });
291
- });
292
- });
293
-
294
- describe('Claude Integration Module', () => {
295
- let claude: ClaudeIntegration;
296
-
297
- beforeEach(() => {
298
- claude = new ClaudeIntegration('test-api-key');
299
- });
300
-
301
- describe('AI Communication', () => {
302
- test('should send messages to Claude and receive responses', async () => {
303
- const response = await claude.sendMessage('Hello, can you help me deploy a web application?');
304
-
305
- expect(response).toHaveProperty('content');
306
- expect(response).toHaveProperty('usage');
307
- expect(typeof response.content).toBe('string');
308
- });
309
-
310
- test('should handle conversation context', async () => {
311
- await claude.sendMessage('I want to deploy nginx');
312
- const response = await claude.sendMessage('What are the recommended resources?');
313
-
314
- // Response should reference the previous nginx context
315
- expect(response.content.toLowerCase()).toContain('nginx');
316
- });
317
-
318
- test('should format Kubernetes YAML properly', async () => {
319
- const response = await claude.generateYAML('deployment', {
320
- app: 'nginx',
321
- replicas: 2,
322
- image: 'nginx:latest'
323
- });
324
-
325
- expect(response).toHaveProperty('yaml');
326
- expect(response).toHaveProperty('explanation');
327
- expect(response.yaml).toContain('apiVersion');
328
- expect(response.yaml).toContain('kind: Deployment');
329
- });
330
- });
331
-
332
- describe('Error Handling', () => {
333
- test('should handle API errors gracefully', async () => {
334
- const invalidClaude = new ClaudeIntegration('invalid-key');
335
-
336
- await expect(invalidClaude.sendMessage('test')).rejects.toThrow();
337
- });
338
-
339
- test('should provide meaningful error messages', async () => {
340
- try {
341
- const invalidClaude = new ClaudeIntegration('');
342
- await invalidClaude.sendMessage('test');
343
- } catch (error) {
344
- expect((error as Error).message).toContain('API key');
345
- }
346
- });
347
- });
348
-
349
- describe('Learning Integration', () => {
350
- test('should learn from successful interactions', async () => {
351
- const interaction = {
352
- input: 'Deploy nginx with 2 replicas',
353
- output: 'Successfully created deployment',
354
- success: true
355
- };
356
-
357
- await claude.recordInteraction(interaction);
358
-
359
- const patterns = await claude.getSuccessfulPatterns();
360
- expect(patterns).toContainEqual(expect.objectContaining(interaction));
361
- });
362
- });
363
-
364
- describe('Centralized Configuration Management', () => {
365
- const originalKubeconfig = process.env.KUBECONFIG;
366
- const originalAnthropicKey = process.env.ANTHROPIC_API_KEY;
367
-
368
- afterEach(() => {
369
- // Restore original environment variables
370
- if (originalKubeconfig) {
371
- process.env.KUBECONFIG = originalKubeconfig;
372
- } else {
373
- delete process.env.KUBECONFIG;
374
- }
375
-
376
- if (originalAnthropicKey) {
377
- process.env.ANTHROPIC_API_KEY = originalAnthropicKey;
378
- } else {
379
- delete process.env.ANTHROPIC_API_KEY;
380
- }
381
- });
382
-
383
- test('should read ANTHROPIC_API_KEY from environment when not provided in config', () => {
384
- process.env.ANTHROPIC_API_KEY = 'test-api-key-from-env';
385
- delete process.env.KUBECONFIG;
386
-
387
- const dotAI = new DotAI();
388
-
389
- // Use the public getter to verify API key was read from environment
390
- expect(dotAI.getAnthropicApiKey()).toBe('test-api-key-from-env');
391
- });
392
-
393
- test('should use provided config values over environment variables', () => {
394
- process.env.KUBECONFIG = '/env/kubeconfig.yaml';
395
- process.env.ANTHROPIC_API_KEY = 'env-api-key';
396
-
397
- const dotAI = new DotAI({
398
- kubernetesConfig: '/override/kubeconfig.yaml',
399
- anthropicApiKey: 'override-api-key'
400
- });
401
-
402
- // Should use the provided config values, not environment
403
- expect(dotAI.getAnthropicApiKey()).toBe('override-api-key');
404
- });
405
-
406
- test('should handle missing environment variables gracefully', () => {
407
- delete process.env.KUBECONFIG;
408
- delete process.env.ANTHROPIC_API_KEY;
409
-
410
- const dotAI = new DotAI();
411
-
412
- expect(dotAI.getAnthropicApiKey()).toBeUndefined();
413
- });
414
-
415
- test('should provide public getter for API key', () => {
416
- const dotAI = new DotAI({
417
- anthropicApiKey: 'test-getter-key'
418
- });
419
-
420
- expect(dotAI.getAnthropicApiKey()).toBe('test-getter-key');
421
- });
422
-
423
- test('should return undefined when no API key is available', () => {
424
- delete process.env.ANTHROPIC_API_KEY;
425
-
426
- const dotAI = new DotAI();
427
-
428
- expect(dotAI.getAnthropicApiKey()).toBeUndefined();
429
- });
430
-
431
- test('should validate configuration and reject empty API key', () => {
432
- expect(() => {
433
- new DotAI({
434
- anthropicApiKey: ''
435
- });
436
- }).toThrow('Invalid configuration: Empty API key provided');
437
- });
438
- });
439
- });
@@ -1,8 +0,0 @@
1
- apiVersion: v1
2
- kind: ConfigMap
3
- metadata:
4
- name: configmap-no-best-practices
5
- # Missing labels and namespace - should trigger warnings
6
- data:
7
- config.yaml: |
8
- setting: value
@@ -1,6 +0,0 @@
1
- apiVersion: pkg.crossplane.io/v1
2
- kind: Configuration
3
- metadata:
4
- name: crossplane-app
5
- spec:
6
- package: xpkg.upbound.io/devops-toolkit/dot-application:v3.0.15