Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,21 @@ Entries below `Unreleased` are written by CI from the GitHub release body — se
`main` with no change here. It now asserts the alias resolves to exactly the canonical message,
and `require` raises `laranail/package-tools` to `^0.1.3` so a `prefer-lowest` resolve gets a
version that registers it.
- **PHPStan 2.3.0 failed the `phpstan` job with 50 errors in `tests/`, with no change here.** It
now binds `$this` inside `Closure::call()`, so `(fn () => $this->createDefaultValidator(...))->call($request)`
is typed as `FormRequest`'s declared return, the Validator contract, which has no `passes()`;
44 `expect($validator->passes())` calls could not resolve `TValue`, and two
`@phpstan-ignore argument.type` comments stopped matching. The 68 call sites now go through a
typed `defaultValidatorFor()` helper in `tests/Pest.php`, which asserts the concrete
`Illuminate\Validation\Validator` that `HasFluentRules` returns, and the stale ignores are gone.
Its bleeding-edge unused-variable rule found three test values that were never checked: the
null-values batch test now asserts validation passed, the custom-rule message test asserts the
rule object survives `RuleSet::compile()`, and the `BadMethodCallException` test asserts the
caught exception is `UnknownFluentRuleMethod`. Clean under both 2.2.17 and 2.3.0.

### Changed

- `scripts/verify-tag-currency.sh` passes a tag that is behind `main` only by `.github/`-only commits (a Dependabot bump ships nothing), and tells a package on real releases to cut a patch rather than move a published tag.
- `PostIncDecToPreIncDecRector` is skipped. Rector rewrote `$i++` to `++$i` in 21 files and Pint's
`increment_style` rewrote every one of them back, so the two gates could never both pass.
Formatting belongs to Pint.
Expand Down
25 changes: 24 additions & 1 deletion scripts/verify-tag-currency.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,19 @@ fi

tags=$(git tag --list 'v*' --sort=v:refname)

# A commit that touches only .github/ ships nothing (the directory is export-ignored), so a tag behind
# the branch by such commits still hands consumers exactly what the branch would. Dependabot adds one
# weekly; without this the scheduled check goes red on every workflow bump, in both tag models.
ci_only() {
local files
files=$(git diff --name-only "$1" "${head}") || return 1
[ -n "${files}" ] || return 1
! printf '%s\n' "${files}" | grep -qvE '^\.github/'
}

# More than one v* tag means real releases, which are immutable: they get a new patch, never a move.
released() { [ "$(printf '%s\n' "${tags}" | grep -c .)" -gt 1 ]; }

if [ -z "${tags}" ]; then
ok "No v* tag, so nothing is pinned."
exit 0
Expand Down Expand Up @@ -159,7 +172,11 @@ if [ "${commit}" = "${head}" ]; then
ok "${highest} is also on ${BRANCH}."
else
hbehind=$(git rev-list --count "${hcommit}..${head}")
if ci_only "${hcommit}"; then
ok "${highest} is behind ${BRANCH} only by ${hbehind} commit(s) that touch only .github/."
else
fail "${highest} is the highest tag and is ${hbehind} commit(s) behind ${BRANCH}. An unconstrained \`composer require\` resolves it, so it must be current or it must not exist."
fi
fi
fi
else
Expand All @@ -169,7 +186,13 @@ else
git log --oneline --no-decorate "${commit}..${head}" | sed 's/^/ /'
echo

fail "${current} is behind ${BRANCH}. Move it (git tag -f ${current} ${BRANCH} && git push --force ${REMOTE} ${current}) or cut a new one."
if ci_only "${commit}"; then
ok "${current} is behind ${BRANCH} only by commits that touch only .github/, so nothing a consumer installs is unreleased."
elif released; then
fail "${current} is behind ${BRANCH}. It is a published release, so do not move it: cut ${current%.*}.$(( ${current##*.} + 1 ))."
else
fail "${current} is behind ${BRANCH}. Move it (git tag -f ${current} ${BRANCH} && git push --force ${REMOTE} ${current}) or cut a new one."
fi
fi

exit "${FAILED}"
16 changes: 10 additions & 6 deletions tests/BatchDatabaseValidationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ function setupTestDatabase(): void
DB::connection('testing')->enableQueryLog();

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

expect($validator->passes())->toBeTrue();

Expand Down Expand Up @@ -478,7 +478,7 @@ function setupTestDatabase(): void
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

expect($validator->passes())->toBeFalse()
->and($validator->errors()->keys())->toContain('items.1.email')
Expand Down Expand Up @@ -507,7 +507,7 @@ function setupTestDatabase(): void
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

// Should fail — email doesn't exist in DB
expect($validator->passes())->toBeFalse()
Expand Down Expand Up @@ -535,7 +535,7 @@ function setupTestDatabase(): void
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

expect($validator->passes())->toBeTrue();
});
Expand Down Expand Up @@ -564,7 +564,7 @@ function setupTestDatabase(): void
DB::connection('testing')->enableQueryLog();

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

expect($validator->passes())->toBeTrue();

Expand Down Expand Up @@ -601,7 +601,7 @@ function setupTestDatabase(): void
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

// carol is soft-deleted, so this should fail
expect($validator->passes())->toBeFalse()
Expand Down Expand Up @@ -744,6 +744,10 @@ function setupTestDatabase(): void
$errors = $validationException->errors();
}

