InstanceSettings::updateOrCreate(['id' => 0], ['id' => 0])); $this->team = Team::factory()->create(); $this->admin = User::factory()->create(); $this->admin->teams()->attach($this->team, ['role' => 'admin']); $this->member = User::factory()->create(); $this->member->teams()->attach($this->team, ['role' => 'member']); }); // --- Discord --- test('member cannot send test notification on discord', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); $settings = $this->team->discordNotificationSettings; $settings->update([ 'discord_enabled' => true, 'discord_webhook_url' => 'https://discord.com/api/webhooks/test', ]); Livewire::test(DiscordNotification::class) ->call('sendTestNotification') ->assertDispatched('error'); }); test('member cannot update discord notification settings', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); Livewire::test(DiscordNotification::class) ->call('syncData', true) ->assertForbidden(); }); test('admin can update discord notification settings', function () { $this->actingAs($this->admin); session(['currentTeam' => $this->team]); $settings = $this->team->discordNotificationSettings; expect($this->admin->can('update', $settings))->toBeTrue(); }); // --- Slack --- test('member cannot send test notification on slack', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); $settings = $this->team->slackNotificationSettings; $settings->update([ 'slack_enabled' => true, 'slack_webhook_url' => 'https://hooks.slack.com/services/test', ]); Livewire::test(SlackNotification::class) ->call('sendTestNotification') ->assertDispatched('error'); }); test('member cannot update slack notification settings', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); Livewire::test(SlackNotification::class) ->call('syncData', true) ->assertForbidden(); }); test('admin can update slack notification settings', function () { $this->actingAs($this->admin); session(['currentTeam' => $this->team]); $settings = $this->team->slackNotificationSettings; expect($this->admin->can('update', $settings))->toBeTrue(); }); // --- Telegram --- test('member cannot send test notification on telegram', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); $settings = $this->team->telegramNotificationSettings; $settings->update([ 'telegram_enabled' => true, 'telegram_token' => 'test-token', 'telegram_chat_id' => '123456', ]); Livewire::test(TelegramNotification::class) ->call('sendTestNotification') ->assertDispatched('error'); }); test('member cannot update telegram notification settings', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); Livewire::test(TelegramNotification::class) ->call('syncData', true) ->assertForbidden(); }); test('admin can update telegram notification settings', function () { $this->actingAs($this->admin); session(['currentTeam' => $this->team]); $settings = $this->team->telegramNotificationSettings; expect($this->admin->can('update', $settings))->toBeTrue(); }); // --- Email --- test('member cannot send test email notification', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); $settings = $this->team->emailNotificationSettings; $settings->update([ 'smtp_enabled' => true, 'smtp_host' => 'localhost', 'smtp_port' => 587, 'smtp_from_address' => 'test@test.com', 'smtp_from_name' => 'Test', ]); Livewire::test(EmailNotification::class) ->call('sendTestEmail') ->assertDispatched('error'); }); test('member cannot update email notification settings', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); Livewire::test(EmailNotification::class) ->call('syncData', true) ->assertForbidden(); }); test('member cannot update smtp email transport directly', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); Livewire::test(EmailNotification::class) ->set('smtpFromAddress', 'member@example.com') ->set('smtpFromName', 'Member') ->set('smtpHost', 'smtp.example.com') ->set('smtpPort', '587') ->set('smtpEncryption', 'starttls') ->set('smtpPassword', 'member-smtp-password') ->call('submitSmtp') ->assertForbidden(); }); test('member cannot update resend email transport directly', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); Livewire::test(EmailNotification::class) ->set('smtpFromAddress', 'member@example.com') ->set('smtpFromName', 'Member') ->set('resendApiKey', 'member-resend-api-key') ->call('submitResend') ->assertForbidden(); }); test('member cannot copy instance email settings', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); Livewire::test(EmailNotification::class) ->call('copyFromInstanceSettings') ->assertForbidden(); }); test('admin can update email notification settings', function () { $this->actingAs($this->admin); session(['currentTeam' => $this->team]); $settings = $this->team->emailNotificationSettings; expect($this->admin->can('update', $settings))->toBeTrue(); }); // --- Pushover --- test('member cannot send test notification on pushover', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); $settings = $this->team->pushoverNotificationSettings; $settings->update([ 'pushover_enabled' => true, 'pushover_app_token' => 'test-token', 'pushover_user_key' => 'test-user-key', ]); Livewire::test(PushoverNotification::class) ->call('sendTestNotification') ->assertDispatched('error'); }); test('member cannot update pushover notification settings', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); Livewire::test(PushoverNotification::class) ->call('syncData', true) ->assertForbidden(); }); test('admin can update pushover notification settings', function () { $this->actingAs($this->admin); session(['currentTeam' => $this->team]); $settings = $this->team->pushoverNotificationSettings; expect($this->admin->can('update', $settings))->toBeTrue(); }); // --- Webhook --- test('member cannot send test notification on webhook', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); $settings = $this->team->webhookNotificationSettings; $settings->update([ 'webhook_enabled' => true, 'webhook_url' => 'https://example.com/webhook', ]); Livewire::test(WebhookNotification::class) ->call('sendTestNotification') ->assertDispatched('error'); }); test('member cannot update webhook notification settings', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); Livewire::test(WebhookNotification::class) ->call('syncData', true) ->assertForbidden(); }); test('admin can update webhook notification settings', function () { $this->actingAs($this->admin); session(['currentTeam' => $this->team]); $settings = $this->team->webhookNotificationSettings; expect($this->admin->can('update', $settings))->toBeTrue(); }); // --- Send test policy checks --- test('admin can send test on all notification channels', function () { $this->actingAs($this->admin); session(['currentTeam' => $this->team]); expect($this->admin->can('sendTest', $this->team->discordNotificationSettings))->toBeTrue(); expect($this->admin->can('sendTest', $this->team->slackNotificationSettings))->toBeTrue(); expect($this->admin->can('sendTest', $this->team->telegramNotificationSettings))->toBeTrue(); expect($this->admin->can('sendTest', $this->team->emailNotificationSettings))->toBeTrue(); expect($this->admin->can('sendTest', $this->team->pushoverNotificationSettings))->toBeTrue(); expect($this->admin->can('sendTest', $this->team->webhookNotificationSettings))->toBeTrue(); }); test('member cannot send test on any notification channel', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); expect($this->member->can('sendTest', $this->team->discordNotificationSettings))->toBeFalse(); expect($this->member->can('sendTest', $this->team->slackNotificationSettings))->toBeFalse(); expect($this->member->can('sendTest', $this->team->telegramNotificationSettings))->toBeFalse(); expect($this->member->can('sendTest', $this->team->emailNotificationSettings))->toBeFalse(); expect($this->member->can('sendTest', $this->team->pushoverNotificationSettings))->toBeFalse(); expect($this->member->can('sendTest', $this->team->webhookNotificationSettings))->toBeFalse(); }); test('member cannot view notification secrets', function (string $component, string $settingsRelation, array $secrets) { $settings = $this->team->{$settingsRelation}; $settings->update($secrets); $this->actingAs($this->member); session(['currentTeam' => $this->team]); $componentTest = Livewire::test($component); foreach ($secrets as $column => $value) { $property = str($column)->camel()->toString(); $componentTest ->assertSet($property, null) ->assertDontSee($value); } $componentTest->assertSee('Hidden (only admins can view)'); })->with([ 'discord webhook' => [DiscordNotification::class, 'discordNotificationSettings', [ 'discord_webhook_url' => 'https://discord.com/api/webhooks/secret-member', ]], 'slack webhook' => [SlackNotification::class, 'slackNotificationSettings', [ 'slack_webhook_url' => 'https://hooks.slack.com/services/secret-member', ]], 'telegram token and chat id' => [TelegramNotification::class, 'telegramNotificationSettings', [ 'telegram_token' => 'telegram-secret-token', 'telegram_chat_id' => 'telegram-secret-chat', ]], 'pushover credentials' => [PushoverNotification::class, 'pushoverNotificationSettings', [ 'pushover_user_key' => 'pushover-secret-user', 'pushover_api_token' => 'pushover-secret-token', ]], 'generic webhook' => [WebhookNotification::class, 'webhookNotificationSettings', [ 'webhook_url' => 'https://example.com/secret-webhook', ]], 'email credentials' => [EmailNotification::class, 'emailNotificationSettings', [ 'smtp_password' => 'smtp-secret-password', 'resend_api_key' => 'resend-secret-api-key', ]], ]); test('admin can view notification secrets', function (string $component, string $settingsRelation, array $secrets) { $settings = $this->team->{$settingsRelation}; $settings->update($secrets); $this->actingAs($this->admin); session(['currentTeam' => $this->team]); $componentTest = Livewire::test($component); foreach ($secrets as $column => $value) { $property = str($column)->camel()->toString(); $componentTest->assertSet($property, $value); } })->with([ 'discord webhook' => [DiscordNotification::class, 'discordNotificationSettings', [ 'discord_webhook_url' => 'https://discord.com/api/webhooks/secret-admin', ]], 'slack webhook' => [SlackNotification::class, 'slackNotificationSettings', [ 'slack_webhook_url' => 'https://hooks.slack.com/services/secret-admin', ]], 'telegram token and chat id' => [TelegramNotification::class, 'telegramNotificationSettings', [ 'telegram_token' => 'telegram-admin-token', 'telegram_chat_id' => 'telegram-admin-chat', ]], 'pushover credentials' => [PushoverNotification::class, 'pushoverNotificationSettings', [ 'pushover_user_key' => 'pushover-admin-user', 'pushover_api_token' => 'pushover-admin-token', ]], 'generic webhook' => [WebhookNotification::class, 'webhookNotificationSettings', [ 'webhook_url' => 'https://example.com/admin-webhook', ]], 'email credentials' => [EmailNotification::class, 'emailNotificationSettings', [ 'smtp_password' => 'smtp-admin-password', 'resend_api_key' => 'resend-admin-api-key', ]], ]);