UNPKG

@neirth/sony-camera-mcp

Version:

MCP Server for controlling Sony Alpha 6100 camera

410 lines â€ĸ 17.3 kB
// 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