diff --git a/app/Console/Commands/OidcTestCommand.php b/app/Console/Commands/OidcTestCommand.php index 2ecfef2..c04f263 100644 --- a/app/Console/Commands/OidcTestCommand.php +++ b/app/Console/Commands/OidcTestCommand.php @@ -2,7 +2,9 @@ namespace App\Console\Commands; +use Exception; use Illuminate\Console\Command; +use InvalidArgumentException; use Laravel\Socialite\Facades\Socialite; class OidcTestCommand extends Command @@ -31,7 +33,7 @@ class OidcTestCommand extends Command // Check if OIDC is enabled $enabled = config('services.oidc.enabled'); - $this->line("OIDC Enabled: " . ($enabled ? '✅ Yes' : '❌ No')); + $this->line('OIDC Enabled: '.($enabled ? '✅ Yes' : '❌ No')); // Check configuration values $endpoint = config('services.oidc.endpoint'); @@ -40,11 +42,11 @@ class OidcTestCommand extends Command $redirect = config('services.oidc.redirect'); $scopes = config('services.oidc.scopes', []); - $this->line("OIDC Endpoint: " . ($endpoint ? "✅ {$endpoint}" : '❌ Not set')); - $this->line("Client ID: " . ($clientId ? "✅ {$clientId}" : '❌ Not set')); - $this->line("Client Secret: " . ($clientSecret ? '✅ Set' : '❌ Not set')); - $this->line("Redirect URL: " . ($redirect ? "✅ {$redirect}" : '❌ Not set')); - $this->line("Scopes: " . (empty($scopes) ? '❌ Not set' : '✅ ' . implode(', ', $scopes))); + $this->line('OIDC Endpoint: '.($endpoint ? "✅ {$endpoint}" : '❌ Not set')); + $this->line('Client ID: '.($clientId ? "✅ {$clientId}" : '❌ Not set')); + $this->line('Client Secret: '.($clientSecret ? '✅ Set' : '❌ Not set')); + $this->line('Redirect URL: '.($redirect ? "✅ {$redirect}" : '❌ Not set')); + $this->line('Scopes: '.(empty($scopes) ? '❌ Not set' : '✅ '.implode(', ', $scopes))); $this->newLine(); @@ -53,38 +55,45 @@ class OidcTestCommand extends Command // Only test driver if we have basic configuration if ($endpoint && $clientId && $clientSecret) { $driver = Socialite::driver('oidc'); - $this->line("OIDC Driver: ✅ Successfully registered and accessible"); - + $this->line('OIDC Driver: ✅ Successfully registered and accessible'); + if ($enabled) { - $this->info("✅ OIDC is fully configured and ready to use!"); - $this->line("You can test the login flow at: /auth/oidc/redirect"); + $this->info('✅ OIDC is fully configured and ready to use!'); + $this->line('You can test the login flow at: /auth/oidc/redirect'); } else { - $this->warn("⚠️ OIDC driver is working but OIDC_ENABLED is false."); + $this->warn('⚠️ OIDC driver is working but OIDC_ENABLED is false.'); } } else { - $this->line("OIDC Driver: ✅ Registered (configuration test skipped due to missing values)"); - $this->warn("⚠️ OIDC driver is registered but missing required configuration."); - $this->line("Please set the following environment variables:"); - if (!$enabled) $this->line(" - OIDC_ENABLED=true"); - if (!$endpoint) { - $this->line(" - OIDC_ENDPOINT=https://your-oidc-provider.com (base URL)"); - $this->line(" OR"); - $this->line(" - OIDC_ENDPOINT=https://your-oidc-provider.com/.well-known/openid-configuration (full URL)"); + $this->line('OIDC Driver: ✅ Registered (configuration test skipped due to missing values)'); + $this->warn('⚠️ OIDC driver is registered but missing required configuration.'); + $this->line('Please set the following environment variables:'); + if (! $enabled) { + $this->line(' - OIDC_ENABLED=true'); + } + if (! $endpoint) { + $this->line(' - OIDC_ENDPOINT=https://your-oidc-provider.com (base URL)'); + $this->line(' OR'); + $this->line(' - OIDC_ENDPOINT=https://your-oidc-provider.com/.well-known/openid-configuration (full URL)'); + } + if (! $clientId) { + $this->line(' - OIDC_CLIENT_ID=your-client-id'); + } + if (! $clientSecret) { + $this->line(' - OIDC_CLIENT_SECRET=your-client-secret'); } - if (!$clientId) $this->line(" - OIDC_CLIENT_ID=your-client-id"); - if (!$clientSecret) $this->line(" - OIDC_CLIENT_SECRET=your-client-secret"); } - } catch (\InvalidArgumentException $e) { + } catch (InvalidArgumentException $e) { if (str_contains($e->getMessage(), 'Driver [oidc] not supported')) { - $this->error("❌ OIDC Driver registration failed: Driver not supported"); + $this->error('❌ OIDC Driver registration failed: Driver not supported'); } else { - $this->error("❌ OIDC Driver error: " . $e->getMessage()); + $this->error('❌ OIDC Driver error: '.$e->getMessage()); } - } catch (\Exception $e) { - $this->warn("⚠️ OIDC Driver registered but configuration error: " . $e->getMessage()); + } catch (Exception $e) { + $this->warn('⚠️ OIDC Driver registered but configuration error: '.$e->getMessage()); } $this->newLine(); + return Command::SUCCESS; } } diff --git a/app/Http/Controllers/Auth/OidcController.php b/app/Http/Controllers/Auth/OidcController.php index 305dd49..f7847d9 100644 --- a/app/Http/Controllers/Auth/OidcController.php +++ b/app/Http/Controllers/Auth/OidcController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Models\User; +use Exception; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; @@ -17,23 +18,25 @@ class OidcController extends Controller */ public function redirect() { - if (!config('services.oidc.enabled')) { + if (! config('services.oidc.enabled')) { return redirect()->route('login')->withErrors(['oidc' => 'OIDC authentication is not enabled.']); } // Check if all required OIDC configuration is present $requiredConfig = ['endpoint', 'client_id', 'client_secret']; foreach ($requiredConfig as $key) { - if (!config("services.oidc.{$key}")) { + if (! config("services.oidc.{$key}")) { Log::error("OIDC configuration missing: {$key}"); + return redirect()->route('login')->withErrors(['oidc' => 'OIDC is not properly configured.']); } } try { return Socialite::driver('oidc')->redirect(); - } catch (\Exception $e) { - Log::error('OIDC redirect error: ' . $e->getMessage()); + } catch (Exception $e) { + Log::error('OIDC redirect error: '.$e->getMessage()); + return redirect()->route('login')->withErrors(['oidc' => 'Failed to initiate OIDC authentication.']); } } @@ -43,32 +46,34 @@ class OidcController extends Controller */ public function callback(Request $request) { - if (!config('services.oidc.enabled')) { + if (! config('services.oidc.enabled')) { return redirect()->route('login')->withErrors(['oidc' => 'OIDC authentication is not enabled.']); } // Check if all required OIDC configuration is present $requiredConfig = ['endpoint', 'client_id', 'client_secret']; foreach ($requiredConfig as $key) { - if (!config("services.oidc.{$key}")) { + if (! config("services.oidc.{$key}")) { Log::error("OIDC configuration missing: {$key}"); + return redirect()->route('login')->withErrors(['oidc' => 'OIDC is not properly configured.']); } } try { $oidcUser = Socialite::driver('oidc')->user(); - + // Find or create the user $user = $this->findOrCreateUser($oidcUser); - + // Log the user in Auth::login($user, true); - + return redirect()->intended(route('dashboard', absolute: false)); - - } catch (\Exception $e) { - Log::error('OIDC callback error: ' . $e->getMessage()); + + } catch (Exception $e) { + Log::error('OIDC callback error: '.$e->getMessage()); + return redirect()->route('login')->withErrors(['oidc' => 'Failed to authenticate with OIDC provider.']); } } @@ -80,26 +85,28 @@ class OidcController extends Controller { // First, try to find user by OIDC subject ID $user = User::where('oidc_sub', $oidcUser->getId())->first(); - + if ($user) { // Update user information from OIDC $user->update([ 'name' => $oidcUser->getName() ?: $user->name, 'email' => $oidcUser->getEmail() ?: $user->email, ]); + return $user; } // If not found by OIDC sub, try to find by email if ($oidcUser->getEmail()) { $user = User::where('email', $oidcUser->getEmail())->first(); - + if ($user) { // Link the existing user with OIDC $user->update([ 'oidc_sub' => $oidcUser->getId(), 'name' => $oidcUser->getName() ?: $user->name, ]); + return $user; } } @@ -108,9 +115,9 @@ class OidcController extends Controller return User::create([ 'oidc_sub' => $oidcUser->getId(), 'name' => $oidcUser->getName() ?: 'OIDC User', - 'email' => $oidcUser->getEmail() ?: $oidcUser->getId() . '@oidc.local', + 'email' => $oidcUser->getEmail() ?: $oidcUser->getId().'@oidc.local', 'password' => bcrypt(Str::random(32)), // Random password since we're using OIDC 'email_verified_at' => now(), // OIDC users are considered verified ]); } -} \ No newline at end of file +} diff --git a/app/Liquid/FileSystems/InlineTemplatesFileSystem.php b/app/Liquid/FileSystems/InlineTemplatesFileSystem.php index 01adf1b..dbde888 100644 --- a/app/Liquid/FileSystems/InlineTemplatesFileSystem.php +++ b/app/Liquid/FileSystems/InlineTemplatesFileSystem.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Liquid\FileSystems; +use InvalidArgumentException; use Keepsuit\Liquid\Contracts\LiquidFileSystem; /** @@ -52,10 +53,10 @@ class InlineTemplatesFileSystem implements LiquidFileSystem public function readTemplateFile(string $templateName): string { - if (!isset($this->templates[$templateName])) { - throw new \InvalidArgumentException("Template '{$templateName}' not found in inline templates"); + if (! isset($this->templates[$templateName])) { + throw new InvalidArgumentException("Template '{$templateName}' not found in inline templates"); } return $this->templates[$templateName]; } -} \ No newline at end of file +} diff --git a/app/Liquid/Tags/TemplateTag.php b/app/Liquid/Tags/TemplateTag.php index 19f664e..279b67e 100644 --- a/app/Liquid/Tags/TemplateTag.php +++ b/app/Liquid/Tags/TemplateTag.php @@ -20,6 +20,7 @@ use Keepsuit\Liquid\TagBlock; class TemplateTag extends TagBlock { protected string $templateName; + protected Raw $body; public static function tagName(): string @@ -36,16 +37,16 @@ class TemplateTag extends TagBlock { // Get the template name from the tag parameters $templateNameExpression = $context->params->expression(); - + $this->templateName = match (true) { is_string($templateNameExpression) => trim($templateNameExpression), is_numeric($templateNameExpression) => (string) $templateNameExpression, $templateNameExpression instanceof VariableLookup => (string) $templateNameExpression, - default => throw new SyntaxException("Template name must be a string, number, or variable"), + default => throw new SyntaxException('Template name must be a string, number, or variable'), }; // Validate template name (letters, numbers, underscores, and slashes only) - if (!preg_match('/^[a-zA-Z0-9_\/]+$/', $this->templateName)) { + if (! preg_match('/^[a-zA-Z0-9_\/]+$/', $this->templateName)) { throw new SyntaxException("Invalid template name '{$this->templateName}' - template names must contain only letters, numbers, underscores, and slashes"); } @@ -74,7 +75,7 @@ class TemplateTag extends TagBlock // Get the file system from the environment $fileSystem = $context->environment->fileSystem; - if (!$fileSystem instanceof InlineTemplatesFileSystem) { + if (! $fileSystem instanceof InlineTemplatesFileSystem) { // If no inline file system is available, just return empty string // This allows the template to be used in contexts where inline templates aren't supported return ''; @@ -96,4 +97,4 @@ class TemplateTag extends TagBlock { return $this->body; } -} \ No newline at end of file +} diff --git a/app/Models/Device.php b/app/Models/Device.php index 420975a..5001a22 100644 --- a/app/Models/Device.php +++ b/app/Models/Device.php @@ -182,10 +182,12 @@ class Device extends Model { return $this->belongsTo(Firmware::class, 'update_firmware_id'); } + public function deviceModel(): BelongsTo { return $this->belongsTo(DeviceModel::class); } + public function logs(): HasMany { return $this->hasMany(DeviceLog::class); @@ -224,19 +226,20 @@ class Device extends Model if ($from < $to) { // Normal range, same day return $now->between($from, $to) ? (int) $now->diffInSeconds($to, false) : null; - } else { - // Overnight range - if ($now->gte($from)) { - // After 'from', before midnight - return (int) $now->diffInSeconds($to->copy()->addDay(), false); - } elseif ($now->lt($to)) { - // After midnight, before 'to' - return (int) $now->diffInSeconds($to, false); - } else { - // Not in sleep window - return null; - } } + // Overnight range + if ($now->gte($from)) { + // After 'from', before midnight + return (int) $now->diffInSeconds($to->copy()->addDay(), false); + } + if ($now->lt($to)) { + // After midnight, before 'to' + return (int) $now->diffInSeconds($to, false); + } + + // Not in sleep window + return null; + } public function isPauseActive(): bool diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 8433d76..b7deb3b 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -41,6 +41,7 @@ class AppServiceProvider extends ServiceProvider // Register OIDC provider with Socialite Socialite::extend('oidc', function ($app) { $config = $app['config']['services.oidc'] ?? []; + return new OidcProvider( $app['request'], $config['client_id'] ?? null, diff --git a/app/Services/ImageGenerationService.php b/app/Services/ImageGenerationService.php index d0ecddc..137b0af 100644 --- a/app/Services/ImageGenerationService.php +++ b/app/Services/ImageGenerationService.php @@ -51,7 +51,7 @@ class ImageGenerationService if (config('app.puppeteer_wait_for_network_idle')) { $browsershot->waitUntilNetworkIdle(); } - if (config('app.puppeteer_window_size_strategy') == 'v2') { + if (config('app.puppeteer_window_size_strategy') === 'v2') { $browsershot->windowSize($imageSettings['width'], $imageSettings['height']); } else { $browsershot->windowSize(800, 480); diff --git a/app/Services/OidcProvider.php b/app/Services/OidcProvider.php index ad9799d..2da8c84 100644 --- a/app/Services/OidcProvider.php +++ b/app/Services/OidcProvider.php @@ -2,11 +2,11 @@ namespace App\Services; +use Exception; +use GuzzleHttp\Client; use Laravel\Socialite\Two\AbstractProvider; use Laravel\Socialite\Two\ProviderInterface; use Laravel\Socialite\Two\User; -use GuzzleHttp\Client; -use Illuminate\Support\Arr; class OidcProvider extends AbstractProvider implements ProviderInterface { @@ -36,19 +36,19 @@ class OidcProvider extends AbstractProvider implements ProviderInterface public function __construct($request, $clientId, $clientSecret, $redirectUrl, $scopes = [], $guzzle = []) { parent::__construct($request, $clientId, $clientSecret, $redirectUrl, $guzzle); - + $endpoint = config('services.oidc.endpoint'); - if (!$endpoint) { - throw new \Exception('OIDC endpoint is not configured. Please set OIDC_ENDPOINT environment variable.'); + if (! $endpoint) { + throw new Exception('OIDC endpoint is not configured. Please set OIDC_ENDPOINT environment variable.'); } - + // Handle both full well-known URL and base URL if (str_ends_with($endpoint, '/.well-known/openid-configuration')) { $this->baseUrl = str_replace('/.well-known/openid-configuration', '', $endpoint); } else { $this->baseUrl = rtrim($endpoint, '/'); } - + $this->scopes = $scopes ?: ['openid', 'profile', 'email']; $this->loadOidcConfiguration(); } @@ -59,21 +59,21 @@ class OidcProvider extends AbstractProvider implements ProviderInterface protected function loadOidcConfiguration() { try { - $url = $this->baseUrl . '/.well-known/openid-configuration'; + $url = $this->baseUrl.'/.well-known/openid-configuration'; $client = new Client(); $response = $client->get($url); $this->oidcConfig = json_decode($response->getBody()->getContents(), true); - - if (!$this->oidcConfig) { - throw new \Exception('OIDC configuration is empty or invalid JSON'); + + if (! $this->oidcConfig) { + throw new Exception('OIDC configuration is empty or invalid JSON'); } - - if (!isset($this->oidcConfig['authorization_endpoint'])) { - throw new \Exception('authorization_endpoint not found in OIDC configuration'); + + if (! isset($this->oidcConfig['authorization_endpoint'])) { + throw new Exception('authorization_endpoint not found in OIDC configuration'); } - - } catch (\Exception $e) { - throw new \Exception('Failed to load OIDC configuration: ' . $e->getMessage()); + + } catch (Exception $e) { + throw new Exception('Failed to load OIDC configuration: '.$e->getMessage()); } } @@ -82,9 +82,10 @@ class OidcProvider extends AbstractProvider implements ProviderInterface */ protected function getAuthUrl($state) { - if (!$this->oidcConfig || !isset($this->oidcConfig['authorization_endpoint'])) { - throw new \Exception('OIDC configuration not loaded or authorization_endpoint not found.'); + if (! $this->oidcConfig || ! isset($this->oidcConfig['authorization_endpoint'])) { + throw new Exception('OIDC configuration not loaded or authorization_endpoint not found.'); } + return $this->buildAuthUrlFromBase($this->oidcConfig['authorization_endpoint'], $state); } @@ -93,9 +94,10 @@ class OidcProvider extends AbstractProvider implements ProviderInterface */ protected function getTokenUrl() { - if (!$this->oidcConfig || !isset($this->oidcConfig['token_endpoint'])) { - throw new \Exception('OIDC configuration not loaded or token_endpoint not found.'); + if (! $this->oidcConfig || ! isset($this->oidcConfig['token_endpoint'])) { + throw new Exception('OIDC configuration not loaded or token_endpoint not found.'); } + return $this->oidcConfig['token_endpoint']; } @@ -104,13 +106,13 @@ class OidcProvider extends AbstractProvider implements ProviderInterface */ protected function getUserByToken($token) { - if (!$this->oidcConfig || !isset($this->oidcConfig['userinfo_endpoint'])) { - throw new \Exception('OIDC configuration not loaded or userinfo_endpoint not found.'); + if (! $this->oidcConfig || ! isset($this->oidcConfig['userinfo_endpoint'])) { + throw new Exception('OIDC configuration not loaded or userinfo_endpoint not found.'); } - + $response = $this->getHttpClient()->get($this->oidcConfig['userinfo_endpoint'], [ 'headers' => [ - 'Authorization' => 'Bearer ' . $token, + 'Authorization' => 'Bearer '.$token, ], ]); @@ -153,4 +155,4 @@ class OidcProvider extends AbstractProvider implements ProviderInterface 'grant_type' => 'authorization_code', ]); } -} \ No newline at end of file +} diff --git a/config/services.php b/config/services.php index 7fe0344..5cb8a74 100644 --- a/config/services.php +++ b/config/services.php @@ -58,7 +58,7 @@ return [ 'endpoint' => env('OIDC_ENDPOINT'), 'client_id' => env('OIDC_CLIENT_ID'), 'client_secret' => env('OIDC_CLIENT_SECRET'), - 'redirect' => env('APP_URL', 'http://localhost:8000') . '/auth/oidc/callback', + 'redirect' => env('APP_URL', 'http://localhost:8000').'/auth/oidc/callback', 'scopes' => explode(',', env('OIDC_SCOPES', 'openid,profile,email')), ], diff --git a/tests/Feature/Devices/DeviceRotationTest.php b/tests/Feature/Devices/DeviceRotationTest.php index 2f2374f..e6fb7e0 100644 --- a/tests/Feature/Devices/DeviceRotationTest.php +++ b/tests/Feature/Devices/DeviceRotationTest.php @@ -2,7 +2,8 @@ declare(strict_types=1); -use App\Models\{Device, User}; +use App\Models\Device; +use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; uses(RefreshDatabase::class); @@ -16,8 +17,8 @@ test('dashboard shows device image with correct rotation', function () { ]); // Mock the file existence check - \Illuminate\Support\Facades\Storage::fake('public'); - \Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content'); + Illuminate\Support\Facades\Storage::fake('public'); + Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content'); $response = $this->actingAs($user) ->get(route('dashboard')); @@ -36,8 +37,8 @@ test('device configure page shows device image with correct rotation', function ]); // Mock the file existence check - \Illuminate\Support\Facades\Storage::fake('public'); - \Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content'); + Illuminate\Support\Facades\Storage::fake('public'); + Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content'); $response = $this->actingAs($user) ->get(route('devices.configure', $device)); @@ -56,8 +57,8 @@ test('device with no rotation shows no transform style', function () { ]); // Mock the file existence check - \Illuminate\Support\Facades\Storage::fake('public'); - \Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content'); + Illuminate\Support\Facades\Storage::fake('public'); + Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content'); $response = $this->actingAs($user) ->get(route('dashboard')); @@ -75,8 +76,8 @@ test('device with null rotation defaults to 0', function () { ]); // Mock the file existence check - \Illuminate\Support\Facades\Storage::fake('public'); - \Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content'); + Illuminate\Support\Facades\Storage::fake('public'); + Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content'); $response = $this->actingAs($user) ->get(route('dashboard')); diff --git a/tests/Feature/PluginArchiveTest.php b/tests/Feature/PluginArchiveTest.php index 34014ec..bfd987c 100644 --- a/tests/Feature/PluginArchiveTest.php +++ b/tests/Feature/PluginArchiveTest.php @@ -266,7 +266,7 @@ it('maintains correct yaml field order', function () { // Extract and read the settings.yml file $zip->extractTo(sys_get_temp_dir(), 'settings.yml'); - $yamlContent = file_get_contents(sys_get_temp_dir() . '/settings.yml'); + $yamlContent = file_get_contents(sys_get_temp_dir().'/settings.yml'); $zip->close(); // Check that the YAML content has the expected field order @@ -285,11 +285,11 @@ it('maintains correct yaml field order', function () { $lines = explode("\n", $yamlContent); $fieldLines = []; - + foreach ($lines as $line) { $line = trim($line); if (preg_match('/^([a-zA-Z_]+):/', $line, $matches)) { - $fieldLines[] = $matches[1] . ':'; + $fieldLines[] = $matches[1].':'; } } @@ -304,5 +304,5 @@ it('maintains correct yaml field order', function () { } // Clean up - unlink(sys_get_temp_dir() . '/settings.yml'); + unlink(sys_get_temp_dir().'/settings.yml'); }); diff --git a/tests/Feature/PluginDefaultValuesTest.php b/tests/Feature/PluginDefaultValuesTest.php index 339aeee..30f62c0 100644 --- a/tests/Feature/PluginDefaultValuesTest.php +++ b/tests/Feature/PluginDefaultValuesTest.php @@ -3,10 +3,6 @@ use App\Models\Plugin; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Http\UploadedFile; -use Illuminate\Support\Facades\Storage; -use Livewire\Volt\Volt; -use Symfony\Component\Yaml\Yaml; uses(RefreshDatabase::class); @@ -22,23 +18,23 @@ test('plugin import extracts default values from custom_fields and stores in con 'field_type' => 'string', 'name' => 'Reading Days', 'description' => 'Select days of the week to read', - 'default' => 'Monday,Friday,Saturday,Sunday' + 'default' => 'Monday,Friday,Saturday,Sunday', ], [ 'keyname' => 'refresh_interval', 'field_type' => 'number', 'name' => 'Refresh Interval', 'description' => 'How often to refresh data', - 'default' => 15 + 'default' => 15, ], [ 'keyname' => 'timezone', 'field_type' => 'time_zone', 'name' => 'Timezone', - 'description' => 'Select your timezone' + 'description' => 'Select your timezone', // No default value - ] - ] + ], + ], ]; // Extract default values from custom_fields and populate configuration @@ -53,7 +49,7 @@ test('plugin import extracts default values from custom_fields and stores in con // Create the plugin directly $plugin = Plugin::create([ - 'uuid' => \Illuminate\Support\Str::uuid(), + 'uuid' => Illuminate\Support\Str::uuid(), 'user_id' => $user->id, 'name' => 'Test Plugin with Defaults', 'data_stale_minutes' => 30, @@ -68,11 +64,11 @@ test('plugin import extracts default values from custom_fields and stores in con expect($plugin->configuration)->toHaveKey('reading_days'); expect($plugin->configuration)->toHaveKey('refresh_interval'); expect($plugin->configuration)->not->toHaveKey('timezone'); - + expect($plugin->getConfiguration('reading_days'))->toBe('Monday,Friday,Saturday,Sunday'); expect($plugin->getConfiguration('refresh_interval'))->toBe(15); expect($plugin->getConfiguration('timezone'))->toBeNull(); - + // Verify configuration template was stored correctly expect($plugin->configuration_template)->toBeArray(); expect($plugin->configuration_template['custom_fields'])->toHaveCount(3); @@ -89,9 +85,9 @@ test('plugin import handles custom_fields without default values', function () { 'keyname' => 'timezone', 'field_type' => 'time_zone', 'name' => 'Timezone', - 'description' => 'Select your timezone' - ] - ] + 'description' => 'Select your timezone', + ], + ], ]; // Extract default values from custom_fields and populate configuration @@ -106,7 +102,7 @@ test('plugin import handles custom_fields without default values', function () { // Create the plugin directly $plugin = Plugin::create([ - 'uuid' => \Illuminate\Support\Str::uuid(), + 'uuid' => Illuminate\Support\Str::uuid(), 'user_id' => $user->id, 'name' => 'Test Plugin No Defaults', 'data_stale_minutes' => 30, @@ -119,8 +115,8 @@ test('plugin import handles custom_fields without default values', function () { expect($plugin)->not->toBeNull(); expect($plugin->configuration)->toBeArray(); expect($plugin->configuration)->toBeEmpty(); - + // Verify configuration template was stored correctly expect($plugin->configuration_template)->toBeArray(); expect($plugin->configuration_template['custom_fields'])->toHaveCount(1); -}); \ No newline at end of file +});