// Every item is nullable and null, so validation passes
expect($errors)
->toBeEmpty();

$queryLog = DB::connection('testing')->getQueryLog();
DB::connection('testing')->disableQueryLog();

Expand Down
22 changes: 11 additions & 11 deletions tests/BatchValidationGuardsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public function __toString(): string
DB::connection('testing')->enableQueryLog();

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);
$validator->passes();

$queryLog = DB::connection('testing')->getQueryLog();
Expand Down Expand Up @@ -257,7 +257,7 @@ public function __toString(): string
DB::connection('testing')->enableQueryLog();

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);
$validator->passes();

$queryLog = DB::connection('testing')->getQueryLog();
Expand Down Expand Up @@ -297,7 +297,7 @@ public function __toString(): string
DB::connection('testing')->enableQueryLog();

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);
$validator->passes();

$queryLog = DB::connection('testing')->getQueryLog();
Expand Down Expand Up @@ -543,7 +543,7 @@ public function __toString(): string
$caught = null;

try {
(fn () => $this->createDefaultValidator($factory))->call($formRequest);
defaultValidatorFor($formRequest, $factory);
} catch (ValidationException $e) {
$caught = $e;
}
Expand Down Expand Up @@ -737,7 +737,7 @@ public function __toString(): string
$caught = null;
try {
$factory = resolve(Factory::class);
(fn () => $this->createDefaultValidator($factory))->call($formRequest);
defaultValidatorFor($formRequest, $factory);
} catch (ValidationException $validationException) {
$caught = $validationException;
}
Expand Down Expand Up @@ -777,7 +777,7 @@ public function __toString(): string
DB::connection('testing')->enableQueryLog();

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

expect($validator->passes())->toBeTrue();

Expand Down Expand Up @@ -822,7 +822,7 @@ public function __toString(): string
$caught = null;
try {
$factory = resolve(Factory::class);
(fn () => $this->createDefaultValidator($factory))->call($formRequest);
defaultValidatorFor($formRequest, $factory);
} catch (ValidationException $validationException) {
$caught = $validationException;
}
Expand Down Expand Up @@ -860,7 +860,7 @@ public function __toString(): string
$caught = null;
try {
$factory = resolve(Factory::class);
(fn () => $this->createDefaultValidator($factory))->call($formRequest);
defaultValidatorFor($formRequest, $factory);
} catch (ValidationException $validationException) {
$caught = $validationException;
}
Expand Down Expand Up @@ -900,7 +900,7 @@ public function __toString(): string
$caught = null;
try {
$factory = resolve(Factory::class);
(fn () => $this->createDefaultValidator($factory))->call($formRequest);
defaultValidatorFor($formRequest, $factory);
} catch (ValidationException $validationException) {
$caught = $validationException;
}
Expand Down Expand Up @@ -1039,7 +1039,7 @@ protected function failedValidation(Validator $validator): void
$caught = null;
try {
$factory = resolve(Factory::class);
(fn () => $this->createDefaultValidator($factory))->call($formRequest);
defaultValidatorFor($formRequest, $factory);
} catch (ValidationException $validationException) {
$caught = $validationException;
}
Expand Down Expand Up @@ -1202,7 +1202,7 @@ protected function failedValidation(Validator $validator): void
$caught = null;
try {
$factory = resolve(Factory::class);
(fn () => $this->createDefaultValidator($factory))->call($formRequest);
defaultValidatorFor($formRequest, $factory);
} catch (ValidationException $e) {
$caught = $e;
}
Expand Down
4 changes: 1 addition & 3 deletions tests/Exceptions/UnknownFluentRuleMethodTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@
})->throws(UnknownFluentRuleMethod::class);

it('extends BadMethodCallException so existing catches keep working', function (): void {
$caught = null;

try {
throw UnknownFluentRuleMethod::on('min');
} catch (BadMethodCallException $badMethodCallException) {
$caught = $badMethodCallException;
}

expect($caught)->not->toBeNull();
expect($caught)->toBeInstanceOf(UnknownFluentRuleMethod::class);
});

it('names the called method in the message', function (): void {
Expand Down
14 changes: 7 additions & 7 deletions tests/ExpandsWildcardsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

expect($validator->passes())->toBeTrue()
->and($validator->validated())->toHaveKeys(['items']);
Expand All @@ -49,7 +49,7 @@
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

expect($validator->passes())->toBeFalse()
->and($validator->errors()->keys())->toContain('items.0.name');
Expand All @@ -72,7 +72,7 @@
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

return response()->json($validator->validated());
});
Expand Down Expand Up @@ -100,10 +100,10 @@
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

if ($validator->fails()) {
throw new ValidationException($validator); // @phpstan-ignore argument.type
throw new ValidationException($validator);
}

return response()->json($validator->validated());
Expand Down Expand Up @@ -136,7 +136,7 @@
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

expect($validator->passes())->toBeTrue()
->and($validator->validated())->toHaveKeys(['title', 'items']);
Expand All @@ -160,7 +160,7 @@
);

$factory = resolve(Factory::class);
$validator = (fn () => $this->createDefaultValidator($factory))->call($formRequest);
$validator = defaultValidatorFor($formRequest, $factory);

expect($validator->passes())->toBeTrue();
});
Loading
Loading