@neirth/sony-camera-mcp
Version:
MCP Server for controlling Sony Alpha 6100 camera
410 lines âĸ 17.3 kB
JavaScript
// camera.test.js
// Jest tests for Sony camera integration
import SonyCamera from '../sony-camera';
import fetch from 'node-fetch';
import { CameraError } from '../models';
// Test configuration
const CAMERA_IP = process.env.CAMERA_IP || "192.168.122.1";
const CAMERA_PORT = process.env.CAMERA_PORT || "10000";
const DD_XML_PORT = process.env.DD_XML_PORT || "64321";
const DEBUG = process.env.DEBUG === 'true' || false;
// Variable to control if there is a camera connected
let hasCameraConnected = false;
// Function to verify if the camera is accessible
async function isCameraAvailable() {
try {
const response = await fetch(`http://${CAMERA_IP}:${DD_XML_PORT}/dd.xml`);
return response.ok;
}
catch (error) {
return false;
}
}
// Increase test timeout globally
const TEST_TIMEOUT = 120000; // 120 seconds
const LONG_TEST_TIMEOUT = 180000; // 3 minutes
// Shared camera instance for tests
let camera;
// Before all tests
beforeAll(async () => {
console.log("đ Preparing tests with Sony camera...");
console.log(`đĄ Configuration: IP=${CAMERA_IP}, Port=${CAMERA_PORT}, DD XML Port=${DD_XML_PORT}`);
// Verify if camera is available
hasCameraConnected = await isCameraAvailable();
if (!hasCameraConnected) {
console.log("â ī¸ No camera detected. Tests will be skipped.");
return;
}
camera = new SonyCamera({
cameraIp: CAMERA_IP,
cameraPort: CAMERA_PORT,
ddXmlPort: DD_XML_PORT,
debug: DEBUG
});
// Try to connect to the camera
try {
await camera.connect();
console.log("â
Connection established successfully");
}
catch (error) {
console.error(`â Connection error: ${error.message}`);
hasCameraConnected = false;
}
}, TEST_TIMEOUT);
// Before each test
beforeEach(() => {
// Clean up any previous state if needed
});
// After all tests
afterAll(async () => {
console.log("đ Finalizing tests...");
// Make sure to stop liveview if active
try {
await camera.stopLiveview();
}
catch (error) {
// Ignore errors when stopping liveview
}
});
// Helper for tests requiring camera
function testWithCamera(name, fn, timeout) {
test(name, async () => {
if (!hasCameraConnected) {
console.log(`âī¸ Test "${name}" skipped - No camera connected`);
return;
}
await fn();
}, timeout);
}
// Helper to verify camera errors
const expectCameraError = (error, code, retriable = false) => {
expect(error).toBeInstanceOf(CameraError);
expect(error.code).toBe(code);
expect(error.isRetriable()).toBe(retriable);
};
// Test group for basic camera information
describe('Basic camera information', () => {
beforeAll(() => {
if (!hasCameraConnected) {
console.log('â ī¸ Basic information tests skipped - No camera connected');
}
});
test('Get application information', async () => {
// Skip test if no camera
if (!hasCameraConnected) {
console.log('âī¸ Test skipped - No camera connected');
return;
}
const appInfo = await camera.getApplicationInfo();
// Verify that the response has the expected format
expect(Array.isArray(appInfo)).toBe(true);
expect(appInfo.length).toBeGreaterThanOrEqual(2);
expect(typeof appInfo[0]).toBe('string');
console.log(`đą App: ${appInfo[0]}, Version: ${appInfo[1]}`);
}, TEST_TIMEOUT);
});
// Test group for shoot modes
describe('Shoot modes', () => {
beforeAll(() => {
if (!hasCameraConnected) {
console.log('â ī¸ Shoot mode tests skipped - No camera connected');
}
});
testWithCamera('Get available shoot modes', async () => {
const shootModes = await camera.getAvailableShootMode();
// Verify that response has expected format (adapted to different formats)
expect(Array.isArray(shootModes)).toBe(true);
expect(shootModes.length).toBeGreaterThan(0);
// Show available modes
console.log(`đ¸ Mode information: ${JSON.stringify(shootModes)}`);
}, TEST_TIMEOUT);
testWithCamera('Get current shoot mode', async () => {
const shootMode = await camera.getShootMode();
// Verify that response has expected format
expect(Array.isArray(shootMode)).toBe(true);
expect(shootMode.length).toBeGreaterThan(0);
expect(typeof shootMode[0]).toBe('string');
console.log(`đ¸ Current mode: ${shootMode[0]}`);
}, TEST_TIMEOUT);
});
// Test group for liveview
describe('Liveview', () => {
beforeAll(() => {
if (!hasCameraConnected) {
console.log('â ī¸ Liveview tests skipped - No camera connected');
}
});
testWithCamera('Start and stop liveview', async () => {
// Start liveview
const liveviewResult = await camera.startLiveview();
const liveviewUrl = liveviewResult[0];
// Verify URL is valid
expect(typeof liveviewUrl).toBe('string');
expect(liveviewUrl.startsWith('http')).toBe(true);
console.log(`đĨ Liveview started: ${liveviewUrl}`);
// Wait a bit to see the liveview
await new Promise(resolve => setTimeout(resolve, 2000));
// Stop liveview
await camera.stopLiveview();
console.log(`đĨ Liveview stopped`);
}, TEST_TIMEOUT);
});
// Test group for zoom
describe('Zoom control', () => {
beforeAll(() => {
if (!hasCameraConnected) {
console.log('â ī¸ Zoom control tests skipped - No camera connected');
}
});
testWithCamera('Zoom in/out control', async () => {
// Zoom in
await camera.zoom("in", "1shot");
console.log(`đ Zoom in executed`);
// Wait a moment
await new Promise(resolve => setTimeout(resolve, 1000));
// Zoom out
await camera.zoom("out", "1shot");
console.log(`đ Zoom out executed`);
// There's no easy way to verify zoom worked,
// so we just verify no errors occurred
expect(true).toBe(true);
}, TEST_TIMEOUT);
});
// Test group for exposure parameters
describe('Exposure, ISO and shutter speed control', () => {
test('Get available exposure compensation values', async () => {
try {
const exposureValues = await camera.getAvailableExposureCompensation();
console.log(`đ Available exposure values: ${JSON.stringify(exposureValues)}`);
// Verify that response has valid format
expect(Array.isArray(exposureValues)).toBe(true);
expect(exposureValues.length).toBeGreaterThan(0);
}
catch (error) {
console.log(`â ī¸ Camera does not support getAvailableExposureCompensation: ${error.message}`);
}
}, TEST_TIMEOUT);
test('Get available ISO values', async () => {
try {
const isoValues = await camera.getAvailableIsoSpeedRate();
console.log(`đ Available ISO values: ${JSON.stringify(isoValues)}`);
// Verify that response has valid format
expect(Array.isArray(isoValues)).toBe(true);
expect(isoValues.length).toBeGreaterThan(0);
}
catch (error) {
console.log(`â ī¸ Camera does not support getAvailableIsoSpeedRate: ${error.message}`);
}
}, TEST_TIMEOUT);
test('Get available shutter speed values', async () => {
try {
const shutterValues = await camera.getAvailableShutterSpeed();
console.log(`đ Available shutter speed values: ${JSON.stringify(shutterValues)}`);
// Verify that response has valid format
expect(Array.isArray(shutterValues)).toBe(true);
expect(shutterValues.length).toBeGreaterThan(0);
}
catch (error) {
console.log(`â ī¸ Camera does not support getAvailableShutterSpeed: ${error.message}`);
}
}, TEST_TIMEOUT);
testWithCamera('Set exposure, ISO and speed values', async () => {
console.log('đ¸ Getting available camera values...');
// 1. Exposure compensation
const exposureValues = await camera.getAvailableExposureCompensation();
console.log(`đ Available exposure values: ${JSON.stringify(exposureValues)}`);
if (Array.isArray(exposureValues) && exposureValues.length >= 4) {
const [current, max, min, step] = exposureValues;
// Use a safe value: 0 (no compensation) or closest to 0
const safeExposure = 0;
if (safeExposure >= min && safeExposure <= max) {
console.log(`đ¯ Setting exposure compensation to: ${safeExposure}`);
await camera.setExposureCompensation(safeExposure);
}
else {
console.log('â ī¸ Could not set a safe exposure value');
}
}
// 2. ISO
const isoValues = await camera.getAvailableIsoSpeedRate();
console.log(`đ Available ISO values: ${JSON.stringify(isoValues)}`);
let validIsoValues = [];
if (Array.isArray(isoValues)) {
if (isoValues.length >= 2 && Array.isArray(isoValues[1])) {
validIsoValues = isoValues[1];
}
else if (isoValues.length > 0) {
validIsoValues = isoValues;
}
}
// Try to use AUTO first, then 100 if available, or lowest available value
const preferredIsoValues = ['AUTO', '100', '200'];
const safeIso = preferredIsoValues.find(iso => validIsoValues.includes(iso)) || validIsoValues[0];
if (safeIso) {
console.log(`đ¯ Setting ISO to: ${safeIso}`);
await camera.setIsoSpeedRate(safeIso);
}
// 3. Shutter speed
const shutterValues = await camera.getAvailableShutterSpeed();
console.log(`đ Available shutter speed values: ${JSON.stringify(shutterValues)}`);
let validShutterValues = [];
if (Array.isArray(shutterValues)) {
if (shutterValues.length >= 2 && Array.isArray(shutterValues[1])) {
validShutterValues = shutterValues[1];
}
else if (shutterValues.length > 0) {
validShutterValues = shutterValues;
}
}
// Try to use typical safe values: 1/60, 1/125, or closest available value
const preferredShutterSpeeds = ['1/60', '1/125', '1/250'];
const safeShutter = preferredShutterSpeeds.find(speed => validShutterValues.includes(speed)) || validShutterValues[0];
if (safeShutter) {
console.log(`đ¯ Setting shutter speed to: ${safeShutter}`);
await camera.setShutterSpeed(safeShutter);
}
console.log('â
Camera configuration completed with safe values');
// Test passes if we reach here without critical errors
expect(true).toBe(true);
}, LONG_TEST_TIMEOUT);
test('Get current exposure, ISO and speed values', async () => {
// Current exposure
try {
const exposureValue = await camera.getExposureCompensation();
console.log(`đ Current exposure: ${JSON.stringify(exposureValue)}`);
expect(Array.isArray(exposureValue)).toBe(true);
}
catch (error) {
console.log(`â ī¸ Error getting current exposure: ${error.message}`);
}
// Current ISO
try {
const isoValue = await camera.getIsoSpeedRate();
console.log(`đ Current ISO: ${JSON.stringify(isoValue)}`);
expect(Array.isArray(isoValue)).toBe(true);
}
catch (error) {
console.log(`â ī¸ Error getting current ISO: ${error.message}`);
}
// Current shutter speed
try {
const shutterValue = await camera.getShutterSpeed();
console.log(`đ Current shutter speed: ${JSON.stringify(shutterValue)}`);
expect(Array.isArray(shutterValue)).toBe(true);
}
catch (error) {
console.log(`â ī¸ Error getting current shutter speed: ${error.message}`);
}
}, TEST_TIMEOUT);
});
// Test group for exposure control
describe('Exposure control', () => {
beforeAll(() => {
if (!hasCameraConnected) {
console.log('â ī¸ Exposure control tests skipped - No camera connected');
}
});
testWithCamera('Get and set ISO', async () => {
// Get available ISO values
const isoValues = await camera.getAvailableIsoSpeedRate();
expect(Array.isArray(isoValues)).toBe(true);
if (Array.isArray(isoValues[0])) {
expect(isoValues[0]).toContain("AUTO");
console.log(`đ Available ISO values: ${isoValues[0].join(", ")}`);
}
else {
console.log(`đ Available ISO values: ${isoValues}`);
}
// Get current ISO
const currentIso = await camera.getIsoSpeedRate();
expect(Array.isArray(currentIso)).toBe(true);
console.log(`đ Current ISO: ${currentIso[0]}`);
// Set new ISO
await camera.setIsoSpeedRate("400");
console.log(`đ ISO set to 400`);
}, TEST_TIMEOUT);
testWithCamera('Get and set shutter speed', async () => {
// Get available speeds
const speeds = await camera.getAvailableShutterSpeed();
expect(Array.isArray(speeds)).toBe(true);
console.log(`⥠Available speeds: ${JSON.stringify(speeds)}`);
// Get current speed
const currentSpeed = await camera.getShutterSpeed();
expect(Array.isArray(currentSpeed)).toBe(true);
console.log(`⥠Current speed: ${JSON.stringify(currentSpeed)}`);
// Set new speed
await camera.setShutterSpeed("1/500");
console.log(`⥠Speed set to 1/500`);
}, TEST_TIMEOUT);
testWithCamera('Get and set exposure compensation', async () => {
// Get available values
const values = await camera.getAvailableExposureCompensation();
expect(Array.isArray(values)).toBe(true);
console.log(`đĄ Available compensations: ${JSON.stringify(values)}`);
// Get current value
const currentValue = await camera.getExposureCompensation();
expect(Array.isArray(currentValue)).toBe(true);
console.log(`đĄ Current compensation: ${JSON.stringify(currentValue)}`);
// Set new value
await camera.setExposureCompensation(1.0);
console.log(`đĄ Compensation set to +1.0`);
}, TEST_TIMEOUT);
});
// Test taking pictures
describe('Taking pictures', () => {
beforeAll(() => {
if (!hasCameraConnected) {
console.log('â ī¸ Picture taking tests skipped - No camera connected');
}
});
testWithCamera('Take a picture', async () => {
try {
console.log('đˇ Attempting to take a picture...');
// Stop liveview if active to improve performance
try {
await camera.stopLiveview();
console.log('đĨ Liveview stopped before taking picture');
await new Promise(resolve => setTimeout(resolve, 1000));
}
catch (error) {
console.log('âšī¸ Liveview was already stopped');
}
// Check and set still mode if necessary
const shootMode = await camera.getShootMode();
console.log(`đ¸ Current mode: ${shootMode[0]}`);
if (shootMode[0] !== 'still') {
await camera.setShootMode('still');
console.log('âŗ Changing to still mode...');
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Try to take the picture
console.log('đ¸ Taking picture...');
const startTime = Date.now();
const photoResult = await camera.takePicture();
const endTime = Date.now();
console.log(`âąī¸ Capture time: ${endTime - startTime}ms`);
// Verify correct format according to Sony documentation
expect(typeof photoResult).toBe('string');
expect(photoResult).toMatch(/^http/);
console.log(`đ¸ Image URL received: ${photoResult}`);
// Verify that the URL is accessible
try {
const response = await fetch(photoResult);
expect(response.ok).toBe(true);
const contentType = response.headers.get('content-type');
expect(contentType).toMatch(/^image\//);
console.log(`â
Image URL verified and accessible`);
}
catch (fetchError) {
console.warn(`â ī¸ Could not verify URL but format is correct: ${fetchError instanceof Error ? fetchError.message : String(fetchError)}`);
}
console.log('â
Photo test completed successfully');
}
catch (error) {
// Propagate all errors
console.error(`â Error taking photo: ${error.message}`);
throw error;
}
}, 40000);
});
//# sourceMappingURL=camera.test.js.map