/**
* Integration Test: Version Tool
*
* Tests the version tool via REST API against a real test cluster.
* Validates that the tool returns comprehensive system status information.
*
* NOTE: Written based on actual API response inspection following PRD best practices.
*/
import { describe, test, expect, beforeAll } from 'vitest';
import { IntegrationTest } from '../helpers/test-base.js';
import packageJson from '../../../package.json';
import { CURRENT_MODELS } from '../../../dist/core/model-config.js';
describe.concurrent('Version Tool Integration', () => {
const integrationTest = new IntegrationTest();
// Get expected provider and model based on test environment configuration
const aiProvider = process.env.AI_PROVIDER || 'anthropic';
const expectedModelName = CURRENT_MODELS[aiProvider as keyof typeof CURRENT_MODELS];
// All providers use VercelProvider (PRD #238), providerType matches the configured provider
const expectedProviderType = aiProvider;
// Detect deployment mode based on MCP_BASE_URL
const isInClusterMode = process.env.MCP_BASE_URL?.includes('nip.io') || false;
beforeAll(() => {
// Verify we're using the test environment (either kubeconfig or in-cluster)
if (!isInClusterMode) {
const kubeconfig = process.env.KUBECONFIG;
expect(kubeconfig).toContain('kubeconfig-test.yaml');
}
});
describe('System Status via REST API', () => {
test('should return comprehensive system status with correct structure', async () => {
// Define expected response structure (based on actual API inspection)
// Adjust expectations based on deployment mode (host vs in-cluster)
const expectedVersionResponse = {
success: true,
data: {
tool: 'version',
executionTime: expect.any(Number), // Variable - execution time varies
result: {
status: 'success',
system: {
version: {
version: packageJson.version, // Dynamic - should match actual package.json version
nodeVersion: expect.stringMatching(/^v\d+\.\d+\.\d+/), // Pattern - Node.js version changes
platform: isInClusterMode ? 'linux' : process.platform, // In-cluster runs on Linux
arch: expect.any(String) // Architecture varies
},
vectorDB: {
connected: true, // Specific - should be connected to Qdrant
url: isInClusterMode
? expect.stringContaining('qdrant') // In-cluster: service DNS (qdrant.dot-ai.svc.cluster.local)
: 'http://localhost:6335', // Host mode: localhost
collections: {
patterns: expect.objectContaining({
exists: expect.any(Boolean)
}),
policies: expect.objectContaining({
exists: expect.any(Boolean)
}),
capabilities: expect.objectContaining({
exists: expect.any(Boolean)
})
}
},
embedding: {
available: true, // Specific - test environment should have embedding configured
provider: 'openai', // Specific - using OpenAI for embeddings
model: 'text-embedding-3-small', // Specific - model name
dimensions: 1536 // Specific - embedding dimensions
},
aiProvider: {
connected: true, // Specific - should be connected with API key
keyConfigured: true, // Specific - API key should be configured
providerType: expectedProviderType, // Specific - validates against AI_PROVIDER env var
modelName: expectedModelName // Specific - validates against AI_PROVIDER env var
},
kubernetes: {
connected: true, // Specific - should be connected to our test cluster
kubeconfig: isInClusterMode
? 'in-cluster' // In-cluster: uses service account
: expect.stringContaining('kubeconfig-test.yaml'), // Host mode: uses kubeconfig file
clusterInfo: {
context: isInClusterMode ? 'in-cluster' : 'kind-dot-test', // Context differs by mode
version: expect.stringMatching(/^v\d+\.\d+\.\d+/), // Pattern - K8s version changes
endpoint: isInClusterMode
? expect.stringMatching(/^https:\/\/\d+\.\d+\.\d+\.\d+:\d+$/) // In-cluster: Kubernetes service IP
: expect.stringMatching(/^https:\/\/127\.0\.0\.1:\d+$/) // Host mode: localhost with port
}
},
capabilities: {
systemReady: true, // Specific - capability system should be ready
vectorDBHealthy: true, // Specific - vector DB should be healthy
collectionAccessible: true, // Specific - collections should be accessible
storedCount: expect.any(Number), // Variable - stored count varies
lastDiagnosis: expect.stringMatching(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/) // Pattern - ISO timestamp
},
kyverno: {
installed: true, // Specific - Kyverno should be installed
version: expect.stringMatching(/^\d+\.\d+\.\d+$/), // Pattern - semantic version
webhookReady: true, // Specific - webhook should be ready
policyGenerationReady: true // Specific - policy generation should be ready
}
},
summary: {
overall: 'healthy', // Specific - test environment should be healthy
patternSearch: expect.stringMatching(/^(semantic\+keyword|keyword|semantic)$/), // Pattern - search capabilities
capabilityScanning: 'ready', // Specific - capability scanning should be ready
kubernetesAccess: 'connected', // Specific - should match kubernetes.connected
policyIntentManagement: 'ready', // Specific - policy intent management should be ready (available without Kyverno)
kyvernoPolicyGeneration: 'ready', // Specific - Kyverno policy generation should be ready (requires Kyverno)
capabilities: expect.arrayContaining([
'policy-intent-management', // Available with Vector DB and embedding service
'capability-scanning',
'semantic-search',
'ai-recommendations',
'kubernetes-integration',
'kyverno-policy-generation' // Available only when Kyverno is installed
]) // Pattern - should contain all expected capabilities
},
timestamp: expect.stringMatching(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/) // Pattern - ISO timestamp
}
},
meta: {
timestamp: expect.stringMatching(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/), // Pattern - ISO timestamp
requestId: expect.stringMatching(/^rest_\d+_\d+$/), // Pattern - format is predictable
version: 'v1' // Specific - API version is fixed
}
};
// Call version tool via REST API (POST request as required)
const response = await integrationTest.httpClient.post('/api/v1/tools/version', {
interaction_id: 'system_status'
});
// Single comprehensive assertion using expected structure
expect(response).toMatchObject(expectedVersionResponse);
});
test('should handle POST method requirement', async () => {
// Define expected error response structure (based on actual API inspection)
const expectedErrorResponse = {
success: false,
error: {
code: 'METHOD_NOT_ALLOWED',
message: 'Only POST method allowed for tool execution'
},
meta: {
timestamp: expect.stringMatching(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/),
requestId: expect.stringMatching(/^rest_\d+_\d+$/),
version: 'v1'
}
};
// Test that GET method is not allowed
const response = await integrationTest.httpClient.get('/api/v1/tools/version');
// Single comprehensive assertion using expected structure
expect(response).toMatchObject(expectedErrorResponse);
});
});
describe('Test Environment Validation', () => {
test('should use test-specific configuration', async () => {
// Define expected response structure for test environment validation
// Adjust expectations based on deployment mode
const expectedTestResponse = {
success: true,
data: {
tool: 'version',
executionTime: expect.any(Number),
result: {
status: 'success',
system: {
kubernetes: {
connected: true,
kubeconfig: isInClusterMode
? 'in-cluster'
: expect.stringContaining('kubeconfig-test.yaml'),
clusterInfo: {
context: isInClusterMode ? 'in-cluster' : 'kind-dot-test',
version: expect.stringMatching(/^v\d+\.\d+\.\d+/),
endpoint: isInClusterMode
? expect.stringMatching(/^https:\/\/\d+\.\d+\.\d+\.\d+:\d+$/)
: expect.stringMatching(/^https:\/\/127\.0\.0\.1:\d+$/)
}
}
}
}
},
meta: {
timestamp: expect.stringMatching(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/),
requestId: expect.stringMatching(/^rest_\d+_\d+$/),
version: 'v1'
}
};
const response = await integrationTest.httpClient.post('/api/v1/tools/version', {
interaction_id: 'test_environment_validation'
});
// Validate test environment configuration in API response
expect(response).toMatchObject(expectedTestResponse);
});
});
describe('Error Handling', () => {
test('should handle invalid endpoints gracefully', async () => {
// Define expected error response structure (based on actual API inspection)
const expectedErrorResponse = {
success: false,
error: {
code: 'TOOL_NOT_FOUND',
message: "Tool 'nonexistent' not found"
},
meta: {
timestamp: expect.stringMatching(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/),
requestId: expect.stringMatching(/^rest_\d+_\d+$/),
version: 'v1'
}
};
const response = await integrationTest.httpClient.post('/api/v1/tools/nonexistent', {});
// Single comprehensive assertion using expected structure
expect(response).toMatchObject(expectedErrorResponse);
});
});
});