'test'], ['name' => 'T', 'price' => 0, 'features' => []]); $this->company = Company::create([ 'plan_id' => $plan->id, 'slug' => 'inv-' . uniqid(), 'name' => 'Inv Co', 'status' => 'active', ]); app(TenantManager::class)->setCurrent($this->company); app(RbacSeeder::class)->seedTenantRoles($this->company->id); } public function test_send_invitation_generates_token_marks_pending_and_queues_mail(): void { Mail::fake(); $u = User::create(['name' => 'X', 'email' => 'x@e.com', 'password' => bcrypt('x'), 'role' => 'mechanic', 'status' => 'active']); $token = $u->sendInvitation(); $u->refresh(); $this->assertNotEmpty($token); $this->assertEquals(64, strlen($token)); $this->assertNotNull($u->invited_at); $this->assertNull($u->accepted_at); $this->assertEquals('inactive', $u->status); $this->assertTrue($u->isPendingInvitation()); // Stored token is sha256 hash, not the raw value $this->assertNotEquals($token, $u->invitation_token); $this->assertEquals(hash('sha256', $token), $u->invitation_token); Mail::assertQueued(UserInvitationMail::class, fn ($m) => $m->user->id === $u->id); } public function test_find_by_invitation_token_resolves_user(): void { Mail::fake(); $u = User::create(['name' => 'X', 'email' => 'x@e.com', 'password' => bcrypt('x'), 'role' => 'mechanic', 'status' => 'active']); $token = $u->sendInvitation(); $found = User::findByInvitationToken($token); $this->assertNotNull($found); $this->assertEquals($u->id, $found->id); // Wrong token returns null $this->assertNull(User::findByInvitationToken('not-a-real-token')); } public function test_accept_invitation_sets_password_clears_token_activates(): void { Mail::fake(); $u = User::create(['name' => 'X', 'email' => 'x@e.com', 'password' => bcrypt('placeholder'), 'role' => 'mechanic', 'status' => 'inactive']); $token = $u->sendInvitation(); $u->refresh(); $u->acceptInvitation('NewStrongPass123'); $u->refresh(); $this->assertNull($u->invitation_token); $this->assertNotNull($u->accepted_at); $this->assertEquals('active', $u->status); $this->assertNotNull($u->email_verified_at); $this->assertTrue(\Hash::check('NewStrongPass123', $u->password)); $this->assertFalse($u->isPendingInvitation()); } public function test_invitation_url_returns_form_with_token(): void { Mail::fake(); $u = User::create(['name' => 'X', 'email' => 'x@e.com', 'password' => bcrypt('x'), 'role' => 'mechanic', 'status' => 'active']); $token = $u->sendInvitation(); $resp = $this->get("/invitations/{$token}"); $resp->assertStatus(200); $resp->assertSee($u->name); $resp->assertSee('Activează contul'); } public function test_invitation_post_accepts_password_and_redirects(): void { Mail::fake(); $u = User::create(['name' => 'X', 'email' => 'x@e.com', 'password' => bcrypt('placeholder'), 'role' => 'mechanic', 'status' => 'active']); $token = $u->sendInvitation(); $resp = $this->post("/invitations/{$token}", [ 'password' => 'BrandNewPass1', 'password_confirmation' => 'BrandNewPass1', ]); $resp->assertStatus(302); $u->refresh(); $this->assertNotNull($u->accepted_at); $this->assertTrue(\Hash::check('BrandNewPass1', $u->password)); } public function test_invalid_token_returns_invalid_view(): void { $resp = $this->get('/invitations/not-a-real-token'); $resp->assertStatus(200); $resp->assertSee('Invitație invalidă'); } public function test_expired_invitation_returns_expired_view(): void { Mail::fake(); $u = User::create(['name' => 'X', 'email' => 'x@e.com', 'password' => bcrypt('x'), 'role' => 'mechanic', 'status' => 'active']); $token = $u->sendInvitation(); // Force expiry by backdating invited_at $u->forceFill(['invited_at' => now()->subDays(8)])->saveQuietly(); $resp = $this->get("/invitations/{$token}"); $resp->assertStatus(200); $resp->assertSee('Invitație expirată'); } public function test_password_too_short_returns_validation_error(): void { Mail::fake(); $u = User::create(['name' => 'X', 'email' => 'x@e.com', 'password' => bcrypt('x'), 'role' => 'mechanic', 'status' => 'active']); $token = $u->sendInvitation(); $resp = $this->post("/invitations/{$token}", [ 'password' => 'short', 'password_confirmation' => 'short', ]); $resp->assertSessionHasErrors('password'); $u->refresh(); $this->assertNull($u->accepted_at); } }