test(Settings::class) ->set('name', 'New Name')->set('email', 'new@ops.test')->call('saveAccount')->assertHasNoErrors(); expect($owner->fresh()->name)->toBe('New Name')->and($owner->fresh()->email)->toBe('new@ops.test'); }); it('lets an Owner invite staff with a role', function () { Livewire::actingAs(operator('Owner'))->test(Settings::class) ->set('staffName', 'Sam')->set('staffEmail', 'sam@ops.test')->set('staffRole', 'Support') ->call('inviteStaff')->assertHasNoErrors(); $sam = User::query()->where('email', 'sam@ops.test')->first(); expect($sam)->not->toBeNull()->and($sam->hasRole('Support'))->toBeTrue(); }); it('surfaces a usable temporary password for the invited staff', function () { $c = Livewire::actingAs(operator('Owner'))->test(Settings::class) ->set('staffName', 'Ivy')->set('staffEmail', 'ivy@ops.test')->set('staffRole', 'Support') ->call('inviteStaff'); $temp = $c->get('invitedPassword'); expect($temp)->not->toBeNull(); $ivy = User::query()->where('email', 'ivy@ops.test')->first(); expect(Illuminate\Support\Facades\Hash::check($temp, $ivy->password))->toBeTrue(); }); it('forbids a non-Owner from managing staff', function () { Livewire::actingAs(operator('Admin'))->test(Settings::class) ->set('staffName', 'X')->set('staffEmail', 'x@ops.test')->set('staffRole', 'Support') ->call('inviteStaff')->assertForbidden(); expect(User::query()->where('email', 'x@ops.test')->exists())->toBeFalse(); }); it('will not create an operator from a customer email', function () { Customer::factory()->create(['email' => 'dup@ops.test']); Livewire::actingAs(operator('Owner'))->test(Settings::class) ->set('staffName', 'Dup')->set('staffEmail', 'dup@ops.test')->set('staffRole', 'Support') ->call('inviteStaff')->assertHasErrors(['staffEmail']); }); it('will not escalate a non-staff user via a tampered id', function () { $victim = User::factory()->create(); // a plain (customer) user, no role Livewire::actingAs(operator('Owner'))->test(Settings::class)->call('setStaffRole', $victim->id, 'Admin'); expect($victim->fresh()->isOperator())->toBeFalse(); }); it('protects the last owner and self-role', function () { $owner = operator('Owner'); $support = operator('Support'); // Cannot change own role. Livewire::actingAs($owner)->test(Settings::class)->call('setStaffRole', $owner->id, 'Admin'); expect($owner->fresh()->hasRole('Owner'))->toBeTrue(); // Only one owner → cannot revoke it. Livewire::actingAs($owner)->test(Settings::class)->call('revokeStaff', $owner->id); expect($owner->fresh()->hasRole('Owner'))->toBeTrue(); // Owner can change another operator's role. Livewire::actingAs($owner)->test(Settings::class)->call('setStaffRole', $support->id, 'Billing'); expect($support->fresh()->hasRole('Billing'))->toBeTrue()->and($support->fresh()->hasRole('Support'))->toBeFalse(); });