'Secure Org', 'contact_name' => 'Org Admin', 'email' => 'admin@secureorg.com', 'desired_subdomain' => 'secure-org-tenant', 'employee_count' => 25, 'requested_features' => ['payroll'], 'email_verified_at' => now(), 'status' => 'approved', ]); $job = new ProvisionTenantJob($inquiry); $job->handle(); $tenant = Tenant::find('secure-org-tenant'); $this->assertNotNull($tenant); // 1. Verify state transition to active $this->assertEquals('active', $inquiry->fresh()->status); // 2. Verify TenantEntitlement created in central DB $entitlement = TenantEntitlement::where('tenant_id', 'secure-org-tenant')->first(); $this->assertNotNull($entitlement); $this->assertEquals(25, $entitlement->max_employees); // 3. Verify TenantSetupToken generated with unexpired hashed token $setupToken = TenantSetupToken::where('tenant_id', 'secure-org-tenant')->first(); $this->assertNotNull($setupToken); $this->assertEquals('admin@secureorg.com', $setupToken->email); $this->assertFalse($setupToken->isExpired()); $this->assertFalse($setupToken->isUsed()); // 4. Verify email invitation was dispatched Mail::assertSent(\App\Mail\TenantSetupInvitationMail::class, function ($mail) { return $mail->hasTo('admin@secureorg.com'); }); // 5. Verify admin account inside tenant DB uses default password '12345678' $tenant->run(function () { $user = \App\Models\User::where('email', 'admin@secureorg.com')->first(); $this->assertNotNull($user); $this->assertEquals('active', $user->status); $this->assertTrue(\Illuminate\Support\Facades\Hash::check('12345678', $user->password)); }); } public function test_admin_can_set_password_via_token_and_token_expires_after_use(): void { $tenantId = 'setup-token-test'; $email = 'tokenadmin@acme.com'; $dbFile = database_path('tenant_' . $tenantId); if (!file_exists($dbFile)) { touch($dbFile); } Tenant::create([ 'id' => $tenantId, 'tenancy_db_name' => 'tenant_' . $tenantId, 'email' => $email, ]); $rawToken = 'valid_raw_test_token_123456'; $tokenHash = hash('sha256', $rawToken); $setupToken = TenantSetupToken::create([ 'tenant_id' => $tenantId, 'email' => $email, 'token_hash' => $tokenHash, 'expires_at' => now()->addHours(24), ]); $tenant = Tenant::find($tenantId); $tenant->run(function () use ($dbFile, $email) { config(['database.connections.tenant.database' => $dbFile]); \Illuminate\Support\Facades\DB::purge('tenant'); \Illuminate\Support\Facades\DB::reconnect('tenant'); \Illuminate\Support\Facades\Artisan::call('migrate', [ '--database' => 'tenant', '--path' => 'database/migrations/tenant', '--force' => true, ]); \App\Models\User::updateOrCreate( ['email' => $email], [ 'name' => 'Token Admin', 'password' => \Illuminate\Support\Facades\Hash::make('temp_random_hash'), 'status' => 'inactive', ] ); }); // Execute setup request $controller = new \App\Http\Controllers\TenantSetupController(); $request = \Illuminate\Http\Request::create('/setup-admin', 'POST', [ 'token' => $rawToken, 'password' => 'NewSecurePassword123!', 'password_confirmation' => 'NewSecurePassword123!', ]); $response = $tenant->run(function () use ($controller, $request) { return $controller->completeSetup($request); }); $this->assertEquals(302, $response->getStatusCode()); // Verify token is marked used $this->assertTrue($setupToken->fresh()->isUsed()); // Verify admin account is active and password updated inside tenant DB $tenant->run(function () use ($email) { $user = \App\Models\User::where('email', $email)->first(); $this->assertEquals('active', $user->status); $this->assertTrue(\Illuminate\Support\Facades\Hash::check('NewSecurePassword123!', $user->password)); }); } }