Skip to main content
Glama
orneryd

M.I.M.I.R - Multi-agent Intelligent Memory & Insight Repository

by orneryd
ImageProcessor.test.ts9.63 kB
/** * @file testing/indexing/ImageProcessor.test.ts * @description Unit tests for ImageProcessor * * NOTE: These tests create temporary test images for testing. * Uses os.tmpdir() for cross-platform temp directory support. * No external services are required. Files are cleaned up after tests. */ import { describe, it, expect, beforeEach, beforeAll, afterAll } from 'vitest'; import { ImageProcessor } from '../../src/indexing/ImageProcessor.js'; import sharp from 'sharp'; import * as fs from 'fs/promises'; import * as path from 'path'; import * as os from 'os'; describe('ImageProcessor', () => { let processor: ImageProcessor; const testFiles: string[] = []; let tempDir: string; // Get cross-platform temp directory beforeAll(async () => { // Use os.tmpdir() for cross-platform support (works on Windows, macOS, Linux) tempDir = path.join(os.tmpdir(), 'mimir-image-processor-tests'); await fs.mkdir(tempDir, { recursive: true }); }); beforeEach(() => { processor = new ImageProcessor({ maxPixels: 3211264, // ~1792×1792 targetSize: 1536, resizeQuality: 90 }); }); afterAll(async () => { // Clean up test files for (const file of testFiles) { try { await fs.unlink(file); } catch (e) { // Ignore errors - file may not exist } } // Clean up temp directory try { await fs.rm(tempDir, { recursive: true, force: true }); } catch (e) { // Ignore errors } }); describe('isImageFile', () => { it('should identify image files correctly', () => { expect(ImageProcessor.isImageFile('photo.jpg')).toBe(true); expect(ImageProcessor.isImageFile('photo.jpeg')).toBe(true); expect(ImageProcessor.isImageFile('photo.png')).toBe(true); expect(ImageProcessor.isImageFile('photo.webp')).toBe(true); expect(ImageProcessor.isImageFile('photo.gif')).toBe(true); expect(ImageProcessor.isImageFile('photo.bmp')).toBe(true); expect(ImageProcessor.isImageFile('photo.tiff')).toBe(true); }); it('should reject non-image files', () => { expect(ImageProcessor.isImageFile('document.pdf')).toBe(false); expect(ImageProcessor.isImageFile('code.ts')).toBe(false); expect(ImageProcessor.isImageFile('data.json')).toBe(false); expect(ImageProcessor.isImageFile('video.mp4')).toBe(false); }); it('should be case insensitive', () => { expect(ImageProcessor.isImageFile('PHOTO.JPG')).toBe(true); expect(ImageProcessor.isImageFile('Photo.PNG')).toBe(true); }); }); describe('prepareImageForVL', () => { it('should process small images without resizing', async () => { // Create a small test image (100×100) const testImage = await sharp({ create: { width: 100, height: 100, channels: 3, background: { r: 255, g: 0, b: 0 } } }) .jpeg() .toBuffer(); const tempPath = path.join(tempDir, 'test-small-image.jpg'); testFiles.push(tempPath); await sharp(testImage).toFile(tempPath); const result = await processor.prepareImageForVL(tempPath); expect(result.wasResized).toBe(false); expect(result.originalSize.width).toBe(100); expect(result.originalSize.height).toBe(100); expect(result.processedSize.width).toBe(100); expect(result.processedSize.height).toBe(100); expect(result.base64).toBeTruthy(); expect(result.buffer).toBeInstanceOf(Buffer); }); it('should resize large images', async () => { // Create a large test image (2560×1440 = 3.69 MP, exceeds 3.21 MP limit) const testImage = await sharp({ create: { width: 2560, height: 1440, channels: 3, background: { r: 0, g: 255, b: 0 } } }) .jpeg() .toBuffer(); const tempPath = path.join(tempDir, 'test-large-image.jpg'); testFiles.push(tempPath); await sharp(testImage).toFile(tempPath); const result = await processor.prepareImageForVL(tempPath); expect(result.wasResized).toBe(true); expect(result.originalSize.width).toBe(2560); expect(result.originalSize.height).toBe(1440); expect(result.processedSize.width).toBeLessThan(2560); expect(result.processedSize.height).toBeLessThan(1440); // Verify aspect ratio is preserved const originalAspect = result.originalSize.width / result.originalSize.height; const processedAspect = result.processedSize.width / result.processedSize.height; expect(Math.abs(originalAspect - processedAspect)).toBeLessThan(0.01); // Verify it's within limits const processedPixels = result.processedSize.width * result.processedSize.height; expect(processedPixels).toBeLessThanOrEqual(3211264); }); it('should preserve aspect ratio for portrait images', async () => { // Create a portrait image (1080×1920) const testImage = await sharp({ create: { width: 1080, height: 1920, channels: 3, background: { r: 0, g: 0, b: 255 } } }) .jpeg() .toBuffer(); const tempPath = path.join(tempDir, 'test-portrait-image.jpg'); testFiles.push(tempPath); await sharp(testImage).toFile(tempPath); const result = await processor.prepareImageForVL(tempPath); expect(result.wasResized).toBe(false); // 1080×1920 = 2.07 MP, within limit const originalAspect = result.originalSize.width / result.originalSize.height; const processedAspect = result.processedSize.width / result.processedSize.height; expect(Math.abs(originalAspect - processedAspect)).toBeLessThan(0.01); }); it('should handle square images', async () => { // Create a square image (1792×1792 = 3.21 MP, at the limit) const testImage = await sharp({ create: { width: 1792, height: 1792, channels: 3, background: { r: 128, g: 128, b: 128 } } }) .jpeg() .toBuffer(); const tempPath = path.join(tempDir, 'test-square-image.jpg'); testFiles.push(tempPath); await sharp(testImage).toFile(tempPath); const result = await processor.prepareImageForVL(tempPath); expect(result.wasResized).toBe(false); // At the limit, no resize needed expect(result.originalSize.width).toBe(1792); expect(result.originalSize.height).toBe(1792); }); }); describe('createDataURL', () => { it('should create valid data URL for JPEG', () => { const base64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='; const dataURL = processor.createDataURL(base64, 'jpeg'); expect(dataURL).toMatch(/^data:image\/jpeg;base64,/); expect(dataURL).toContain(base64); }); it('should create valid data URL for PNG', () => { const base64 = 'test123'; const dataURL = processor.createDataURL(base64, 'png'); expect(dataURL).toBe('data:image/png;base64,test123'); }); it('should handle different formats', () => { const base64 = 'test'; expect(processor.createDataURL(base64, 'webp')).toMatch(/^data:image\/webp;base64,/); expect(processor.createDataURL(base64, 'gif')).toMatch(/^data:image\/gif;base64,/); expect(processor.createDataURL(base64, 'bmp')).toMatch(/^data:image\/bmp;base64,/); }); it('should default to jpeg for unknown formats', () => { const base64 = 'test'; const dataURL = processor.createDataURL(base64, 'unknown'); expect(dataURL).toMatch(/^data:image\/jpeg;base64,/); }); }); describe('configuration', () => { it('should respect custom maxPixels', async () => { const customProcessor = new ImageProcessor({ maxPixels: 1000000, // 1 MP limit targetSize: 1024, resizeQuality: 90 }); // Create image that exceeds custom limit (1920×1080 = 2.07 MP) const testImage = await sharp({ create: { width: 1920, height: 1080, channels: 3, background: { r: 255, g: 255, b: 255 } } }) .jpeg() .toBuffer(); const tempPath = path.join(tempDir, 'test-custom-limit.jpg'); testFiles.push(tempPath); await sharp(testImage).toFile(tempPath); const result = await customProcessor.prepareImageForVL(tempPath); expect(result.wasResized).toBe(true); const processedPixels = result.processedSize.width * result.processedSize.height; expect(processedPixels).toBeLessThanOrEqual(1000000); }); it('should respect custom targetSize', async () => { const customProcessor = new ImageProcessor({ maxPixels: 3211264, targetSize: 800, // Conservative target resizeQuality: 90 }); // Create large image const testImage = await sharp({ create: { width: 3840, height: 2160, channels: 3, background: { r: 255, g: 255, b: 255 } } }) .jpeg() .toBuffer(); const tempPath = path.join(tempDir, 'test-custom-target.jpg'); testFiles.push(tempPath); await sharp(testImage).toFile(tempPath); const result = await customProcessor.prepareImageForVL(tempPath); expect(result.wasResized).toBe(true); expect(Math.max(result.processedSize.width, result.processedSize.height)).toBeLessThanOrEqual(800); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/orneryd/Mimir'

If you have feedback or need assistance with the MCP directory API, please join our Discord server