byos_laravel/tests/Unit/Services/ImageGenerationServiceTest.php
2025-09-24 19:24:55 +02:00

320 lines
11 KiB
PHP

<?php
declare(strict_types=1);
use App\Enums\ImageFormat;
use App\Models\Device;
use App\Models\DeviceModel;
use App\Services\ImageGenerationService;
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
TrmnlPipeline::fake();
});
it('get_image_settings returns device model settings when available', function (): void {
// Create a DeviceModel
$deviceModel = DeviceModel::factory()->create([
'width' => 1024,
'height' => 768,
'colors' => 256,
'bit_depth' => 8,
'scale_factor' => 1.5,
'rotation' => 90,
'mime_type' => 'image/png',
'offset_x' => 10,
'offset_y' => 20,
]);
// Create a device with the DeviceModel
$device = Device::factory()->create([
'device_model_id' => $deviceModel->id,
]);
// Use reflection to access private method
$reflection = new ReflectionClass(ImageGenerationService::class);
$method = $reflection->getMethod('getImageSettings');
$method->setAccessible(true);
$settings = $method->invoke(null, $device);
// Assert DeviceModel settings are used
expect($settings['width'])->toBe(1024);
expect($settings['height'])->toBe(768);
expect($settings['colors'])->toBe(256);
expect($settings['bit_depth'])->toBe(8);
expect($settings['scale_factor'])->toBe(1.5);
expect($settings['rotation'])->toBe(90);
expect($settings['mime_type'])->toBe('image/png');
expect($settings['offset_x'])->toBe(10);
expect($settings['offset_y'])->toBe(20);
expect($settings['use_model_settings'])->toBe(true);
});
it('get_image_settings falls back to device settings when no device model', function (): void {
// Create a device without DeviceModel
$device = Device::factory()->create([
'width' => 800,
'height' => 480,
'rotate' => 180,
'image_format' => ImageFormat::PNG_8BIT_GRAYSCALE->value,
]);
// Use reflection to access private method
$reflection = new ReflectionClass(ImageGenerationService::class);
$method = $reflection->getMethod('getImageSettings');
$method->setAccessible(true);
$settings = $method->invoke(null, $device);
// Assert device settings are used
expect($settings['width'])->toBe(800);
expect($settings['height'])->toBe(480);
expect($settings['rotation'])->toBe(180);
expect($settings['image_format'])->toBe(ImageFormat::PNG_8BIT_GRAYSCALE->value);
expect($settings['use_model_settings'])->toBe(false);
});
it('get_image_settings uses defaults for missing device properties', function (): void {
// Create a device without DeviceModel and missing properties
$device = Device::factory()->create([
'width' => null,
'height' => null,
'rotate' => null,
// image_format has a default value of 'auto', so we can't set it to null
]);
// Use reflection to access private method
$reflection = new ReflectionClass(ImageGenerationService::class);
$method = $reflection->getMethod('getImageSettings');
$method->setAccessible(true);
$settings = $method->invoke(null, $device);
// Assert default values are used
expect($settings['width'])->toBe(800);
expect($settings['height'])->toBe(480);
expect($settings['rotation'])->toBe(0);
expect($settings['colors'])->toBe(2);
expect($settings['bit_depth'])->toBe(1);
expect($settings['scale_factor'])->toBe(1.0);
expect($settings['mime_type'])->toBe('image/png');
expect($settings['offset_x'])->toBe(0);
expect($settings['offset_y'])->toBe(0);
// image_format defaults to 'auto' when not set
expect($settings['image_format'])->toBe('auto');
});
it('determine_image_format_from_model returns correct formats', function (): void {
// Use reflection to access private method
$reflection = new ReflectionClass(ImageGenerationService::class);
$method = $reflection->getMethod('determineImageFormatFromModel');
$method->setAccessible(true);
// Test BMP format
$bmpModel = DeviceModel::factory()->create([
'mime_type' => 'image/bmp',
'bit_depth' => 1,
'colors' => 2,
]);
$format = $method->invoke(null, $bmpModel);
expect($format)->toBe(ImageFormat::BMP3_1BIT_SRGB->value);
// Test PNG 8-bit grayscale format
$pngGrayscaleModel = DeviceModel::factory()->create([
'mime_type' => 'image/png',
'bit_depth' => 8,
'colors' => 2,
]);
$format = $method->invoke(null, $pngGrayscaleModel);
expect($format)->toBe(ImageFormat::PNG_8BIT_GRAYSCALE->value);
// Test PNG 8-bit 256 color format
$png256Model = DeviceModel::factory()->create([
'mime_type' => 'image/png',
'bit_depth' => 8,
'colors' => 256,
]);
$format = $method->invoke(null, $png256Model);
expect($format)->toBe(ImageFormat::PNG_8BIT_256C->value);
// Test PNG 2-bit 4 color format
$png4ColorModel = DeviceModel::factory()->create([
'mime_type' => 'image/png',
'bit_depth' => 2,
'colors' => 4,
]);
$format = $method->invoke(null, $png4ColorModel);
expect($format)->toBe(ImageFormat::PNG_2BIT_4C->value);
// Test unknown format returns AUTO
$unknownModel = DeviceModel::factory()->create([
'mime_type' => 'image/jpeg',
'bit_depth' => 16,
'colors' => 65536,
]);
$format = $method->invoke(null, $unknownModel);
expect($format)->toBe(ImageFormat::AUTO->value);
});
it('cleanup_folder identifies active images correctly', function (): void {
// Create devices with images
$device1 = Device::factory()->create(['current_screen_image' => 'active-uuid-1']);
$device2 = Device::factory()->create(['current_screen_image' => 'active-uuid-2']);
$device3 = Device::factory()->create(['current_screen_image' => null]);
// Create a plugin with image
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'plugin-uuid']);
// For unit testing, we could test the logic that determines active UUIDs
$activeDeviceImageUuids = Device::pluck('current_screen_image')->filter()->toArray();
$activePluginImageUuids = App\Models\Plugin::pluck('current_image')->filter()->toArray();
$activeImageUuids = array_merge($activeDeviceImageUuids, $activePluginImageUuids);
expect($activeImageUuids)->toContain('active-uuid-1');
expect($activeImageUuids)->toContain('active-uuid-2');
expect($activeImageUuids)->toContain('plugin-uuid');
expect($activeImageUuids)->not->toContain(null);
});
it('reset_if_not_cacheable detects device models', function (): void {
// Create a plugin
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
// Create a device with DeviceModel
Device::factory()->create([
'device_model_id' => DeviceModel::factory()->create()->id,
]);
// Test that the method detects DeviceModels and resets cache
ImageGenerationService::resetIfNotCacheable($plugin);
$plugin->refresh();
expect($plugin->current_image)->toBeNull();
});
it('reset_if_not_cacheable detects custom dimensions', function (): void {
// Create a plugin
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
// Create a device with custom dimensions
Device::factory()->create([
'width' => 1024, // Different from default 800
'height' => 768, // Different from default 480
]);
// Test that the method detects custom dimensions and resets cache
ImageGenerationService::resetIfNotCacheable($plugin);
$plugin->refresh();
expect($plugin->current_image)->toBeNull();
});
it('reset_if_not_cacheable preserves cache for standard devices', function (): void {
// Create a plugin
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
// Create devices with standard dimensions
Device::factory()->count(3)->create([
'width' => 800,
'height' => 480,
'rotate' => 0,
]);
// Test that the method preserves cache for standard devices
ImageGenerationService::resetIfNotCacheable($plugin);
$plugin->refresh();
expect($plugin->current_image)->toBe('test-uuid');
});
it('reset_if_not_cacheable preserves cache for og_png and og_plus device models', function (): void {
// Create a plugin
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
// Create og_png device model
$ogPngModel = DeviceModel::factory()->create([
'name' => 'test_og_png',
'width' => 800,
'height' => 480,
'rotation' => 0,
]);
// Create og_plus device model
$ogPlusModel = DeviceModel::factory()->create([
'name' => 'test_og_plus',
'width' => 800,
'height' => 480,
'rotation' => 0,
]);
// Create devices with og_png and og_plus device models
Device::factory()->create(['device_model_id' => $ogPngModel->id]);
Device::factory()->create(['device_model_id' => $ogPlusModel->id]);
// Test that the method preserves cache for standard device models
ImageGenerationService::resetIfNotCacheable($plugin);
$plugin->refresh();
expect($plugin->current_image)->toBe('test-uuid');
});
it('reset_if_not_cacheable resets cache for non-standard device models', function (): void {
// Create a plugin
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
// Create a non-standard device model (e.g., kindle)
$kindleModel = DeviceModel::factory()->create([
'name' => 'test_amazon_kindle_2024',
'width' => 1400,
'height' => 840,
'rotation' => 90,
]);
// Create a device with the non-standard device model
Device::factory()->create(['device_model_id' => $kindleModel->id]);
// Test that the method resets cache for non-standard device models
ImageGenerationService::resetIfNotCacheable($plugin);
$plugin->refresh();
expect($plugin->current_image)->toBeNull();
});
it('reset_if_not_cacheable handles null plugin', function (): void {
// Test that the method handles null plugin gracefully
expect(fn () => ImageGenerationService::resetIfNotCacheable(null))->not->toThrow(Exception::class);
});
it('image_format enum includes new 2bit 4c format', function (): void {
// Test that the new format is properly defined in the enum
expect(ImageFormat::PNG_2BIT_4C->value)->toBe('png_2bit_4c');
expect(ImageFormat::PNG_2BIT_4C->label())->toBe('PNG 2-bit Grayscale 4c');
});
it('device model relationship works correctly', function (): void {
// Create a DeviceModel
$deviceModel = DeviceModel::factory()->create();
// Create a device with the DeviceModel
$device = Device::factory()->create([
'device_model_id' => $deviceModel->id,
]);
// Test the relationship
expect($device->deviceModel)->toBeInstanceOf(DeviceModel::class);
expect($device->deviceModel->id)->toBe($deviceModel->id);
});
it('device without device model returns null relationship', function (): void {
// Create a device without DeviceModel
$device = Device::factory()->create([
'device_model_id' => null,
]);
// Test the relationship returns null
expect($device->deviceModel)->toBeNull();
});