Skip to content

fix: detect that the signer search has more results - #8286

Merged
vitormattos merged 3 commits into
LibreSign:mainfrom
maia-andre:fix/8053-signer-plugin-has-more
Sep 7, 2026
Merged

fix: detect that the signer search has more results#8286
vitormattos merged 3 commits into
LibreSign:mainfrom
maia-andre:fix/8053-signer-plugin-has-more

Conversation

@maia-andre

Copy link
Copy Markdown
Contributor

Related to: #8053

📝 Summary

Infection on lib/Collaboration/Collaborators/SignerPlugin.php reported 21 mutants, 6 escaped and a Covered MSI of 71%. One of them, > into >= on line 52, pointed at a real problem rather than a missing assertion.

$limit was incremented before the mapper call and the extra row was then looked for beyond that same incremented limit. As IdentifyMethodMapper::searchByIdentifierValue() applies setMaxResults(), it can never return more rows than it was asked for, so count($identifiers) > $limit was never true: the plugin never reported more results and never removed the extra row it had asked for.

What a user sees: a search with more signers than the page can show returns one item too many, and that item appears again at the top of the next page, because the offset is applied in SQL. The flag itself does not reach the API today — IdentifyController::search() destructures [$result] and drops the second element returned by the collaborators search — so the visible part is the extra item and its repetition.

The mapper is still asked for one row beyond the page, and that row is now what tells the plugin there is more to show, in the same way AccountPhonePlugin and ContactPhonePlugin already do it.

The old pagination test could not catch this: it returned 31 rows from a mapper call made with a limit of 26. The scenarios now stay within what the mapper can answer and cover the boundary, where the number of rows found is exactly the size of the page.

This PR also removes canValidateMethod(). It was private and had no caller anywhere in the project, so it never ran and no mutant could reach it.

The remaining escaped mutants were assertions missing from the tests: nothing checked the label and the value offered for a signer, and the search term was never written in a different case than the stored data, so lowercasing it was not covered on either side of the comparison.

before after
Generated mutants 21 22
Killed 15 22
Escaped 6 0
Covered Code MSI 71% 100%

The three commits are meant to be read in order: the fix with its regression test, the removal of the unused method, then the remaining test improvements.

🧪 How to test

composer test:unit -- --filter SignerPluginTest

Expected result:

OK (18 tests, 44 assertions)

To see the bug, check out the first commit and revert only lib/Collaboration/Collaborators/SignerPlugin.php. The one row more than the page can show scenario then fails with:

Failed asserting that false is identical to true.

Infection, scoped to this source file:

composer mutation:test -- \
  lib/Collaboration/Collaborators/SignerPlugin.php \
  --show-mutations \
  --with-uncovered \
  --threads=1

Expected result:

22 mutations were generated:
      22 mutants were killed by Test Framework

Metrics:
         Mutation Score Indicator (MSI): 100%
         Mutation Code Coverage: 100%
         Covered Code MSI: 100%

The classes around this one were run as well: IdentifyControllerTest, ResultFormatterTest, ShareTypeResolverTest and the four collaborator plugins — 82 tests, 215 assertions, all passing. Psalm reports no error on the changed file.

⚙️ API / Back‑end changes

  • The signer search now reports when there are more results than the page can show, and returns the page size instead of one item more.
  • The unused private method SignerPlugin::canValidateMethod() was removed.
  • Unit tests added, including a regression test for the pagination boundary.
  • No API, capabilities or OpenAPI change.

✅ Checklist

  • I have read and followed the contribution guide.
  • php-cs-fixer, php -l and psalm pass on the changed files.

🤖 AI (if applicable)

  • The content of this PR was partially or fully generated using AI

The limit was incremented before the mapper call and the extra row was
then looked for beyond that same incremented limit. As
IdentifyMethodMapper::searchByIdentifierValue() applies setMaxResults(),
it can never return more rows than it was asked for, so the condition was
never true: the plugin never reported more results and never removed the
extra row it had asked for.

A search with more signers than the page could show returned one item too
many, and that item appeared again at the top of the next page, because
the offset is applied in SQL.

The mapper is still asked for one row beyond the page, and that row is
now what tells the plugin there is more to show, in the same way
AccountPhonePlugin and ContactPhonePlugin do it.

The previous pagination test could not catch this: it returned 31 rows
from a mapper call made with a limit of 26. The scenarios now stay within
what the mapper can answer and cover the boundary, where the number of
rows found is exactly the size of the page.

Ref LibreSign#8053

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: André Maia <andrefnkmm@gmail.com>
canValidateMethod() was private and had no caller anywhere in the
project. It never ran, so no test and no mutant could reach it.

Ref LibreSign#8053

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: André Maia <andrefnkmm@gmail.com>
Infection showed that nothing asserted the label and the value offered
for a signer, so the display name and the identifier could be dropped
from the result item without any test noticing.

It also showed that the search term itself was never written in a
different case than the stored data, so lowercasing it was not covered on
either side of the comparison. The scenarios now cover an identifier
stored in another case, a search typed in another case, and a match by
display name where both are written differently.

Ref LibreSign#8053

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: André Maia <andrefnkmm@gmail.com>
@github-project-automation github-project-automation Bot moved this from 0. Backlog to 1. to do in Roadmap Sep 7, 2026
@vitormattos

Copy link
Copy Markdown
Member

/backport to stable35

@vitormattos

Copy link
Copy Markdown
Member

/backport to stable34

@vitormattos

Copy link
Copy Markdown
Member

/backport to stable33

@vitormattos

Copy link
Copy Markdown
Member

/backport to stable32

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 4. to release

Development

Successfully merging this pull request may close these issues.

2 participants