From fd4fc815d9ff6086093da601800242cd5dde11b0 Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Thu, 30 Apr 2026 01:05:27 -0400 Subject: [PATCH 1/5] Fix phpcs violations and add phpcs to CI phpcbf auto-fixed 36 violations (blank-line spacing on enums and similar). Manual fixes: - src/Client.php: split long URL string into a renterId variable - src/ClientException.php: add a marker comment so the empty class body doesn't trip the empty-lines-around-braces rules - src/Model/Address.php: extract repeated regex/message into class constants, line-wrap the message to stay under 120 chars - test/Unit/Model/AddressTest.php: extract long fixture strings into local variables and the shared message into a constant; reformat the data provider rows so each fits on one line Add a phpcs CI job that runs against vendor/rentpost/coding-standard-php with --warning-severity=0, so warnings (TU API field names that include digits, intentional unused PHPUnit @depends params) are visible but only errors fail the build. All 66 unit tests pass. --- .github/workflows/ci.yml | 28 ++++++++++++++ src/Client.php | 3 +- src/ClientException.php | 1 + src/ClientInterface.php | 1 - src/Model/Address.php | 26 +++++-------- src/Model/EmploymentStatus.php | 2 + src/Model/RenterRole.php | 2 + src/Model/RenterStatus.php | 2 + src/Model/ReportType.php | 1 + src/Model/RequestedProduct.php | 2 + test/Integration/IntegrationTest.php | 4 +- test/Unit/Model/AddressTest.php | 56 ++++++++++++++++++++++++---- test/Unit/Model/RenterTest.php | 24 ++++++++++-- 13 files changed, 120 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c416ff5..2cde5f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,34 @@ jobs: - name: Run unit tests run: vendor/bin/phpunit --testsuite unit + phpcs: + name: phpcs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: none + tools: composer:v2 + + - name: Install dependencies + uses: ramsey/composer-install@v3 + with: + composer-options: '--no-interaction --no-progress --prefer-dist' + + # --warning-severity=0 fails CI only on errors. Warnings (e.g. TU API + # field names that contain digits, intentional unused PHPUnit @depends + # params) are surfaced in the output but don't block. + - name: Run phpcs + run: | + vendor/bin/phpcs \ + --standard=vendor/rentpost/coding-standard-php/phpcs.xml \ + --warning-severity=0 \ + src/ test/ + # Integration tests hit the real TransUnion ShareAble sandbox API and require # credentials. They run only on manual maintainer trigger so PR validation # never burns sandbox quota and so secrets are never exposed to fork PRs. diff --git a/src/Client.php b/src/Client.php index 3c2e3d5..a628705 100644 --- a/src/Client.php +++ b/src/Client.php @@ -447,9 +447,10 @@ public function addRenterToScreeningRequest( ScreeningRequestRenter $screeningRequestRenter, ): ScreeningRequestRenter { + $renterId = $screeningRequestRenter->getRenterId(); $response = $this->requestJson( 'POST', - "ScreeningRequests/$screeningRequestId/Renters/{$screeningRequestRenter->getRenterId()}/ScreeningRequestRenters", + "ScreeningRequests/$screeningRequestId/Renters/$renterId/ScreeningRequestRenters", $screeningRequestRenter->toArray(), // No clue on this - docs don't say it's needed, but it is ); diff --git a/src/ClientException.php b/src/ClientException.php index e8bbf19..a4af81a 100644 --- a/src/ClientException.php +++ b/src/ClientException.php @@ -7,4 +7,5 @@ class ClientException extends \Exception { + // Marker exception for ShareAble client errors. } diff --git a/src/ClientInterface.php b/src/ClientInterface.php index fa2d33b..3b22e76 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -4,7 +4,6 @@ namespace Rentpost\TUShareable; -use Rentpost\TUShareable\Model\Attestation; use Rentpost\TUShareable\Model\AttestationGroup; use Rentpost\TUShareable\Model\Bundle; use Rentpost\TUShareable\Model\CultureCode; diff --git a/src/Model/Address.php b/src/Model/Address.php index f7785b1..ab28d93 100644 --- a/src/Model/Address.php +++ b/src/Model/Address.php @@ -15,34 +15,28 @@ class Address use Validate; + private const ADDRESS_PATTERN = '/^[a-zA-Z0-9 #()&.,\'\-_\+~\/\*]*$/'; + private const ADDRESS_PATTERN_MESSAGE = 'Address field must only contain letters, numbers, spaces, hashes, ' + . 'parentheses, ampersands, commas, periods, single quotes, hyphens, underscores, pluses, tildes, ' + . 'forward slashes and asterisks.'; + + public function __construct( #[Assert\NotBlank] #[Assert\Length(min: 1, max: 50)] - #[Assert\Regex( - pattern: '/^[a-zA-Z0-9 #()&.,\'\-_\+~\/\*]*$/', - message: 'Address field must only contain letters, numbers, spaces, hashes, parentheses, ampersands, commas, periods, single quotes, hyphens, underscores, pluses, tildes, forward slashes and asterisks.', - )] + #[Assert\Regex(pattern: self::ADDRESS_PATTERN, message: self::ADDRESS_PATTERN_MESSAGE)] private string $addressLine1, #[Assert\Length(max: 100)] - #[Assert\Regex( - pattern: '/^[a-zA-Z0-9 #()&.,\'\-_\+~\/\*]*$/', - message: 'Address field must only contain letters, numbers, spaces, hashes, parentheses, ampersands, commas, periods, single quotes, hyphens, underscores, pluses, tildes, forward slashes and asterisks.', - )] + #[Assert\Regex(pattern: self::ADDRESS_PATTERN, message: self::ADDRESS_PATTERN_MESSAGE)] private ?string $addressLine2, #[Assert\Length(max: 100)] - #[Assert\Regex( - pattern: '/^[a-zA-Z0-9 #()&.,\'\-_\+~\/\*]*$/', - message: 'Address field must only contain letters, numbers, spaces, hashes, parentheses, ampersands, commas, periods, single quotes, hyphens, underscores, pluses, tildes, forward slashes and asterisks.', - )] + #[Assert\Regex(pattern: self::ADDRESS_PATTERN, message: self::ADDRESS_PATTERN_MESSAGE)] private ?string $addressLine3, #[Assert\Length(max: 100)] - #[Assert\Regex( - pattern: '/^[a-zA-Z0-9 #()&.,\'\-_\+~\/\*]*$/', - message: 'Address field must only contain letters, numbers, spaces, hashes, parentheses, ampersands, commas, periods, single quotes, hyphens, underscores, pluses, tildes, forward slashes and asterisks.', - )] + #[Assert\Regex(pattern: self::ADDRESS_PATTERN, message: self::ADDRESS_PATTERN_MESSAGE)] private ?string $addressLine4, #[Assert\NotBlank] diff --git a/src/Model/EmploymentStatus.php b/src/Model/EmploymentStatus.php index f6d10a9..167052d 100644 --- a/src/Model/EmploymentStatus.php +++ b/src/Model/EmploymentStatus.php @@ -8,8 +8,10 @@ enum EmploymentStatus: string { + use From; + case NotEmployed = 'NotEmployed'; case Employed = 'Employed'; case SelfEmployed = 'SelfEmployed'; diff --git a/src/Model/RenterRole.php b/src/Model/RenterRole.php index eeeac9c..340cbdd 100644 --- a/src/Model/RenterRole.php +++ b/src/Model/RenterRole.php @@ -8,8 +8,10 @@ enum RenterRole: string { + use From; + case Applicant = 'Applicant'; case CoSigner = 'CoSigner'; } diff --git a/src/Model/RenterStatus.php b/src/Model/RenterStatus.php index b0f9509..eeb088f 100644 --- a/src/Model/RenterStatus.php +++ b/src/Model/RenterStatus.php @@ -8,8 +8,10 @@ enum RenterStatus: string { + use From; + case IdentityVerificationPending = 'IdentityVerificationPending'; case ScreeningRequestCanceled = 'ScreeningRequestCanceled'; case ReadyForReportRequest = 'ReadyForReportRequest'; diff --git a/src/Model/ReportType.php b/src/Model/ReportType.php index 7a1f4d8..405e4ac 100644 --- a/src/Model/ReportType.php +++ b/src/Model/ReportType.php @@ -6,6 +6,7 @@ enum ReportType: string { + case Html = 'html'; case Json = 'json'; } diff --git a/src/Model/RequestedProduct.php b/src/Model/RequestedProduct.php index 5b0dc39..bffe2d1 100644 --- a/src/Model/RequestedProduct.php +++ b/src/Model/RequestedProduct.php @@ -8,8 +8,10 @@ enum RequestedProduct: string { + use From; + case All = 'all'; case None = 'none'; diff --git a/test/Integration/IntegrationTest.php b/test/Integration/IntegrationTest.php index c97359a..49f011f 100644 --- a/test/Integration/IntegrationTest.php +++ b/test/Integration/IntegrationTest.php @@ -596,7 +596,7 @@ public function testGetReportsForLandlord( // Normally we execute this only after receiving a notification from the service // But we can just sleep until they should be finished - while ((time() - $reportRequestTime) < 60) { + while (time() - $reportRequestTime < 60) { sleep(1); } @@ -634,7 +634,7 @@ public function testGetReportsForRenter( // Normally we execute this only after receiving a notification from the service // But we can just sleep until they should be finished - while ((time() - $reportRequestTime) < 60) { + while (time() - $reportRequestTime < 60) { sleep(1); } diff --git a/test/Unit/Model/AddressTest.php b/test/Unit/Model/AddressTest.php index ae6d2d3..5366f70 100644 --- a/test/Unit/Model/AddressTest.php +++ b/test/Unit/Model/AddressTest.php @@ -12,6 +12,11 @@ class AddressTest extends TestCase { + private const INVALID_CHARS_MESSAGE = 'Address field must only contain letters, numbers, spaces, hashes, ' + . 'parentheses, ampersands, commas, periods, single quotes, hyphens, underscores, pluses, tildes, ' + . 'forward slashes and asterisks.'; + + public function testConstructorAndGetters(): void { $addr = new Address( @@ -51,21 +56,56 @@ public function testConstructorAndGetters(): void */ public static function validationProvider(): array { + $tooLongAddress = 'omNScSZL7pBjZYmgGPcFmGbGVs9sJf110IeSSeh5bPSXgb0YkfX'; + $tooLongLocality = 'too-long-locality-value-here'; + return [ // addressLine1 missing - [ ['', '', '', '', 'City', 'FL', '12345'], 'addressLine1', 'This value is too short. It should have 1 character or more.' ], + [ + ['', '', '', '', 'City', 'FL', '12345'], + 'addressLine1', + 'This value is too short. It should have 1 character or more.', + ], // addressLine1 too long - [ ['omNScSZL7pBjZYmgGPcFmGbGVs9sJf110IeSSeh5bPSXgb0YkfX', '', '', '', 'City', 'FL', '12345'], 'addressLine1', 'This value is too long. It should have 50 characters or less.' ], + [ + [$tooLongAddress, '', '', '', 'City', 'FL', '12345'], + 'addressLine1', + 'This value is too long. It should have 50 characters or less.', + ], // locality missing - [ ['Street 1', '', '', '', '', 'FL', '12345'], 'locality', 'This value is too short. It should have 2 characters or more.' ], + [ + ['Street 1', '', '', '', '', 'FL', '12345'], + 'locality', + 'This value is too short. It should have 2 characters or more.', + ], // locality too long - [ ['Street 1', '', '', '', 'too-long-locality-value-here', 'FL', '12345'], 'locality', 'This value is too long. It should have 27 characters or less.' ], + [ + ['Street 1', '', '', '', $tooLongLocality, 'FL', '12345'], + 'locality', + 'This value is too long. It should have 27 characters or less.', + ], // invalid state - [ ['Street 1', '', '', '', 'City', 'XX', '12345'], 'region', 'The value is not a valid US state.' ], + [ + ['Street 1', '', '', '', 'City', 'XX', '12345'], + 'region', + 'The value is not a valid US state.', + ], // invalid characters - [ ['Invalid character "', '', '', '', 'St Pete', 'FL', '12345'], 'region', 'Address field must only contain letters, numbers, spaces, hashes, parentheses, ampersands, commas, periods, single quotes, hyphens, underscores, pluses, tildes, forward slashes and asterisks.' ], - [ ['Invalid character $', '', '', '', 'St Pete', 'FL', '12345'], 'region', 'Address field must only contain letters, numbers, spaces, hashes, parentheses, ampersands, commas, periods, single quotes, hyphens, underscores, pluses, tildes, forward slashes and asterisks.' ], - [ ['Invalid character @', '', '', '', 'St Pete', 'FL', '12345'], 'region', 'Address field must only contain letters, numbers, spaces, hashes, parentheses, ampersands, commas, periods, single quotes, hyphens, underscores, pluses, tildes, forward slashes and asterisks.' ], + [ + ['Invalid character "', '', '', '', 'St Pete', 'FL', '12345'], + 'region', + self::INVALID_CHARS_MESSAGE, + ], + [ + ['Invalid character $', '', '', '', 'St Pete', 'FL', '12345'], + 'region', + self::INVALID_CHARS_MESSAGE, + ], + [ + ['Invalid character @', '', '', '', 'St Pete', 'FL', '12345'], + 'region', + self::INVALID_CHARS_MESSAGE, + ], ]; } diff --git a/test/Unit/Model/RenterTest.php b/test/Unit/Model/RenterTest.php index 8e7b101..16c7386 100644 --- a/test/Unit/Model/RenterTest.php +++ b/test/Unit/Model/RenterTest.php @@ -124,11 +124,27 @@ public static function validationProvider(): array { return [ // Invalid income frequency - ['1000', 'Invalid', '5000', 'PerYear', '12000', EmploymentStatus::SelfEmployed, null, - 'The value you selected is not a valid choice.', 'incomeFrequency'], + [ + '1000', + 'Invalid', + '5000', + 'PerYear', + '12000', + EmploymentStatus::SelfEmployed, + null, + 'The value you selected is not a valid choice.', + 'incomeFrequency',], // Invalid other income frequency - ['1000', 'PerMonth', '5000', 'Invalid', '12000', EmploymentStatus::SelfEmployed, null, - 'The value you selected is not a valid choice.', 'otherIncomeFrequency'], + [ + '1000', + 'PerMonth', + '5000', + 'Invalid', + '12000', + EmploymentStatus::SelfEmployed, + null, + 'The value you selected is not a valid choice.', + 'otherIncomeFrequency',], ]; } From fcf86a3af8f33b6c04bd199455264396a50e4248 Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Thu, 30 Apr 2026 01:12:10 -0400 Subject: [PATCH 2/5] phpcbf reorder for the updated ClassStructure rule The just-merged coding-standard-php master tightens SlevomatCodingStandard.Classes.ClassStructure so 'public static methods' must come before 'public methods'. phpcbf moved the static methods (fromArray, validationProvider, etc.) above the instance methods across the model and test classes. Pure reorder: every file has equal additions and removals, no logic touched. --- src/Model/Address.php | 32 ++++----- src/Model/Attestation.php | 28 ++++---- src/Model/AttestationGroup.php | 42 ++++++------ src/Model/Bundle.php | 20 +++--- src/Model/Exam.php | 44 ++++++------- src/Model/Landlord.php | 38 +++++------ src/Model/Person.php | 48 +++++++------- src/Model/Property.php | 40 ++++++------ src/Model/Renter.php | 44 ++++++------- src/Model/Reports.php | 32 ++++----- src/Model/ScreeningRequest.php | 46 ++++++------- src/Model/ScreeningRequestRenter.php | 46 ++++++------- test/Unit/Model/AddressTest.php | 68 ++++++++++---------- test/Unit/Model/DateTest.php | 16 ++--- test/Unit/Model/EmailTest.php | 16 ++--- test/Unit/Model/LandlordTest.php | 32 ++++----- test/Unit/Model/MoneyTest.php | 18 +++--- test/Unit/Model/PersonTest.php | 28 ++++---- test/Unit/Model/PhoneTest.php | 28 ++++---- test/Unit/Model/PropertyTest.php | 30 ++++----- test/Unit/Model/RenterTest.php | 60 ++++++++--------- test/Unit/Model/SocialSecurityNumberTest.php | 16 ++--- test/Unit/WebhookParserTest.php | 32 ++++----- 23 files changed, 402 insertions(+), 402 deletions(-) diff --git a/src/Model/Address.php b/src/Model/Address.php index ab28d93..c1950c8 100644 --- a/src/Model/Address.php +++ b/src/Model/Address.php @@ -21,6 +21,22 @@ class Address . 'forward slashes and asterisks.'; + /** @param array $data */ + public static function fromArray(array $data): self + { + return new self( + $data['addressLine1'], + $data['addressLine2'] ?? null, + $data['addressLine3'] ?? null, + $data['addressLine4'] ?? null, + $data['locality'], + $data['region'], + $data['postalCode'], + $data['country'], + ); + } + + public function __construct( #[Assert\NotBlank] #[Assert\Length(min: 1, max: 50)] @@ -247,20 +263,4 @@ public function toArray(): array return $array; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - return new self( - $data['addressLine1'], - $data['addressLine2'] ?? null, - $data['addressLine3'] ?? null, - $data['addressLine4'] ?? null, - $data['locality'], - $data['region'], - $data['postalCode'], - $data['country'], - ); - } } diff --git a/src/Model/Attestation.php b/src/Model/Attestation.php index e255329..43942be 100644 --- a/src/Model/Attestation.php +++ b/src/Model/Attestation.php @@ -13,6 +13,20 @@ class Attestation use Validate; + /** @param array $data */ + public static function fromArray(array $data): self + { + return new self( + $data['attestationId'], + $data['attestationTypeId'], + $data['name'], + $data['legalText'], + $data['affirmativeRequired'], + $data['additionalInformation'], + ); + } + + public function __construct( private int $attestationId, private int $attestationTypeId, @@ -73,18 +87,4 @@ public function toArray(): array 'additionalInformation' => $this->additionalInformation, ]; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - return new self( - $data['attestationId'], - $data['attestationTypeId'], - $data['name'], - $data['legalText'], - $data['affirmativeRequired'], - $data['additionalInformation'], - ); - } } diff --git a/src/Model/AttestationGroup.php b/src/Model/AttestationGroup.php index c5c9a19..7e321e8 100644 --- a/src/Model/AttestationGroup.php +++ b/src/Model/AttestationGroup.php @@ -19,6 +19,27 @@ class AttestationGroup private ?array $attestationResponses = null; + /** @param array|null> $data */ + public static function fromArray(array $data): self + { + $attestationGroupId = $data['attestationGroupId']; + + $attestations = []; + foreach (($data['attestations'] ?? []) as $attestation) { + $attestations[] = new Attestation( + $attestation['attestationId'], + $attestation['attestationTypeId'], + $attestation['name'] ?? null, + $attestation['legalText'] ?? null, + $attestation['affirmativeRequired'], + $attestation['additionalInformation'] ?? null, + ); + } + + return new self($attestationGroupId, $attestations); + } + + public function __construct( private readonly int $attestationGroupId, @@ -55,27 +76,6 @@ public function addAttestationResponse(int $attestationId, bool $isAffirmative): } - /** @param array|null> $data */ - public static function fromArray(array $data): self - { - $attestationGroupId = $data['attestationGroupId']; - - $attestations = []; - foreach (($data['attestations'] ?? []) as $attestation) { - $attestations[] = new Attestation( - $attestation['attestationId'], - $attestation['attestationTypeId'], - $attestation['name'] ?? null, - $attestation['legalText'] ?? null, - $attestation['affirmativeRequired'], - $attestation['additionalInformation'] ?? null, - ); - } - - return new self($attestationGroupId, $attestations); - } - - /** @return array> */ public function toArray(): array { diff --git a/src/Model/Bundle.php b/src/Model/Bundle.php index fcd96be..6d0e684 100644 --- a/src/Model/Bundle.php +++ b/src/Model/Bundle.php @@ -15,6 +15,16 @@ class Bundle use Validate; + /** @param array $data */ + public static function fromArray(array $data): self + { + return new self( + $data['bundleId'], + $data['name'], + ); + } + + public function __construct( #[Assert\NotBlank] private int $bundleId, @@ -45,14 +55,4 @@ public function toArray(): array 'name' => $this->name, ]; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - return new self( - $data['bundleId'], - $data['name'], - ); - } } diff --git a/src/Model/Exam.php b/src/Model/Exam.php index d0a8989..f644525 100644 --- a/src/Model/Exam.php +++ b/src/Model/Exam.php @@ -15,6 +15,28 @@ class Exam private ?string $externalReferenceNumber = null; + /** @param array $data */ + public static function fromArray(array $data): self + { + $exam = new self($data['examId'], $data['result']); + $exam->setExternalReferenceNumber($data['setExternalReferenceNumber'] ?? null); + + foreach ($data['authenticationQuestions'] as $qInfo) { + $question = new ExamQuestion($qInfo['questionKeyName'], $qInfo['questionDisplayName'], $qInfo['type']); + + foreach ($qInfo['choices'] as $choice) { + $answer = new ExamQuestionAnswer($choice['choiceKeyName'], $choice['choiceDisplayName']); + + $question->addChoice($answer); + } + + $exam->addQuestion($question); + } + + return $exam; + } + + public function __construct(private int $examId, private string $result) {} @@ -55,26 +77,4 @@ public function setExternalReferenceNumber(?string $val): void { $this->externalReferenceNumber = $val; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - $exam = new self($data['examId'], $data['result']); - $exam->setExternalReferenceNumber($data['setExternalReferenceNumber'] ?? null); - - foreach ($data['authenticationQuestions'] as $qInfo) { - $question = new ExamQuestion($qInfo['questionKeyName'], $qInfo['questionDisplayName'], $qInfo['type']); - - foreach ($qInfo['choices'] as $choice) { - $answer = new ExamQuestionAnswer($choice['choiceKeyName'], $choice['choiceDisplayName']); - - $question->addChoice($answer); - } - - $exam->addQuestion($question); - } - - return $exam; - } } diff --git a/src/Model/Landlord.php b/src/Model/Landlord.php index be2b0ad..16a811a 100644 --- a/src/Model/Landlord.php +++ b/src/Model/Landlord.php @@ -18,6 +18,25 @@ class Landlord private ?int $landlordId = null; + /** @param array $data */ + public static function fromArray(array $data): self + { + $landlord = new self( + new Email($data['emailAddress']), + $data['firstName'], + $data['lastName'], + new Phone($data['phoneNumber'], $data['phoneType']), + $data['businessName'] ?? null, + Address::fromArray($data['businessAddress']), + boolval($data['acceptedTermsAndConditions']), + ); + + $landlord->setLandlordId($data['landlordId'] ?? null); + + return $landlord; + } + + public function __construct( #[Assert\NotBlank] private Email $emailAddress, @@ -167,23 +186,4 @@ public function toArray(): array return $array; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - $landlord = new self( - new Email($data['emailAddress']), - $data['firstName'], - $data['lastName'], - new Phone($data['phoneNumber'], $data['phoneType']), - $data['businessName'] ?? null, - Address::fromArray($data['businessAddress']), - boolval($data['acceptedTermsAndConditions']), - ); - - $landlord->setLandlordId($data['landlordId'] ?? null); - - return $landlord; - } } diff --git a/src/Model/Person.php b/src/Model/Person.php index 39554b3..c30830e 100644 --- a/src/Model/Person.php +++ b/src/Model/Person.php @@ -18,6 +18,30 @@ class Person private ?int $personId = null; + /** @param array $data */ + public static function fromArray(array $data): self + { + $ssn = $data['socialSecurityNumber'] ?? null; + $dob = $data['dateOfBirth'] ?? null; + + $person = new self( + new Email($data['emailAddress']), + $data['firstName'], + $data['middleName'] ?? null, + $data['lastName'], + new Phone($data['phoneNumber'], $data['phoneType']), + $ssn ? new SocialSecurityNumber($ssn) : null, + $dob ? new Date($dob) : null, + Address::fromArray($data['homeAddress']), + boolval($data['acceptedTermsAndConditions']), + ); + + $person->setPersonId($data['personId'] ?? null); + + return $person; + } + + public function __construct( #[Assert\NotBlank] private Email $emailAddress, @@ -202,28 +226,4 @@ public function toArray(): array return $array; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - $ssn = $data['socialSecurityNumber'] ?? null; - $dob = $data['dateOfBirth'] ?? null; - - $person = new self( - new Email($data['emailAddress']), - $data['firstName'], - $data['middleName'] ?? null, - $data['lastName'], - new Phone($data['phoneNumber'], $data['phoneType']), - $ssn ? new SocialSecurityNumber($ssn) : null, - $dob ? new Date($dob) : null, - Address::fromArray($data['homeAddress']), - boolval($data['acceptedTermsAndConditions']), - ); - - $person->setPersonId($data['personId'] ?? null); - - return $person; - } } diff --git a/src/Model/Property.php b/src/Model/Property.php index 091c41e..aef1b89 100644 --- a/src/Model/Property.php +++ b/src/Model/Property.php @@ -19,6 +19,26 @@ class Property private bool $isActive = true; + /** @param array $data */ + public static function fromArray(array $data): self + { + $property = new self( + $data['propertyName'], + new Money((string)$data['rent']), + new Money((string)$data['deposit']), + Address::fromArray($data), + boolval($data['bankruptcyCheck']), + $data['bankruptcyTimeFrame'], + $data['incomeToRentRatio'], + ); + + $property->setPropertyId($data['propertyId'] ?? null); + $property->setIsActive(boolval($data['isActive'])); + + return $property; + } + + /** * Constructor * @@ -177,24 +197,4 @@ public function toArray(): array return $array; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - $property = new self( - $data['propertyName'], - new Money((string)$data['rent']), - new Money((string)$data['deposit']), - Address::fromArray($data), - boolval($data['bankruptcyCheck']), - $data['bankruptcyTimeFrame'], - $data['incomeToRentRatio'], - ); - - $property->setPropertyId($data['propertyId'] ?? null); - $property->setIsActive(boolval($data['isActive'])); - - return $property; - } } diff --git a/src/Model/Renter.php b/src/Model/Renter.php index b8562f4..39a5501 100644 --- a/src/Model/Renter.php +++ b/src/Model/Renter.php @@ -15,6 +15,28 @@ class Renter use Validate; + /** @param array $data */ + public static function fromArray(array $data): self + { + $msex = $data['multiShareExpirationDate'] ?? null; + + $renter = new self( + Person::fromArray($data), + new Money((string)$data['income']), + $data['incomeFrequency'], + new Money((string)$data['otherIncome']), + $data['otherIncomeFrequency'], + new Money((string)$data['assets']), + EmploymentStatus::from($data['employmentStatus']), + $msex ? new Date($msex) : null, + ); + + $renter->setRenterId($data['renterId'] ?? null); + + return $renter; + } + + public function __construct( #[Assert\NotBlank] private Person $person, @@ -166,26 +188,4 @@ public function toArray(): array return $array; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - $msex = $data['multiShareExpirationDate'] ?? null; - - $renter = new self( - Person::fromArray($data), - new Money((string)$data['income']), - $data['incomeFrequency'], - new Money((string)$data['otherIncome']), - $data['otherIncomeFrequency'], - new Money((string)$data['assets']), - EmploymentStatus::from($data['employmentStatus']), - $msex ? new Date($msex) : null, - ); - - $renter->setRenterId($data['renterId'] ?? null); - - return $renter; - } } diff --git a/src/Model/Reports.php b/src/Model/Reports.php index 93ac46c..ab410b4 100644 --- a/src/Model/Reports.php +++ b/src/Model/Reports.php @@ -14,6 +14,22 @@ class Reports private array $reports = []; + /** @param array $data */ + public static function fromArray(array $data): self + { + $reports = new self($data['reportsExpireNumberOfDays']); + + foreach ($data['reportResponseModelDetails'] as $reportInfo) { + $reports->addReport(new Report( + $reportInfo['providerName'], + $reportInfo['reportData'], + )); + } + + return $reports; + } + + public function __construct(private int $reportsExpireNumberOfDays) {} @@ -37,20 +53,4 @@ public function getReportsExpireNumberOfDays(): int { return $this->reportsExpireNumberOfDays; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - $reports = new self($data['reportsExpireNumberOfDays']); - - foreach ($data['reportResponseModelDetails'] as $reportInfo) { - $reports->addReport(new Report( - $reportInfo['providerName'], - $reportInfo['reportData'], - )); - } - - return $reports; - } } diff --git a/src/Model/ScreeningRequest.php b/src/Model/ScreeningRequest.php index 5e9a732..284e640 100644 --- a/src/Model/ScreeningRequest.php +++ b/src/Model/ScreeningRequest.php @@ -21,6 +21,29 @@ class ScreeningRequest private array $screeningRequestRenters = []; + /** @param array $data */ + public static function fromArray(array $data): self + { + $request = new self( + $data['landlordId'], + $data['propertyId'], + $data['initialBundleId'], + $data['createdOn'] ? new Date(substr($data['createdOn'], 0, 10)) : null, + $data['modifiedOn'] ? new Date(substr($data['modifiedOn'], 0, 10)) : null, + $data['propertyName'] ?? null, + $data['propertySummaryAddress'] ?? null, + ); + + $request->setScreeningRequestId($data['screeningRequestId'] ?? null); + + foreach ($data['screeningRequestRenters'] as $renterInfo) { + $request->addScreeningRequestRenter(ScreeningRequestRenter::fromArray($renterInfo)); + } + + return $request; + } + + public function __construct( #[Assert\NotBlank] private readonly int $landlordId, @@ -179,27 +202,4 @@ public function toArray(): array return $array; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - $request = new self( - $data['landlordId'], - $data['propertyId'], - $data['initialBundleId'], - $data['createdOn'] ? new Date(substr($data['createdOn'], 0, 10)) : null, - $data['modifiedOn'] ? new Date(substr($data['modifiedOn'], 0, 10)) : null, - $data['propertyName'] ?? null, - $data['propertySummaryAddress'] ?? null, - ); - - $request->setScreeningRequestId($data['screeningRequestId'] ?? null); - - foreach ($data['screeningRequestRenters'] as $renterInfo) { - $request->addScreeningRequestRenter(ScreeningRequestRenter::fromArray($renterInfo)); - } - - return $request; - } } diff --git a/src/Model/ScreeningRequestRenter.php b/src/Model/ScreeningRequestRenter.php index c50b892..e1ffe35 100644 --- a/src/Model/ScreeningRequestRenter.php +++ b/src/Model/ScreeningRequestRenter.php @@ -18,6 +18,29 @@ class ScreeningRequestRenter private ?int $screeningRequestRenterId = null; + /** @param array $data */ + public static function fromArray(array $data): self + { + $renter = new self( + $data['landlordId'], + $data['renterId'], + $data['bundleId'], + RenterRole::from($data['renterRole']), + RenterStatus::from($data['renterStatus']), + $data['createdOn'] ? new Date(substr($data['createdOn'], 0, 10)) : null, + $data['modifiedOn'] ? new Date(substr($data['modifiedOn'], 0, 10)) : null, + $data['renterFirstName'] ?? null, + $data['renterLastName'] ?? null, + $data['renterMiddleName'] ?? null, + $data['reportsExpireNumberOfDays'] ?? null, + ); + + $renter->setScreeningRequestRenterId($data['screeningRequestRenterId'] ?? null); + + return $renter; + } + + public function __construct( #[Assert\NotBlank] private int $landlordId, @@ -212,27 +235,4 @@ public function toArray(): array return $array; } - - - /** @param array $data */ - public static function fromArray(array $data): self - { - $renter = new self( - $data['landlordId'], - $data['renterId'], - $data['bundleId'], - RenterRole::from($data['renterRole']), - RenterStatus::from($data['renterStatus']), - $data['createdOn'] ? new Date(substr($data['createdOn'], 0, 10)) : null, - $data['modifiedOn'] ? new Date(substr($data['modifiedOn'], 0, 10)) : null, - $data['renterFirstName'] ?? null, - $data['renterLastName'] ?? null, - $data['renterMiddleName'] ?? null, - $data['reportsExpireNumberOfDays'] ?? null, - ); - - $renter->setScreeningRequestRenterId($data['screeningRequestRenterId'] ?? null); - - return $renter; - } } diff --git a/test/Unit/Model/AddressTest.php b/test/Unit/Model/AddressTest.php index 5366f70..091d9ae 100644 --- a/test/Unit/Model/AddressTest.php +++ b/test/Unit/Model/AddressTest.php @@ -17,40 +17,6 @@ class AddressTest extends TestCase . 'forward slashes and asterisks.'; - public function testConstructorAndGetters(): void - { - $addr = new Address( - 'Street 1 & -test_~/*', - 'Suburb 2 (test*)', - 'Apartment #3', - 'Room +4', - 'Miami', - 'FL', - '12345', - ); - - $this->assertSame('Street 1 & -test_~/*', $addr->getAddressLine1()); - $this->assertSame('Suburb 2 (test*)', $addr->getAddressLine2()); - $this->assertSame('Apartment #3', $addr->getAddressLine3()); - $this->assertSame('Room +4', $addr->getAddressLine4()); - $this->assertSame('Miami', $addr->getLocality()); - $this->assertSame('FL', $addr->getRegion()); - $this->assertSame('12345', $addr->getPostalCode()); - $this->assertSame('US', $addr->getCountry()); - - $this->assertSame([ - 'addressLine1' => 'Street 1 & -test_~/*', - 'addressLine2' => 'Suburb 2 (test*)', - 'addressLine3' => 'Apartment #3', - 'addressLine4' => 'Room +4', - 'locality' => 'Miami', - 'region' => 'FL', - 'postalCode' => '12345', - 'country' => 'US', - ], $addr->toArray()); - } - - /** * @return array> */ @@ -110,6 +76,40 @@ public static function validationProvider(): array } + public function testConstructorAndGetters(): void + { + $addr = new Address( + 'Street 1 & -test_~/*', + 'Suburb 2 (test*)', + 'Apartment #3', + 'Room +4', + 'Miami', + 'FL', + '12345', + ); + + $this->assertSame('Street 1 & -test_~/*', $addr->getAddressLine1()); + $this->assertSame('Suburb 2 (test*)', $addr->getAddressLine2()); + $this->assertSame('Apartment #3', $addr->getAddressLine3()); + $this->assertSame('Room +4', $addr->getAddressLine4()); + $this->assertSame('Miami', $addr->getLocality()); + $this->assertSame('FL', $addr->getRegion()); + $this->assertSame('12345', $addr->getPostalCode()); + $this->assertSame('US', $addr->getCountry()); + + $this->assertSame([ + 'addressLine1' => 'Street 1 & -test_~/*', + 'addressLine2' => 'Suburb 2 (test*)', + 'addressLine3' => 'Apartment #3', + 'addressLine4' => 'Room +4', + 'locality' => 'Miami', + 'region' => 'FL', + 'postalCode' => '12345', + 'country' => 'US', + ], $addr->toArray()); + } + + /** * @param string[] $values */ diff --git a/test/Unit/Model/DateTest.php b/test/Unit/Model/DateTest.php index cb28399..ef4e98f 100644 --- a/test/Unit/Model/DateTest.php +++ b/test/Unit/Model/DateTest.php @@ -12,14 +12,6 @@ class DateTest extends TestCase { - public function testConstructorAndGetters(): void - { - $date = new Date('2022-01-01'); - - $this->assertSame('2022-01-01', $date->getValue()); - } - - /** * @return array> */ @@ -33,6 +25,14 @@ public static function validationProvider(): array } + public function testConstructorAndGetters(): void + { + $date = new Date('2022-01-01'); + + $this->assertSame('2022-01-01', $date->getValue()); + } + + #[DataProvider('validationProvider')] public function testValidationErrors(string $value): void { diff --git a/test/Unit/Model/EmailTest.php b/test/Unit/Model/EmailTest.php index cabada4..774f00b 100644 --- a/test/Unit/Model/EmailTest.php +++ b/test/Unit/Model/EmailTest.php @@ -12,14 +12,6 @@ class EmailTest extends TestCase { - public function testConstructorAndGetters(): void - { - $email = new Email('test@example.com'); - - $this->assertSame('test@example.com', $email->getValue()); - } - - /** * @return array> */ @@ -32,6 +24,14 @@ public static function validationProvider(): array } + public function testConstructorAndGetters(): void + { + $email = new Email('test@example.com'); + + $this->assertSame('test@example.com', $email->getValue()); + } + + #[DataProvider('validationProvider')] public function testValidationErrors(string $address, string $errorMessage): void { diff --git a/test/Unit/Model/LandlordTest.php b/test/Unit/Model/LandlordTest.php index e72f817..2edb20a 100644 --- a/test/Unit/Model/LandlordTest.php +++ b/test/Unit/Model/LandlordTest.php @@ -41,6 +41,22 @@ protected function makeObject( } + /** + * @return array> + */ + public static function validationProvider(): array + { + return [ + // No first name + [ '', 'Last', 'Business', true, 'This value should not be blank.' ], + // No last name + [ 'First', '', 'Business', true, 'This value should not be blank.' ], + // Not accepted terms + [ 'First', 'Last', 'Business', false, 'Terms and conditions need to be accepted.' ], + ]; + } + + public function testConstructorAndGetters(): void { $landlord = $this->makeObject('First', 'Last', 'Business', true); @@ -77,22 +93,6 @@ public function testConstructorAndGetters(): void } - /** - * @return array> - */ - public static function validationProvider(): array - { - return [ - // No first name - [ '', 'Last', 'Business', true, 'This value should not be blank.' ], - // No last name - [ 'First', '', 'Business', true, 'This value should not be blank.' ], - // Not accepted terms - [ 'First', 'Last', 'Business', false, 'Terms and conditions need to be accepted.' ], - ]; - } - - #[DataProvider('validationProvider')] public function testValidationErrors( string $firstName, diff --git a/test/Unit/Model/MoneyTest.php b/test/Unit/Model/MoneyTest.php index 559d370..e390307 100644 --- a/test/Unit/Model/MoneyTest.php +++ b/test/Unit/Model/MoneyTest.php @@ -27,15 +27,6 @@ public static function validProvider(): array } - #[DataProvider('validProvider')] - public function testValidValues(string $value): void - { - $money = new Money($value); - - $this->assertSame($value, $money->getValue()); - } - - /** @return array> */ public static function invalidProvider(): array { @@ -50,6 +41,15 @@ public static function invalidProvider(): array } + #[DataProvider('validProvider')] + public function testValidValues(string $value): void + { + $money = new Money($value); + + $this->assertSame($value, $money->getValue()); + } + + #[DataProvider('invalidProvider')] public function testInvalidValues(string $value): void { diff --git a/test/Unit/Model/PersonTest.php b/test/Unit/Model/PersonTest.php index 43aa843..5fb9850 100644 --- a/test/Unit/Model/PersonTest.php +++ b/test/Unit/Model/PersonTest.php @@ -47,6 +47,20 @@ protected function makeObject( } + /** @return array> */ + public static function validationProvider(): array + { + return [ + // No first name + [ '', 'Middle', 'Last', true, 'This value should not be blank.' ], + // No last name + [ 'First', 'Middle', '', true, 'This value should not be blank.' ], + // Not accepted terms + [ 'First', 'Middle', 'Last', false, 'Terms and conditions need to be accepted.' ], + ]; + } + + public function testConstructorAndGetters(): void { $person = $this->makeObject('First', 'Middle', 'Last', true); @@ -87,20 +101,6 @@ public function testConstructorAndGetters(): void } - /** @return array> */ - public static function validationProvider(): array - { - return [ - // No first name - [ '', 'Middle', 'Last', true, 'This value should not be blank.' ], - // No last name - [ 'First', 'Middle', '', true, 'This value should not be blank.' ], - // Not accepted terms - [ 'First', 'Middle', 'Last', false, 'Terms and conditions need to be accepted.' ], - ]; - } - - #[DataProvider('validationProvider')] public function testValidationErrors( string $firstName, diff --git a/test/Unit/Model/PhoneTest.php b/test/Unit/Model/PhoneTest.php index 47d38f7..499ea23 100644 --- a/test/Unit/Model/PhoneTest.php +++ b/test/Unit/Model/PhoneTest.php @@ -12,20 +12,6 @@ class PhoneTest extends TestCase { - public function testConstructorAndGetters(): void - { - $phone = new Phone('0123456789', 'Home'); - - $this->assertSame('0123456789', $phone->getNumber()); - $this->assertSame('Home', $phone->getType()); - - $this->assertSame([ - 'phoneNumber' => '0123456789', - 'phoneType' => 'Home', - ], $phone->toArray()); - } - - /** @return array> */ public static function validationProvider(): array { @@ -41,6 +27,20 @@ public static function validationProvider(): array } + public function testConstructorAndGetters(): void + { + $phone = new Phone('0123456789', 'Home'); + + $this->assertSame('0123456789', $phone->getNumber()); + $this->assertSame('Home', $phone->getType()); + + $this->assertSame([ + 'phoneNumber' => '0123456789', + 'phoneType' => 'Home', + ], $phone->toArray()); + } + + #[DataProvider('validationProvider')] public function testValidationErrors(string $phone, string $type, string $errorMessage): void { diff --git a/test/Unit/Model/PropertyTest.php b/test/Unit/Model/PropertyTest.php index 4d24835..d9206c6 100644 --- a/test/Unit/Model/PropertyTest.php +++ b/test/Unit/Model/PropertyTest.php @@ -40,6 +40,21 @@ protected function makeObject( } + /** @return array> */ + public static function validationProvider(): array + { + return [ + // No name + [ '', 'This value should not be blank.' ], + // Too long name + [ + 'C1bj6YXjJbGyARqKRljur7mXTUWe1uyWMqECWdCICEWDUv169qU66CT4gztMc9AiRWelsynyT1jMnPsuCz9MfErN9S3XigeDIbJZn', + 'This value is too long. It should have 100 characters or less.', + ], + ]; + } + + public function testConstructorAndGetters(): void { $property = $this->makeObject('Apartment', true, 20, 90); @@ -75,21 +90,6 @@ public function testConstructorAndGetters(): void } - /** @return array> */ - public static function validationProvider(): array - { - return [ - // No name - [ '', 'This value should not be blank.' ], - // Too long name - [ - 'C1bj6YXjJbGyARqKRljur7mXTUWe1uyWMqECWdCICEWDUv169qU66CT4gztMc9AiRWelsynyT1jMnPsuCz9MfErN9S3XigeDIbJZn', - 'This value is too long. It should have 100 characters or less.', - ], - ]; - } - - #[DataProvider('validationProvider')] public function testValidationErrors(string $propertyName, string $errorMessage): void { diff --git a/test/Unit/Model/RenterTest.php b/test/Unit/Model/RenterTest.php index 16c7386..f1b807a 100644 --- a/test/Unit/Model/RenterTest.php +++ b/test/Unit/Model/RenterTest.php @@ -65,6 +65,36 @@ protected function makeObject( } + /** @return array[] */ + public static function validationProvider(): array + { + return [ + // Invalid income frequency + [ + '1000', + 'Invalid', + '5000', + 'PerYear', + '12000', + EmploymentStatus::SelfEmployed, + null, + 'The value you selected is not a valid choice.', + 'incomeFrequency',], + // Invalid other income frequency + [ + '1000', + 'PerMonth', + '5000', + 'Invalid', + '12000', + EmploymentStatus::SelfEmployed, + null, + 'The value you selected is not a valid choice.', + 'otherIncomeFrequency',], + ]; + } + + public function testConstructorAndGetters(): void { $renter = $this->makeObject( @@ -119,36 +149,6 @@ public function testConstructorAndGetters(): void } - /** @return array[] */ - public static function validationProvider(): array - { - return [ - // Invalid income frequency - [ - '1000', - 'Invalid', - '5000', - 'PerYear', - '12000', - EmploymentStatus::SelfEmployed, - null, - 'The value you selected is not a valid choice.', - 'incomeFrequency',], - // Invalid other income frequency - [ - '1000', - 'PerMonth', - '5000', - 'Invalid', - '12000', - EmploymentStatus::SelfEmployed, - null, - 'The value you selected is not a valid choice.', - 'otherIncomeFrequency',], - ]; - } - - #[DataProvider('validationProvider')] public function testValidationErrors( string $income, diff --git a/test/Unit/Model/SocialSecurityNumberTest.php b/test/Unit/Model/SocialSecurityNumberTest.php index bbb2be6..2b3756a 100644 --- a/test/Unit/Model/SocialSecurityNumberTest.php +++ b/test/Unit/Model/SocialSecurityNumberTest.php @@ -12,14 +12,6 @@ class SocialSecurityNumberTest extends TestCase { - public function testConstructorAndGetters(): void - { - $ssn = new SocialSecurityNumber('123456789'); - - $this->assertSame('123456789', $ssn->getValue()); - } - - /** @return array>*/ public static function validationProvider(): array { @@ -33,6 +25,14 @@ public static function validationProvider(): array } + public function testConstructorAndGetters(): void + { + $ssn = new SocialSecurityNumber('123456789'); + + $this->assertSame('123456789', $ssn->getValue()); + } + + #[DataProvider('validationProvider')] public function testValidationErrors(string $ssn, string $errorMessage): void { diff --git a/test/Unit/WebhookParserTest.php b/test/Unit/WebhookParserTest.php index b3b6b79..5638a70 100644 --- a/test/Unit/WebhookParserTest.php +++ b/test/Unit/WebhookParserTest.php @@ -14,6 +14,22 @@ class WebhookParserTest extends TestCase { + protected function createMockRequest(string $data): RequestInterface + { + $stream = $this->createStub(StreamInterface::class); + + $stream->method('getContents') + ->willReturn($data); + + $request = $this->createStub(RequestInterface::class); + + $request->method('getBody') + ->willReturn($stream); + + return $request; + } + + public function testParseReportDelivery(): void { $data = json_encode([ @@ -46,20 +62,4 @@ public function testParseAuthenticationStatus(): void $this->assertSame(12_345, $result->getScreeningRequestRenterId()); $this->assertSame('Passed', $result->getManualAuthenticationStatus()); } - - - protected function createMockRequest(string $data): RequestInterface - { - $stream = $this->createStub(StreamInterface::class); - - $stream->method('getContents') - ->willReturn($data); - - $request = $this->createStub(RequestInterface::class); - - $request->method('getBody') - ->willReturn($stream); - - return $request; - } } From 0114b7d7bddbfc28d86d6a595ce3c4ef942f10f6 Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Thu, 30 Apr 2026 01:23:19 -0400 Subject: [PATCH 3/5] Suppress unavoidable phpcs warnings; enforce both errors and warnings in CI - src/Model/Address.php: addressLine1..4 are TU API field names; renaming them would break the wire contract. Disable the Zend.NamingConventions.ContainsNumbers sniff for this file with a documented phpcs:disable directive. - test/Integration/IntegrationTest.php: testValidateRenterForScreeningRequest and testCreateReport must accept return values from PHPUnit #[Depends] chains, which means some parameters are unavoidably unused. Suppress the Generic.CodeAnalysis.UnusedFunctionParameter sniff for those two methods with explanatory comments on the parameters themselves. - .github/workflows/ci.yml: drop --warning-severity=0 from the phpcs job so CI now fails on either an error or a warning, since neither type should ever reappear. --- .github/workflows/ci.yml | 9 +-------- src/Model/Address.php | 3 +++ test/Integration/IntegrationTest.php | 6 ++++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cde5f0..55ff30b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,15 +59,8 @@ jobs: with: composer-options: '--no-interaction --no-progress --prefer-dist' - # --warning-severity=0 fails CI only on errors. Warnings (e.g. TU API - # field names that contain digits, intentional unused PHPUnit @depends - # params) are surfaced in the output but don't block. - name: Run phpcs - run: | - vendor/bin/phpcs \ - --standard=vendor/rentpost/coding-standard-php/phpcs.xml \ - --warning-severity=0 \ - src/ test/ + run: vendor/bin/phpcs --standard=vendor/rentpost/coding-standard-php/phpcs.xml src/ test/ # Integration tests hit the real TransUnion ShareAble sandbox API and require # credentials. They run only on manual maintainer trigger so PR validation diff --git a/src/Model/Address.php b/src/Model/Address.php index c1950c8..f998e77 100644 --- a/src/Model/Address.php +++ b/src/Model/Address.php @@ -6,6 +6,9 @@ use Symfony\Component\Validator\Constraints as Assert; +// addressLine1..4 are TU API field names; renaming would break the wire contract. +// phpcs:disable Zend.NamingConventions.ValidVariableName.ContainsNumbers + /** * Class that represents an address. */ diff --git a/test/Integration/IntegrationTest.php b/test/Integration/IntegrationTest.php index 49f011f..59ee2f2 100644 --- a/test/Integration/IntegrationTest.php +++ b/test/Integration/IntegrationTest.php @@ -547,10 +547,11 @@ public function testAnswerExam( #[Depends('testUpdateRenter')] #[Depends('testAddRenterToScreeningRequest')] #[Depends('testAnswerExam')] + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter public function testValidateRenterForScreeningRequest( Renter $renter, ScreeningRequestRenter $screeningRequestRenter, - Exam $exam, + Exam $exam, // required by PHPUnit #[Depends] chain even though unused here ): string { $status = self::$client->validateRenterForScreeningRequest( @@ -568,10 +569,11 @@ public function testValidateRenterForScreeningRequest( #[Depends('testUpdateRenter')] #[Depends('testAddRenterToScreeningRequest')] #[Depends('testValidateRenterForScreeningRequest')] + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter public function testCreateReport( Renter $renter, ScreeningRequestRenter $screeningRequestRenter, - string $validationResult, + string $validationResult, // required by PHPUnit #[Depends] chain even though unused here ): int { self::$client->createReport($screeningRequestRenter->getScreeningRequestRenterId(), $renter); From d8c5d6e1eb68dba00c362b04137ddb60375e2402 Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Thu, 30 Apr 2026 01:37:21 -0400 Subject: [PATCH 4/5] Move testCancelScreeningRequestForRenter to end of integration suite PHPUnit runs tests in source order, and cancelling the screening request before testCreateExam puts it in a terminal state in the TU sandbox. TU then rejects createExam with "IDV not possible as the reports were already generated" because cancelled is treated as a final report state. Move cancel to the very end and add a #[Depends('testGetReportsForRenter')] so it can never run before the report chain even if PHPUnit ever changes its default ordering. --- test/Integration/IntegrationTest.php | 34 ++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/test/Integration/IntegrationTest.php b/test/Integration/IntegrationTest.php index 59ee2f2..9f1995d 100644 --- a/test/Integration/IntegrationTest.php +++ b/test/Integration/IntegrationTest.php @@ -482,20 +482,6 @@ public function testGetScreeningRequestsForRenter(ScreeningRequestRenter $screen } - #[Depends('testAddRenterToScreeningRequest')] - public function testCancelScreeningRequestForRenter( - ScreeningRequestRenter $screeningRequestRenter, - ): void - { - self::$client->cancelScreeningRequestForRenter($screeningRequestRenter->getScreeningRequestRenterId()); - - // We don't need to assert anything because if the request - // fails an exception is thrown. But we have to assert something - // to disable phpunit warning. - $this->assertTrue(true); - } - - #[Depends('testUpdateRenter')] #[Depends('testAddRenterToScreeningRequest')] public function testCreateExam(Renter $renter, ScreeningRequestRenter $screeningRequestRenter): Exam @@ -661,4 +647,24 @@ public function testGetReportsForRenter( $this->assertGreaterThan(1_024, strlen($report->getReportData())); } } + + + /** + * Cancellation must run last. Cancelling earlier marks the screening request + * as terminal in the TU sandbox, which then makes testCreateExam fail with + * "IDV not possible as the reports were already generated". + */ + #[Depends('testAddRenterToScreeningRequest')] + #[Depends('testGetReportsForRenter')] + public function testCancelScreeningRequestForRenter( + ScreeningRequestRenter $screeningRequestRenter, + ): void + { + self::$client->cancelScreeningRequestForRenter($screeningRequestRenter->getScreeningRequestRenterId()); + + // We don't need to assert anything because if the request + // fails an exception is thrown. But we have to assert something + // to disable phpunit warning. + $this->assertTrue(true); + } } From 92fdc35e1a8d30c2f725f4f536bcba0f634f01db Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Thu, 30 Apr 2026 01:41:08 -0400 Subject: [PATCH 5/5] Cancel test stands up its own screening request Cancelling the main screening request after the report chain fails with ScreeningRequestCannotCancel because TU treats a completed SR as terminal. Cancelling before the report chain fails because IDV requires a non- terminal SR. There's no order that makes both flows happy on the same SR. Have testCancelScreeningRequestForRenter create a dedicated, never- completed screening request for the existing renter and cancel that one, so the API endpoint is still exercised without disturbing the report flow. --- test/Integration/IntegrationTest.php | 61 ++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/test/Integration/IntegrationTest.php b/test/Integration/IntegrationTest.php index 9f1995d..301541d 100644 --- a/test/Integration/IntegrationTest.php +++ b/test/Integration/IntegrationTest.php @@ -650,21 +650,64 @@ public function testGetReportsForRenter( /** - * Cancellation must run last. Cancelling earlier marks the screening request - * as terminal in the TU sandbox, which then makes testCreateExam fail with - * "IDV not possible as the reports were already generated". + * Cancellation can't reuse the main screening request: by the time the report + * chain finishes, TU treats it as terminal and rejects cancel with + * `ScreeningRequestCannotCancel`. And running before reports breaks + * testCreateExam with `IDVNotPossible`. So this test stands up its own + * fresh, never-completed screening request, adds the renter to it, and + * cancels that one. */ - #[Depends('testAddRenterToScreeningRequest')] + #[Depends('testUpdateLandlord')] + #[Depends('testUpdateProperty')] + #[Depends('testUpdateRenter')] #[Depends('testGetReportsForRenter')] public function testCancelScreeningRequestForRenter( - ScreeningRequestRenter $screeningRequestRenter, + Landlord $landlord, + Property $property, + Renter $renter, ): void { - self::$client->cancelScreeningRequestForRenter($screeningRequestRenter->getScreeningRequestRenterId()); + $attestationGroup = self::$client->getAttestationsForProperty( + $landlord->getLandlordId(), + $property->getPropertyId(), + ); - // We don't need to assert anything because if the request - // fails an exception is thrown. But we have to assert something - // to disable phpunit warning. + foreach ($attestationGroup->getAttestations() as $attestation) { + $attestationGroup->addAttestationResponse($attestation->getAttestationId(), true); + } + + $request = new ScreeningRequest( + landlordId: $landlord->getLandlordId(), + propertyId: $property->getPropertyId(), + initialBundleId: 1_004, + attestationGroup: $attestationGroup, + ); + + self::$client->createScreeningRequest($request); + + $screeningRequestRenter = new ScreeningRequestRenter( + $landlord->getLandlordId(), + $renter->getRenterId(), + 1_004, + RenterRole::Applicant, + null, + null, + null, + $renter->getPerson()->getFirstName(), + $renter->getPerson()->getLastName(), + $renter->getPerson()->getMiddleName(), + ); + + self::$client->addRenterToScreeningRequest( + $request->getScreeningRequestId(), + $screeningRequestRenter, + ); + + self::$client->cancelScreeningRequestForRenter( + $screeningRequestRenter->getScreeningRequestRenterId(), + ); + + // No assertion needed; an exception would be thrown on failure. $this->assertTrue(true); } }