Skip to content

fix: pluralize item suffix in ComplyWith failure messages#951

Merged
vbreuss merged 2 commits into
mainfrom
topic/pluralize-item-suffix-in-complywith
May 16, 2026
Merged

fix: pluralize item suffix in ComplyWith failure messages#951
vbreuss merged 2 commits into
mainfrom
topic/pluralize-item-suffix-in-complywith

Conversation

@vbreuss
Copy link
Copy Markdown
Member

@vbreuss vbreuss commented May 16, 2026

The ComplyWith constraints hardcoded items (always plural), producing ungrammatical output like for exactly one items. Route the suffix through EnumerableQuantifier.GetItemString() like the other collection constraints, so singular quantifiers (Exactly(1), AtLeast(1), AtMost(1)) render item instead.

Also adds verification tests for the Exactly(n).ComplyWith chain and the AtLeast(n).ComplyWith chain, which previously had no coverage.

The ComplyWith constraints hardcoded ` items` (always plural), producing
ungrammatical output like `for exactly one items`. Route the suffix
through EnumerableQuantifier.GetItemString() like the other collection
constraints, so singular quantifiers (Exactly(1), AtLeast(1), AtMost(1))
render `item` instead.

Also adds verification tests for the Exactly(n).ComplyWith chain and the
AtLeast(n).ComplyWith chain, which previously had no coverage.
@vbreuss vbreuss self-assigned this May 16, 2026
Copilot AI review requested due to automatic review settings May 16, 2026 16:39
@vbreuss vbreuss enabled auto-merge (squash) May 16, 2026 16:40
@vbreuss vbreuss added the bug Something isn't working label May 16, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes ungrammatical ComplyWith failure messages (e.g., “for exactly one items”) by routing the item suffix through EnumerableQuantifier.GetItemString() so singular quantifiers render item instead of items.

Changes:

  • Updated ThatEnumerable.Elements.ComplyWith and ThatAsyncEnumerable.Elements.ComplyWith expectation formatting to use GetItemString() for item/items.
  • Removed the now-unneeded ComplyItems constant from ThatEnumerable.
  • Added new unit tests for Exactly(n).ComplyWith(...) and AtLeast(n).ComplyWith(...) chains.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
Tests/aweXpect.Tests/Collections/ThatEnumerable.Exactly.ComplyWith.Tests.cs Adds coverage for Exactly(...).ComplyWith(...) and AtLeast(...).ComplyWith(...) scenarios (including a singular-message assertion for Exactly(1)).
Source/aweXpect/That/Collections/ThatEnumerable.Elements.ComplyWith.cs Uses EnumerableQuantifier.GetItemString() instead of hardcoded " items" in expectation text.
Source/aweXpect/That/Collections/ThatEnumerable.cs Removes the obsolete ComplyItems constant.
Source/aweXpect/That/Collections/ThatAsyncEnumerable.Elements.ComplyWith.cs Aligns async ComplyWith expectation text with the singular/plural logic via GetItemString().

Comment thread Tests/aweXpect.Tests/Collections/ThatEnumerable.Exactly.ComplyWith.Tests.cs Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 16, 2026

Test Results

     23 files   - 27       23 suites   - 27   7m 49s ⏱️ -4s
 19 783 tests  -  8   19 782 ✅  -  8  1 💤 ±0  0 ❌ ±0 
102 230 runs   - 39  102 229 ✅  - 39  1 💤 ±0  0 ❌ ±0 

Results for commit 3f0d619. ± Comparison against base commit 16e2c24.

This pull request removes 3166 and adds 3158 tests. Note that renamed tests count towards both.
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message025bc855-9966-49c5-b0a0-aadecd46570c")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message27e518fe-076f-4524-982e-7be397bc9be0")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message6c7cd426-0194-43a1-abc9-bbab4e17334f")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message8aad0152-bdfa-49fb-a84e-53d7d75b480c")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messageb5cebd3d-7893-4e56-8259-2c14ec4b4b96")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messagefcdd9789-fcb0-4a7e-a44b-b7aa6c15dd16")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message3f0f7626-d166-4785-9a08-6ff6a0aa0cce")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message3fff71db-c16a-4986-b2a4-f7d62d8823b4")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message4869a762-b783-48c3-8215-3a95ea7dbeb0")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message733961eb-98d8-4e4c-997e-4b8cd05ba8af")
…
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message3b389054-a7af-4041-8810-d02816778da2")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message4259f5c9-4172-4974-8686-1164f09aed7c")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message60c52663-14bd-48b5-b649-9555a6491d07")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message88f72555-fdd8-41c4-99d7-681a74c2ea36")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messagec6571064-1a83-4be7-9e08-7967c5b4d810")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messageee41026a-070c-4ba5-bd26-9766f35c5f82")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message10dc5224-6ac0-44bb-a161-33289d1bda2c")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message79273058-6dc8-4784-ad63-5c44ca6a02c9")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message7d71e897-11bf-488a-81a4-99f8cdd047de")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message7e5fc79e-f42d-4ea2-8548-a9ac1fbd6be7")
…

