/**
* Docker Tools Tests
*/
import { jest } from '@jest/globals';
import {
dockerCheckSetup,
dockerAnalyzeProject,
dockerBuild,
setCommandRunner
} from '../src/tools.js';
describe('Docker Tools', () => {
let mockRunner;
beforeEach(() => {
mockRunner = jest.fn();
setCommandRunner(mockRunner);
});
describe('dockerCheckSetup', () => {
test('should pass all checks when Docker is fully configured', async () => {
mockRunner
.mockResolvedValueOnce({ success: true, stdout: 'Docker version 24.0.0', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'CONTAINER ID IMAGE', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'Docker Compose version v2.20.0', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'developer', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'developer docker sudo', stderr: '' });
const result = await dockerCheckSetup();
expect(result.content[0].text).toContain('[OK] Docker installed');
expect(result.content[0].text).toContain('[OK] Docker daemon is running');
expect(result.content[0].text).toContain('[OK] Docker Compose');
expect(result.content[0].text).toContain('[OK] User is in docker group');
});
test('should fail when Docker is not installed', async () => {
mockRunner.mockResolvedValueOnce({
success: false,
error: 'command not found: docker',
stdout: '',
stderr: ''
});
const result = await dockerCheckSetup();
expect(result.content[0].text).toContain('[FAIL] Docker not installed');
expect(result.content[0].text).toContain('https://docs.docker.com/get-docker/');
});
test('should warn when Docker daemon is not running', async () => {
mockRunner
.mockResolvedValueOnce({ success: true, stdout: 'Docker version 24.0.0', stderr: '' })
.mockResolvedValueOnce({ success: false, error: 'Cannot connect to Docker daemon', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'Docker Compose version v2.20.0', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'developer', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'developer sudo', stderr: '' });
const result = await dockerCheckSetup();
expect(result.content[0].text).toContain('[OK] Docker installed');
expect(result.content[0].text).toContain('[FAIL] Docker daemon not running');
expect(result.content[0].text).toContain('systemctl start docker');
});
test('should warn when user is not in docker group', async () => {
mockRunner
.mockResolvedValueOnce({ success: true, stdout: 'Docker version 24.0.0', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'CONTAINER ID IMAGE', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'Docker Compose version v2.20.0', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'developer', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'developer sudo wheel', stderr: '' });
const result = await dockerCheckSetup();
expect(result.content[0].text).toContain('[WARN] User not in docker group');
expect(result.content[0].text).toContain('usermod -aG docker');
});
test('should warn when Docker Compose is not installed', async () => {
mockRunner
.mockResolvedValueOnce({ success: true, stdout: 'Docker version 24.0.0', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'CONTAINER ID IMAGE', stderr: '' })
.mockResolvedValueOnce({ success: false, error: 'command not found', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'developer', stderr: '' })
.mockResolvedValueOnce({ success: true, stdout: 'developer docker', stderr: '' });
const result = await dockerCheckSetup();
expect(result.content[0].text).toContain('[WARN] Docker Compose not found');
});
});
describe('dockerAnalyzeProject', () => {
// Note: These tests are limited because dockerAnalyzeProject uses fs.access
// In a real test scenario, we'd need to mock the fs module as well
test('should detect unknown project type and return generic Dockerfile', async () => {
const result = await dockerAnalyzeProject({ path: '/nonexistent/path' });
expect(result.content[0].text).toContain('PROJECT TYPE DETECTED: unknown');
expect(result.content[0].text).toContain('FROM ubuntu:22.04');
});
});
describe('dockerBuild', () => {
test('should reject invalid image names - uppercase', async () => {
const result = await dockerBuild({ image_name: 'MyApp', tag: 'latest', path: '.' });
expect(result.content[0].text).toContain('Invalid image name');
expect(result.content[0].text).toContain('Must be lowercase');
});
test('should reject invalid image names - starts with special char', async () => {
const result = await dockerBuild({ image_name: '-myapp', tag: 'latest', path: '.' });
expect(result.content[0].text).toContain('Invalid image name');
expect(result.content[0].text).toContain('Must start with letter or number');
});
test('should reject invalid image names - special characters', async () => {
const result = await dockerBuild({ image_name: 'my@app', tag: 'latest', path: '.' });
expect(result.content[0].text).toContain('Invalid image name');
});
test('should accept valid image names', async () => {
// This will fail because Dockerfile doesn't exist, but validates the name check passes
const result = await dockerBuild({ image_name: 'myapp', tag: 'latest', path: '/nonexistent' });
expect(result.content[0].text).toContain('No Dockerfile found');
expect(result.content[0].text).not.toContain('Invalid image name');
});
test('should accept valid image name with dots and hyphens', async () => {
const result = await dockerBuild({ image_name: 'my-app.service', tag: 'v1.0', path: '/nonexistent' });
expect(result.content[0].text).toContain('No Dockerfile found');
expect(result.content[0].text).not.toContain('Invalid image name');
});
test('should report missing Dockerfile', async () => {
const result = await dockerBuild({ image_name: 'myapp', tag: 'latest', path: '/nonexistent/path' });
expect(result.content[0].text).toContain('No Dockerfile found');
expect(result.content[0].text).toContain('docker_analyze_project');
});
});
});