- Post-login (OAuth + dev-login) now redirects to the next upcoming service's edit page instead of /dashboard, mirroring the GET / route. - NameTagResolver now reads the real ChurchTools `responsible` shape (persons[].person.title) and resolves moderator/preacher/worship-leader by responsible ROLE ([Moderation]/[Predigt]/[Lobpreis]). This fixes missing name slides and makes the worship-leader arrangement trigger (e.g. service 12 → "Benedikt Hardt" / "Jennifer Schneider"). - NameTagSlideBuilder no longer silently drops the name slide when the configured macro id points to a missing macro; it emits the slide without a macro instead. - Song export: the "first slide" / "last slide" macro now applies only to the song's very first/last slide (global slide index across all sections), not the first slide of every section. - Export "headlines" for content-less agenda items are now emitted as proper ProPresenter playlist HEADER items instead of text presentations. - Prefix/postfix export files now also accept .probundle (unzipped: inner .pro + media embedded) in addition to .pro, both for upload validation and export injection. Full suite green (587 passed).
173 lines
5.3 KiB
PHP
173 lines
5.3 KiB
PHP
<?php
|
|
|
|
use App\Models\Service;
|
|
use App\Models\User;
|
|
use Laravel\Socialite\Facades\Socialite;
|
|
use Laravel\Socialite\Two\User as SocialiteUser;
|
|
|
|
it('redirects unauthenticated users to login', function () {
|
|
$response = $this->get('/');
|
|
|
|
$response->assertRedirect('/login');
|
|
});
|
|
|
|
it('shows login page with OAuth button', function () {
|
|
$response = $this->get('/login');
|
|
|
|
$response->assertStatus(200);
|
|
$response->assertInertia(
|
|
fn ($page) => $page
|
|
->component('Auth/Login')
|
|
);
|
|
});
|
|
|
|
it('login page has no email or password inputs', function () {
|
|
$response = $this->get('/login');
|
|
|
|
$response->assertStatus(200);
|
|
// The Login.vue should NOT contain email/password form fields
|
|
// This is verified by checking the component renders correctly
|
|
$response->assertInertia(
|
|
fn ($page) => $page
|
|
->component('Auth/Login')
|
|
);
|
|
});
|
|
|
|
it('redirects to ChurchTools OAuth on auth initiation', function () {
|
|
$providerMock = Mockery::mock(\Laravel\Socialite\Two\AbstractProvider::class);
|
|
$providerMock->shouldReceive('redirect')
|
|
->once()
|
|
->andReturn(redirect('https://churchtools.example.com/oauth/authorize'));
|
|
|
|
Socialite::shouldReceive('driver')
|
|
->with('churchtools')
|
|
->once()
|
|
->andReturn($providerMock);
|
|
|
|
$response = $this->get('/auth/churchtools');
|
|
|
|
$response->assertRedirect();
|
|
$response->assertRedirectContains('churchtools.example.com/oauth/authorize');
|
|
});
|
|
|
|
it('creates a new user from OAuth callback', function () {
|
|
$service = Service::factory()->create(['date' => now()->addDays(7)]);
|
|
|
|
$socialiteUser = new SocialiteUser();
|
|
$socialiteUser->map([
|
|
'id' => '42',
|
|
'name' => 'Max Mustermann',
|
|
'email' => 'max@example.com',
|
|
'avatar' => 'https://churchtools.example.com/avatar/42.jpg',
|
|
]);
|
|
$socialiteUser->user = [
|
|
'id' => 42,
|
|
'firstName' => 'Max',
|
|
'lastName' => 'Mustermann',
|
|
'displayName' => 'Max Mustermann',
|
|
'email' => 'max@example.com',
|
|
'imageUrl' => 'https://churchtools.example.com/avatar/42.jpg',
|
|
'groups' => [['id' => 1, 'name' => 'Worship']],
|
|
'roles' => [['id' => 2, 'name' => 'Admin']],
|
|
];
|
|
|
|
$providerMock = Mockery::mock(\Laravel\Socialite\Two\AbstractProvider::class);
|
|
$providerMock->shouldReceive('user')
|
|
->once()
|
|
->andReturn($socialiteUser);
|
|
|
|
Socialite::shouldReceive('driver')
|
|
->with('churchtools')
|
|
->once()
|
|
->andReturn($providerMock);
|
|
|
|
$response = $this->get('/auth/churchtools/callback');
|
|
|
|
$response->assertRedirect(route('services.edit', $service->id));
|
|
|
|
$this->assertDatabaseHas('users', [
|
|
'email' => 'max@example.com',
|
|
'name' => 'Max Mustermann',
|
|
'churchtools_id' => 42,
|
|
'avatar' => 'https://churchtools.example.com/avatar/42.jpg',
|
|
]);
|
|
|
|
$user = User::where('email', 'max@example.com')->first();
|
|
expect($user)->not->toBeNull();
|
|
expect((int) $user->churchtools_id)->toBe(42);
|
|
expect($user->churchtools_groups)->toBe([['id' => 1, 'name' => 'Worship']]);
|
|
expect($user->churchtools_roles)->toBe([['id' => 2, 'name' => 'Admin']]);
|
|
$this->assertAuthenticatedAs($user);
|
|
});
|
|
|
|
it('updates existing user on OAuth callback', function () {
|
|
$service = Service::factory()->create(['date' => now()->addDays(7)]);
|
|
|
|
$existingUser = User::factory()->create([
|
|
'email' => 'max@example.com',
|
|
'name' => 'Old Name',
|
|
'churchtools_id' => 42,
|
|
]);
|
|
|
|
$socialiteUser = new SocialiteUser();
|
|
$socialiteUser->map([
|
|
'id' => '42',
|
|
'name' => 'Max Mustermann',
|
|
'email' => 'max@example.com',
|
|
'avatar' => 'https://churchtools.example.com/avatar/42.jpg',
|
|
]);
|
|
$socialiteUser->user = [
|
|
'id' => 42,
|
|
'firstName' => 'Max',
|
|
'lastName' => 'Mustermann',
|
|
'displayName' => 'Max Mustermann',
|
|
'email' => 'max@example.com',
|
|
'imageUrl' => 'https://churchtools.example.com/avatar/42.jpg',
|
|
'groups' => [['id' => 1, 'name' => 'Worship']],
|
|
'roles' => [['id' => 2, 'name' => 'Admin']],
|
|
];
|
|
|
|
$providerMock = Mockery::mock(\Laravel\Socialite\Two\AbstractProvider::class);
|
|
$providerMock->shouldReceive('user')
|
|
->once()
|
|
->andReturn($socialiteUser);
|
|
|
|
Socialite::shouldReceive('driver')
|
|
->with('churchtools')
|
|
->once()
|
|
->andReturn($providerMock);
|
|
|
|
$response = $this->get('/auth/churchtools/callback');
|
|
|
|
$response->assertRedirect(route('services.edit', $service->id));
|
|
|
|
$existingUser->refresh();
|
|
expect($existingUser->name)->toBe('Max Mustermann');
|
|
expect($existingUser->avatar)->toBe('https://churchtools.example.com/avatar/42.jpg');
|
|
expect(User::count())->toBe(1);
|
|
$this->assertAuthenticatedAs($existingUser);
|
|
});
|
|
|
|
it('logs out user and redirects to login', function () {
|
|
$user = User::factory()->create();
|
|
|
|
$response = $this->actingAs($user)->post('/logout');
|
|
|
|
$response->assertRedirect('/login');
|
|
$this->assertGuest();
|
|
});
|
|
|
|
it('does not have register routes', function () {
|
|
$response = $this->get('/register');
|
|
|
|
$response->assertStatus(404);
|
|
});
|
|
|
|
it('authenticated user can access dashboard', function () {
|
|
$user = User::factory()->create();
|
|
|
|
$response = $this->actingAs($user)->get('/dashboard');
|
|
|
|
$response->assertStatus(200);
|
|
});
|