♻️ This comment has been updated with latest results.

@vbreuss vbreuss disabled auto-merge May 16, 2026 16:54
@vbreuss vbreuss enabled auto-merge (squash) May 16, 2026 16:56
@vbreuss vbreuss changed the title fix: pluralize item suffix in ComplyWith failure messages fix: pluralize item suffix in ComplyWith failure messages May 16, 2026
@vbreuss vbreuss disabled auto-merge May 16, 2026 16:57
@vbreuss vbreuss enabled auto-merge (squash) May 16, 2026 16:58
@sonarqubecloud
Copy link
Copy Markdown

@vbreuss vbreuss merged commit 0d5b1a9 into main May 16, 2026
13 checks passed
@vbreuss vbreuss deleted the topic/pluralize-item-suffix-in-complywith branch May 16, 2026 17:04
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 16, 2026

🚀 Benchmark Results

Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.300
[Host] : .NET 8.0.27 (8.0.27, 8.0.2726.22922), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Method Mean Error StdDev Gen0 Gen1 Allocated
Bool_aweXpect 273.2 ns 3.62 ns 3.38 ns 0.0415 - 696 B
Bool_FluentAssertions 260.1 ns 6.02 ns 5.63 ns 0.0567 - 952 B
Equivalency_aweXpect 327,098.0 ns 1,099.76 ns 1,028.71 ns 20.0195 0.4883 335444 B
Equivalency_FluentAssertions 2,738,181.5 ns 40,096.07 ns 37,505.89 ns 285.1563 46.8750 4804906 B
Int_GreaterThan_aweXpect 302.8 ns 3.11 ns 2.90 ns 0.0515 - 864 B
Int_GreaterThan_FluentAssertions 266.9 ns 10.73 ns 10.04 ns 0.0730 - 1224 B
ItemsCount_AtLeast_aweXpect 488.1 ns 10.78 ns 10.08 ns 0.0811 - 1360 B
ItemsCount_AtLeast_FluentAssertions 519.8 ns 11.44 ns 10.70 ns 0.1192 - 2008 B
String_aweXpect 470.8 ns 6.89 ns 6.44 ns 0.0668 - 1128 B
String_FluentAssertions 1,194.6 ns 31.35 ns 29.32 ns 0.2346 - 3944 B
StringArray_aweXpect 1,931.6 ns 17.83 ns 14.89 ns 0.1564 - 2624 B
StringArray_FluentAssertions 1,246.6 ns 7.28 ns 6.81 ns 0.2480 - 4152 B
StringArrayInAnyOrder_aweXpect 2,477.2 ns 10.01 ns 9.36 ns 0.1678 - 2816 B
StringArrayInAnyOrder_FluentAssertions 89,643.6 ns 742.61 ns 694.64 ns 3.4180 - 57481 B

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 16, 2026

👽 Mutation Results

Mutation testing badge

aweXpect

Details
File Score Killed Survived Timeout No Coverage Ignored Compile Errors Total Detected Total Undetected Total Mutants
That/Collections/ThatAsyncEnumerable.Elements.ComplyWith.cs 62.50% 17 3 3 9 13 8 20 12 53
That/Collections/ThatEnumerable.cs 82.49% 310 9 1 57 113 262 311 66 752
That/Collections/ThatEnumerable.Elements.ComplyWith.cs 80.26% 60 3 1 12 21 24 61 15 121

The final mutation score is 80.82%

Coverage Thresholds: high:80 low:60 break:0

aweXpect.Core

Details
File Score Killed Survived Timeout No Coverage Ignored Compile Errors Total Detected Total Undetected Total Mutants

The final mutation score is NaN%

Coverage Thresholds: high:80 low:60 break:0

github-actions Bot added a commit that referenced this pull request May 16, 2026
github-actions Bot added a commit that referenced this pull request May 16, 2026
@github-actions
Copy link
Copy Markdown
Contributor

This is addressed in release v2.34.0.

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

Labels

bug Something isn't working state: released The issue is released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants