29 files changed
+876
-89
No files matched your search
@@ -3,6 +3,8 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\ApplicationInstallation;
|
||||
use App\Models\Business;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
@@ -10,25 +12,41 @@ class InstallationTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_a_clean_database_can_be_installed_once(): void
|
||||
public function test_a_clean_database_creates_only_a_platform_super_admin(): void
|
||||
{
|
||||
$this->get('/install')->assertOk()->assertSee('Create your business');
|
||||
$this->get('/install')->assertOk()->assertSee('Create the platform administrator');
|
||||
|
||||
$response = $this->post('/install', [
|
||||
'businessName' => 'Installed Shop',
|
||||
'slug' => 'installed-shop',
|
||||
'domain' => 'localhost',
|
||||
'adminName' => 'First Owner',
|
||||
'adminEmail' => 'first-owner@example.test',
|
||||
'adminName' => 'Platform Admin',
|
||||
'adminEmail' => 'admin@example.test',
|
||||
'password' => 'password123',
|
||||
'password_confirmation' => 'password123',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('install'));
|
||||
$this->assertDatabaseHas('ApplicationInstallation', ['id' => 'singleton', 'status' => 'INSTALLED']);
|
||||
$this->assertDatabaseHas('Business', ['slug' => 'installed-shop']);
|
||||
$this->assertDatabaseHas('User', ['email' => 'first-owner@example.test']);
|
||||
$this->get('/install')->assertRedirect(route('public.home'));
|
||||
$response->assertRedirect(route('manager.dashboard'));
|
||||
$this->assertDatabaseHas('ApplicationInstallation', [
|
||||
'id' => 'singleton',
|
||||
'status' => 'INSTALLED',
|
||||
'primaryBusinessId' => null,
|
||||
]);
|
||||
$this->assertDatabaseHas('User', ['email' => 'admin@example.test', 'isSuperAdmin' => 1]);
|
||||
$this->assertSame(0, Business::count());
|
||||
$this->get('/install')->assertRedirect(route('manager.dashboard'));
|
||||
$this->assertSame(1, ApplicationInstallation::count());
|
||||
}
|
||||
|
||||
public function test_installation_cannot_be_repeated(): void
|
||||
{
|
||||
$admin = User::factory()->create(['isSuperAdmin' => true]);
|
||||
ApplicationInstallation::create(['id' => 'singleton', 'status' => 'INSTALLED', 'installedAt' => now(), 'initialAdminUserId' => $admin->id]);
|
||||
|
||||
$this->post('/install', [
|
||||
'adminName' => 'Another Admin',
|
||||
'adminEmail' => 'another@example.test',
|
||||
'password' => 'password123',
|
||||
'password_confirmation' => 'password123',
|
||||
])->assertRedirect(route('manager.login'));
|
||||
|
||||
$this->assertDatabaseMissing('User', ['email' => 'another@example.test']);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use App\Models\Business;
|
||||
use App\Models\BusinessDomain;
|
||||
use App\Models\CustomerProfile;
|
||||
use App\Models\User;
|
||||
use App\Services\Businesses\BusinessProvisioningService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
@@ -13,63 +14,98 @@ class TenantAndAuthTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_the_host_selects_only_its_business(): void
|
||||
public function test_manager_is_restricted_to_platform_super_admins(): void
|
||||
{
|
||||
$this->seed();
|
||||
$this->get('/manager')->assertRedirect(route('manager.login'));
|
||||
|
||||
$other = Business::create([
|
||||
'slug' => 'other-barbershop',
|
||||
'name' => 'Other Barbershop',
|
||||
$member = User::factory()->create(['isSuperAdmin' => false]);
|
||||
$this->actingAs($member)->get('/manager')->assertForbidden();
|
||||
|
||||
$admin = User::factory()->create(['isSuperAdmin' => true]);
|
||||
$this->actingAs($admin)->get('/manager')->assertOk()->assertSee('Businesses');
|
||||
}
|
||||
|
||||
public function test_manager_provisions_an_isolated_business_and_owner_invitation(): void
|
||||
{
|
||||
$admin = User::factory()->create(['isSuperAdmin' => true]);
|
||||
|
||||
$response = $this->actingAs($admin)->post('/manager/businesses', [
|
||||
'name' => 'New Chair Studio',
|
||||
'shortName' => 'New Chair',
|
||||
'slug' => 'new-chair-studio',
|
||||
'domain' => 'new-chair.test',
|
||||
'email' => 'hello@new-chair.test',
|
||||
'timezone' => 'Europe/London',
|
||||
'currency' => 'GBP',
|
||||
'locale' => 'en-GB',
|
||||
]);
|
||||
BusinessDomain::create([
|
||||
'businessId' => $other->id,
|
||||
'hostname' => 'other.test',
|
||||
'isPrimary' => true,
|
||||
'isActive' => true,
|
||||
'ownerName' => 'Owner Person',
|
||||
'ownerEmail' => 'owner@new-chair.test',
|
||||
]);
|
||||
|
||||
$this->get('http://localhost/')
|
||||
->assertOk()
|
||||
->assertSee('Blake Heritage Barbershop')
|
||||
->assertSee('Classic Haircut')
|
||||
->assertDontSeeText('Other Barbershop');
|
||||
$business = Business::where('slug', 'new-chair-studio')->firstOrFail();
|
||||
$response->assertRedirect(route('manager.businesses.show', $business));
|
||||
$this->assertDatabaseHas('BusinessSettings', ['businessId' => $business->id]);
|
||||
$this->assertDatabaseHas('BookingRule', ['businessId' => $business->id]);
|
||||
$this->assertSame(7, $business->hours()->count());
|
||||
$this->assertDatabaseHas('Role', ['businessId' => $business->id, 'name' => 'Owner']);
|
||||
$this->assertDatabaseHas('StaffInvitation', ['businessId' => $business->id, 'email' => 'owner@new-chair.test']);
|
||||
$this->assertDatabaseMissing('User', ['email' => 'owner@new-chair.test', 'isSuperAdmin' => 1]);
|
||||
}
|
||||
|
||||
$this->get('http://other.test/')
|
||||
public function test_owner_invitation_is_one_time_and_redirects_to_the_business_domain(): void
|
||||
{
|
||||
$result = $this->app->make(BusinessProvisioningService::class)->provision([
|
||||
'name' => 'Invite Shop', 'slug' => 'invite-shop', 'domain' => 'invite.test',
|
||||
], ['name' => 'Invited Owner', 'email' => 'invited-owner@example.test']);
|
||||
|
||||
$this->get('http://invite.test/invite/'.$result->ownerInvitation->token)
|
||||
->assertOk()
|
||||
->assertSee('Other Barbershop')
|
||||
->assertDontSeeText('Classic Haircut');
|
||||
->assertSee('Finish your owner setup');
|
||||
|
||||
$this->post('http://invite.test/invite/'.$result->ownerInvitation->token, [
|
||||
'password' => 'password123',
|
||||
'password_confirmation' => 'password123',
|
||||
])->assertRedirect('http://invite.test/owner');
|
||||
|
||||
$this->assertNotNull($result->ownerInvitation->fresh()->acceptedAt);
|
||||
$this->assertAuthenticatedAs(User::where('email', 'invited-owner@example.test')->firstOrFail());
|
||||
$this->get('http://invite.test/invite/'.$result->ownerInvitation->token)->assertStatus(410);
|
||||
}
|
||||
|
||||
public function test_the_host_selects_only_its_business(): void
|
||||
{
|
||||
$provisioner = $this->app->make(BusinessProvisioningService::class);
|
||||
$first = $provisioner->provision([
|
||||
'name' => 'First Barbershop', 'slug' => 'first-barbershop', 'domain' => 'first.test',
|
||||
], ['name' => 'First Owner', 'email' => 'first-owner@example.test']);
|
||||
$second = $provisioner->provision([
|
||||
'name' => 'Second Barbershop', 'slug' => 'second-barbershop', 'domain' => 'second.test',
|
||||
], ['name' => 'Second Owner', 'email' => 'second-owner@example.test']);
|
||||
|
||||
$this->get('http://first.test/')->assertOk()->assertSee('First Barbershop')->assertDontSee('Second Barbershop');
|
||||
$this->get('http://second.test/')->assertOk()->assertSee('Second Barbershop')->assertDontSee('First Barbershop');
|
||||
$this->assertNotSame($first->business->id, $second->business->id);
|
||||
}
|
||||
|
||||
public function test_registration_creates_a_customer_profile_in_the_current_tenant(): void
|
||||
{
|
||||
$this->seed();
|
||||
$business = $this->app->make(BusinessProvisioningService::class)->provision([
|
||||
'name' => 'Customer Shop', 'slug' => 'customer-shop', 'domain' => 'customer.test',
|
||||
], ['name' => 'Shop Owner', 'email' => 'shop-owner@example.test'])->business;
|
||||
|
||||
$response = $this->withHeaders(['Host' => 'localhost'])
|
||||
->post('/register', [
|
||||
'firstName' => 'Taylor',
|
||||
'lastName' => 'Member',
|
||||
'email' => 'taylor@example.test',
|
||||
'phone' => '020 7000 0000',
|
||||
'password' => 'password123',
|
||||
'password_confirmation' => 'password123',
|
||||
]);
|
||||
$response = $this->post('http://customer.test/register', [
|
||||
'firstName' => 'Taylor',
|
||||
'lastName' => 'Member',
|
||||
'email' => 'taylor@example.test',
|
||||
'phone' => '020 7000 0000',
|
||||
'password' => 'password123',
|
||||
'password_confirmation' => 'password123',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('customer.dashboard'));
|
||||
$this->assertAuthenticated();
|
||||
|
||||
$this->assertDatabaseHas('User', ['email' => 'taylor@example.test']);
|
||||
$this->assertDatabaseHas('CustomerProfile', [
|
||||
'email' => 'taylor@example.test',
|
||||
'isGuest' => 0,
|
||||
'businessId' => Business::where('slug', 'demo-barbershop')->value('id'),
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('CustomerProfile', ['email' => 'taylor@example.test', 'isGuest' => 0, 'businessId' => $business->id]);
|
||||
$this->get('http://customer.test/account')->assertOk()->assertSee('Good to see you, Taylor');
|
||||
$this->get('http://customer.test/owner')->assertForbidden();
|
||||
$this->assertSame(1, CustomerProfile::where('email', 'taylor@example.test')->count());
|
||||
$this->withHeaders(['Host' => 'localhost'])->get('/account')->assertOk()->assertSee('Good to see you, Taylor');
|
||||
$this->withHeaders(['Host' => 'localhost'])->get('/owner')->assertForbidden();
|
||||
$this->assertNotNull(User::where('email', 'taylor@example.test')->first());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user