Skip to content

fix: two silent setters, and the warnings that were pointing at them - #3354

Merged
rubenvdlinde merged 1 commit into
developmentfrom
fix/suite-warnings-and-deprecations
Sep 3, 2026
Merged

fix: two silent setters, and the warnings that were pointing at them#3354
rubenvdlinde merged 1 commit into
developmentfrom
fix/suite-warnings-and-deprecations

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

The suite reported 10 PHP warnings and 23 deprecations. Two of the warnings were not hygiene — they were production bugs, and both had been written into the tests as known behaviour.

Organisation::setActive(false) never deactivated anything

It called parent::setActive(active: $value) with a named argument. Entity has no such method: it is reached through __call(array $args), where a named argument lands under its name, so Entity::setter() read an undefined $args[0] and the value never arrived. OrganisationController has an endpoint whose whole job is deactivating an organisation, and it could not work.

OrganisationTest asserted assertTrue() on a value that should have been false, and called it "the current actual behavior".

EndpointLog::setResponse() stored NULL on every call log

Same root cause, same shape. setStatusCode and setStatusMessage were positional and worked; the response payload was dropped on every endpoint call ever logged.

EndpointServiceTest said "setResponse uses named arg in source code (known issue), so response may be null. We verify it was attempted" — and then verified nothing.

Both fixed, both now assert correct behaviour. Negative controls run, not assumed: reintroducing either bug reddens its test.

The class, not just the instances

94 Entity subclasses, 686 magic setter names, 256 named-argument call sites in lib/. Every other match is legitimate: $objectService->setRegister() is a real service method, ->setObject(type:, id:) is OCPs INotification, and my last two candidates were the regex matching self::` in a class constant. The class is bounded at these two sites.

Worth knowing for next time: the cause surfaced as Undefined array key 0 pointing into vendor/nextcloud/ocp/.../Entity.php, where it reads as somebody else`s problem. It was ours both times.

The rest

Fix Why
SettingsController while ($row !== false) never terminates if fetch() answers null, which it can. Two other loops in the same file already used is_array().
BsnFormat str_pad() was handed a null — deprecated in 8.1, a TypeError in 9. Cast once up front; array/object now refused explicitly, which is the contract str_pad() used to enforce further down by accident.
SaveObject A schema with a null slug cannot be found BY slug, and feeding the null to strtolower()/strcasecmp() is deprecated. Skipped instead.
5 test files Mocks answering [] or null where the single real implementation always fills the contract (rechainAll, processSourceChunks, handlePreValidationCascading). The mocks were made to keep the contract rather than the services made defensive about a fake.
RechainAuditTrailCommandTest Gained the tombstone-count assertion its output message never had.
Dynamic properties / setValue() Declared; and ReflectionProperty::setValue() given its explicit null object argument at 14 sites.

Result

19046 tests. Warnings 10 → 0, deprecations 23 → 2, risky 3 → 0 (the risky three landed separately in #3351).

The last two deprecations are ZipArchive::open() on an empty fixture file, left alone deliberately: the fixture is empty on purpose and changing it would change what the test exercises.

🤖 Generated with Claude Code

The suite reported 10 PHP warnings and 23 deprecations. Two of the warnings
were not hygiene: they were production bugs, and both had been written into
the tests as known behaviour.

ORGANISATION::SETACTIVE(FALSE) NEVER DEACTIVATED ANYTHING

It called parent::setActive(active: $value) with a NAMED argument. Entity has
no such method — it is reached through __call(array $args), where a named
argument lands under its name, so Entity::setter() read an undefined $args[0]
and the value never arrived. OrganisationController has an endpoint whose
whole job is deactivating an organisation, and it could not work.

OrganisationTest asserted assertTrue() on a value that should have been false
and called it "the current actual behavior".

ENDPOINTLOG::SETRESPONSE() STORED NULL ON EVERY CALL LOG

Same root cause, same shape. setStatusCode and setStatusMessage were
positional and worked; the response payload was dropped on every endpoint
call ever logged.

EndpointServiceTest said "setResponse uses named arg in source code (known
issue), so response may be null. We verify it was attempted" — and then
verified nothing.

Both are fixed and both now assert the correct behaviour. Reintroducing
either bug reddens its test; that was checked, not assumed.

I swept for the rest of the class rather than stopping at two: 94 Entity
subclasses, 686 magic setter names, 256 named-argument call sites in lib/.
Every other match is legitimate — $objectService->setRegister() is a real
service method, ->setObject(type:, id:) is OCP's INotification, and the last
two candidates were the regex matching self:: in a class constant. The class
is bounded at these two.

THE REST

- SettingsController: `while ($row !== false)` never terminates if fetch()
  answers null, which it can. Two other loops in the same file already used
  is_array(); this one did not.
- BsnFormat: str_pad() was handed a null, deprecated in 8.1 and a TypeError
  in 9. Cast once up front; an array/object is now refused explicitly, which
  is the contract str_pad() used to enforce further down by accident.
- SaveObject: a schema with a null slug cannot be found BY slug, and feeding
  the null to strtolower()/strcasecmp() is deprecated. Skipped instead.
- Four test files: mocks that answered [] or null where the single real
  implementation always fills the contract (rechainAll, processSourceChunks,
  handlePreValidationCascading). The mocks were made to keep the contract
  rather than the services made defensive about a fake. RechainAuditTrail
  also gained the tombstone-count assertion its message never had.
- Dynamic properties declared; ReflectionProperty::setValue() given its
  explicit null object argument at 14 sites.

Suite: 19046 tests. Warnings 10 -> 0, deprecations 23 -> 2, risky 3 -> 0
(the risky three landed separately in #3351). The last two deprecations are
ZipArchive::open() on an empty fixture file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ 15c847d

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-specs
test-l10n
test-l10n-parity
format
check-schema-l10n
check-l10n-js
composer ✅ 174/174
npm ✅ 543/543
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development
Hydra gates

Quality workflow — 2026-09-03 08:10 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 2accc47 into development Sep 3, 2026
47 checks passed
@rubenvdlinde
rubenvdlinde deleted the fix/suite-warnings-and-deprecations branch September 3, 2026 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant