Skip to content

Add ProductController characterization tests; dedup Vendor access checks - #785

Merged
KrzysztofPajak merged 3 commits into
developfrom
product-controller-dedup-tests
Aug 16, 2026
Merged

Add ProductController characterization tests; dedup Vendor access checks#785
KrzysztofPajak merged 3 commits into
developfrom
product-controller-dedup-tests

Conversation

@KrzysztofPajak

@KrzysztofPajak KrzysztofPajak commented Aug 16, 2026

Copy link
Copy Markdown
Member

Summary

First step of a planned consolidation of the near-duplicate ProductController/ProductViewModelService copies in Grand.Web.Admin / Grand.Web.Store / Grand.Web.Vendor (see .ai/workflows/refactor-safely.md Phase 2/3/4).

  • Safety net (Phase 2): characterization tests for the tenant-isolation checks in ProductController.Edit/Delete across all three areas, plus the vendor-specific parts of the forked ProductViewModelService. Grand.Web.Vendor.Tests is a new project - none existed before.
  • Refactor (Phase 4): Vendor's Edit(GET)/Edit(POST)/Delete now use the existing (previously unused by these three actions, but used by the other 27 actions in the file) CheckAccessToProduct helper instead of reimplementing the check inline. No behavior change - verified by the tests above.
  • Security fix: Grand.Web.Vendor.ProductViewModelService.InsertSimilarProductModel/InsertBundleProductModel checked HasAccessToProduct against the product already being edited (always the vendor's own) instead of the candidate being linked in, so the check was a no-op - any vendor could attach another vendor's product to their own listing as "similar" or "bundle". Fixed to check the candidate, matching the sibling InsertRelatedProductModel which already had this right.

Left alone (documented, not addressed here)

  • Store's equivalent duplication (AccessToEntityByStore inlined ~30 times in Grand.Web.Store/ProductController.cs) - no existing helper to consolidate onto, and only 2 of those sites are currently test-covered. Needs broader test coverage first.

Testing

  • dotnet test src/Tests/Grand.Web.Vendor.Tests -> 12/12
  • dotnet test src/Tests/Grand.Web.Store.Tests -> 26/26
  • dotnet test src/Tests/Grand.Web.Admin.Tests -> 61/61

🤖 Generated with Claude Code

KrzysztofPajak and others added 2 commits August 16, 2026 12:04
Ahead of consolidating the duplicated ProductController/ProductViewModelService
copies in Grand.Web.Admin/Store/Vendor, add a safety net covering the
tenant-isolation behavior each area implements differently:

- New Grand.Web.Vendor.Tests project (none existed before) with tests for
  ProductController.Delete/Edit access denial, and for the vendor-specific
  parts of ProductViewModelService (VendorId assignment, vendor-scoped
  search, HasAccessToProduct filtering).
- Grand.Web.Store.Tests: Delete/Edit(POST) store-scoping checks, including
  the counter-intuitive AccessToEntityByStore semantics (a product not
  limited to any store is denied, not allowed), and Edit(GET)'s separate
  permissive "show with warning" path that must not be folded into the
  same check as Delete/Edit(POST).
- Grand.Web.Admin.Tests: baseline confirming Admin performs no ownership
  check at all.

Also documents, without fixing, a suspected authorization bug in
Grand.Web.Vendor ProductViewModelService.InsertSimilarProductModel /
InsertBundleProductModel: the HasAccessToProduct check runs against the
product already being edited (always the vendor's own) instead of the
candidate product being linked in, so it never actually excludes another
vendor's product.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Edit(GET), Edit(POST) and Delete reimplemented the tenant-isolation check
inline (product == null || !HasAccessToProduct(product)) instead of using
the existing CheckAccessToProduct helper already used consistently by the
other 27 actions in this controller. No behavior change: both denial
reasons ("product not found" / "not your product") still redirect to
List, as verified by Grand.Web.Vendor.Tests (10/10 green, unmodified).

Covers only the sites already backed by a characterization test. Store's
equivalent duplication (AccessToEntityByStore inlined ~30 times) is left
alone - it has no existing helper to consolidate onto, and only 2 of
those sites are test-covered, so a partial extraction there would trade
one inconsistency for another.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 16, 2026 10:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…ndleProductModel

Both methods checked HasAccessToProduct(productId1) - the product already
being edited, which the vendor is guaranteed to own - instead of
HasAccessToProduct(product), the candidate being linked in via the loop
variable `id`. The check was therefore always true and never actually
excluded another vendor's product from being linked as a similar or
bundle product, letting a vendor attach a competitor's product to their
own listing.

Now checks the candidate, matching the sibling InsertRelatedProductModel
which already had this right.

Grand.Web.Vendor.Tests: 12/12 green (2 new regression tests + 1 existing
test flipped from documenting the bug to asserting the fix).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread src/Tests/Grand.Web.Admin.Tests/Controllers/ProductControllerTests.cs Dismissed
Comment thread src/Tests/Grand.Web.Admin.Tests/Controllers/ProductControllerTests.cs Dismissed
Comment thread src/Tests/Grand.Web.Store.Tests/Controllers/ProductControllerTests.cs Dismissed
Comment thread src/Tests/Grand.Web.Store.Tests/Controllers/ProductControllerTests.cs Dismissed
Comment thread src/Tests/Grand.Web.Store.Tests/Controllers/ProductControllerTests.cs Dismissed
Comment thread src/Tests/Grand.Web.Store.Tests/Controllers/ProductControllerTests.cs Dismissed
Comment thread src/Tests/Grand.Web.Store.Tests/Controllers/ProductControllerTests.cs Dismissed
Comment thread src/Tests/Grand.Web.Store.Tests/Controllers/ProductControllerTests.cs Dismissed
Comment thread src/Tests/Grand.Web.Store.Tests/Controllers/ProductControllerTests.cs Dismissed
Comment thread src/Tests/Grand.Web.Vendor.Tests/Controllers/ProductControllerTests.cs Dismissed
@KrzysztofPajak
KrzysztofPajak merged commit bb83f0b into develop Aug 16, 2026
4 of 5 checks passed
@KrzysztofPajak
KrzysztofPajak deleted the product-controller-dedup-tests branch August 16, 2026 10:18
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.

2 participants