shouldReceive('assertCanMutateSource') ->once() ->with(Mockery::on(fn ($p) => $p instanceof Project && $p->id === 99), 'delete') ->andThrow(new HttpResponseException(response()->json([], 422))); $service = new ProjectService(new OperationsLogger, $guard); $project = new Project(['tenant_id' => 1]); $project->id = 99; // We expect guard to throw → ProjectService::delete bails out before touching DB. // Если guard НЕ вызывался — Mockery скажет shouldReceive missed → fail. try { $service->delete($project); $this->fail('Expected HttpResponseException'); } catch (HttpResponseException) { $this->assertTrue(true); } } public function test_update_invokes_guard_with_change_source_action_when_signal_identifier_changes(): void { $guard = Mockery::mock(SupplierSnapshotGuard::class); $guard->shouldReceive('assertCanMutateSource') ->once() ->with(Mockery::on(fn ($p) => $p instanceof Project && $p->id === 100), 'change_source') ->andThrow(new HttpResponseException(response()->json([], 422))); $service = new ProjectService(new OperationsLogger, $guard); $project = new Project([ 'tenant_id' => 1, 'signal_type' => 'call', 'signal_identifier' => '79161234567', 'delivered_today' => 0, ]); $project->id = 100; try { $service->update($project, ['signal_identifier' => '79169999999']); $this->fail('Expected HttpResponseException'); } catch (HttpResponseException) { $this->assertTrue(true); } } public function test_update_does_not_invoke_guard_when_only_non_source_fields_change(): void { $guard = Mockery::mock(SupplierSnapshotGuard::class); $guard->shouldNotReceive('assertCanMutateSource'); $service = new ProjectService(new OperationsLogger, $guard); $project = new Project([ 'tenant_id' => 1, 'signal_type' => 'call', 'signal_identifier' => '79161234567', 'delivered_today' => 0, 'daily_limit_target' => 10, ]); $project->id = 101; // Меняем только daily_limit_target / regions — guard вызываться не должен. // Реальный update упадёт на $project->update() (нет таблицы) — это нормально, // нам важна только проверка mockery expectation на guard. try { $service->update($project, ['daily_limit_target' => 20]); } catch (\Throwable) { // ignore — нас интересует только что guard НЕ был вызван } // mockery expectations проверятся в tearDown — если guard ВЫЗВАЛСЯ, тест провалится $this->assertTrue(true); } }