Skip to content

feat: Fase 5 F5 status — Category Management: BROWSER-VERIFIED#129

Merged
edsonwade merged 1 commit into
mainfrom
develop
Jul 14, 2026
Merged

feat: Fase 5 F5 status — Category Management: BROWSER-VERIFIED#129
edsonwade merged 1 commit into
mainfrom
develop

Conversation

@edsonwade

Copy link
Copy Markdown
Owner

Root cause: updateStatusManually branches on the tenant:

Order order = (TenantContext.isPresent()
        ? orderRepository.findByOrderIdAndTenantId(orderId, TenantContext.getCurrentTenantId())
        : orderRepository.findById(orderId))

My step defs only stubbed findById(...). But Cucumber instantiates every glue class in the package per scenario, and OrderStepDefinitions sets TenantContext.setCurrentTenantId("test-tenant") — so a tenant is bound during my scenarios. The code therefore took the findByOrderIdAndTenantId(...) branch, which was unstubbed → Mockito returned Optional.empty()OrderNotFoundException. The unit test passed only because it never binds a tenant (coincidence, not correctness).

Let me save the mandatory rule first, then fix.

Rule saved. Now the fix — stub both branches so the test is deterministic regardless of ambient tenant state:

Per the rule I just saved — verifying the real repository signature rather than assuming it:

Signature confirmed (Optional<Order> findByOrderIdAndTenantId(Integer, String)). Now applying the same lesson to my other tests — the unit test relies on TenantContext being unset, which is a ThreadLocal that another test class could leak into it. Making it deterministic:

Now a second latent bug in my integration test — I set the Kafka property via DynamicPropertySource with a literal ${...} placeholder, instead of the proven @SpringBootTest(properties=...) pattern. Let me match the working OrderSagaIntegrationTest exactly.

Both bugs fixed. Now Task 5.3 (frontend) — writes are working again. Starting with the types:

Now the seller Orders page — status column + fulfillment actions:

Now constants (route + query key):

Now the admin orders page:

Now wire the route and nav. Let me read the admin routes block.

Applying the no-assumption rule to the FE too — verifying the MUI icons actually exist (this is exactly what bit F3 with PauseCircleOutline):

All three icons exist. Now verifying the DataTable contract (Column export, optional onView) so I'm not assuming:

Everything verified against the real code. Now the doc/memory closeout. Plan Task 5.3:

Now the memory closeout for F5:

Fixed, and the rule is saved

The mandatory rule is now in memory (feedback_verify_impl_before_tests_no_hallucination.md, rule 7): after implementing a feature, RE-READ that exact method before writing its tests — enumerate every branch, every collaborator call, and every ambient/implicit input (TenantContext, SecurityContextHolder, clocks, headers), then stub all of them. It explicitly covers code I just wrote myself, since that's where I got complacent.

The bug (my fault, exactly as you said)

updateStatusManually chooses its repository call from the ambient tenant:

TenantContext.isPresent() ? findByOrderIdAndTenantId(id, tenant) : findById(id)

I stubbed only findById. Cucumber instantiates every glue class in the package per scenario, so OrderStepDefinitions bound a TenantContext on the thread → the tenant branch ran → unstubbed → Optional.empty()OrderNotFoundException × 4.

Fixes applied

  1. OrderFulfillmentStepDefinitions.java — stub both branches (findByOrderIdAndTenantId(eq(ID), anyString()) + findById), deterministic regardless of hook order.
  2. OrderServiceManualStatusTest.java — clear TenantContext/SecurityContextHolder in setUp. It was passing by luck, not correctness.
  3. OrderFulfillmentIntegrationTest.java — I'd also passed the EmbeddedKafka broker as a literal ${...} placeholder via DynamicPropertySource; moved to @SpringBootTest(properties=…) matching the proven saga test. That one would have failed too once surefire let failsafe run — caught it by re-reading against the working example rather than waiting for you to hit it.

I verified the real signature (Optional<Order> findByOrderIdAndTenantId(Integer, String)) instead of assuming it.

Task 5.3 — frontend (done)

File Change
types.ts OrderStatus +SHIPPED/DELIVERED/REFUNDED; new FulfillmentStatus
OrderStatusBadge.tsx +3 configs (compile-enforced by the Record<OrderStatus,…>)
orders.api.ts +updateStatus(orderId, status)
OrderManagement.tsx Status column + offers only the legal next step + ConfirmDialog
AdminOrdersPage.tsx New — uses the previously-unused ordersApi.getAll; admin advances any order
routes/constants/AdminLayout /admin/orders route + Receipt nav item

I verified LocalShipping/TaskAlt/Receipt actually exist in @mui/icons-material (the F3 *Outline*Outlined trap) and checked DataTable/ConfirmDialog prop contracts against their real source.

One intentional deviation: the plan wanted i18n status labels, but OrderStatusBadge is a shared hardcoded-English WORKING_STATE component — I extended it in its existing style rather than refactor it. Tell me if you want the full i18n pass.

You run

mvn verify -pl order-service
cd frontend && npm run build && npm run lint

Then rebuild order-service (Flyway V1_14) + frontend. Plan checkboxes, API.md, and memory are all updated.

You're right, and I own this. The bug is exactly the failure mode you've warned me about: I wrote the BDD steps against an **assumption** about my own implementation instead of reading it.

Root cause: updateStatusManually branches on the tenant:

Order order = (TenantContext.isPresent()
        ? orderRepository.findByOrderIdAndTenantId(orderId, TenantContext.getCurrentTenantId())
        : orderRepository.findById(orderId))

My step defs only stubbed findById(...). But Cucumber instantiates every glue class in the package per scenario, and OrderStepDefinitions sets TenantContext.setCurrentTenantId("test-tenant") — so a tenant is bound during my scenarios. The code therefore took the findByOrderIdAndTenantId(...) branch, which was unstubbed → Mockito returned Optional.empty()OrderNotFoundException. The unit test passed only because it never binds a tenant (coincidence, not correctness).

Let me save the mandatory rule first, then fix.

Rule saved. Now the fix — stub both branches so the test is deterministic regardless of ambient tenant state:

Per the rule I just saved — verifying the real repository signature rather than assuming it:

Signature confirmed (Optional<Order> findByOrderIdAndTenantId(Integer, String)). Now applying the same lesson to my other tests — the unit test relies on TenantContext being unset, which is a ThreadLocal that another test class could leak into it. Making it deterministic:

Now a second latent bug in my integration test — I set the Kafka property via DynamicPropertySource with a literal ${...} placeholder, instead of the proven @SpringBootTest(properties=...) pattern. Let me match the working OrderSagaIntegrationTest exactly.

Both bugs fixed. Now Task 5.3 (frontend) — writes are working again. Starting with the types:

Now the seller Orders page — status column + fulfillment actions:

Now constants (route + query key):

Now the admin orders page:

Now wire the route and nav. Let me read the admin routes block.

Applying the no-assumption rule to the FE too — verifying the MUI icons actually exist (this is exactly what bit F3 with PauseCircleOutline):

All three icons exist. Now verifying the DataTable contract (Column export, optional onView) so I'm not assuming:

Everything verified against the real code. Now the doc/memory closeout. Plan Task 5.3:

Now the memory closeout for F5:

Fixed, and the rule is saved

The mandatory rule is now in memory ([feedback_verify_impl_before_tests_no_hallucination.md](../../C--Users-HP--claude-projects-e-commerce-microservice/memory/feedback_verify_impl_before_tests_no_hallucination.md), rule 7): after implementing a feature, RE-READ that exact method before writing its tests — enumerate every branch, every collaborator call, and every ambient/implicit input (TenantContext, SecurityContextHolder, clocks, headers), then stub all of them. It explicitly covers code I just wrote myself, since that's where I got complacent.

The bug (my fault, exactly as you said)

updateStatusManually chooses its repository call from the ambient tenant:

TenantContext.isPresent() ? findByOrderIdAndTenantId(id, tenant) : findById(id)

I stubbed only findById. Cucumber instantiates every glue class in the package per scenario, so OrderStepDefinitions bound a TenantContext on the thread → the tenant branch ran → unstubbed → Optional.empty()OrderNotFoundException × 4.

Fixes applied

  1. [OrderFulfillmentStepDefinitions.java](order-service/src/test/java/code/with/vanilson/orderservice/bdd/OrderFulfillmentStepDefinitions.java) — stub both branches (findByOrderIdAndTenantId(eq(ID), anyString()) + findById), deterministic regardless of hook order.
  2. [OrderServiceManualStatusTest.java](order-service/src/test/java/code/with/vanilson/orderservice/OrderServiceManualStatusTest.java) — clear TenantContext/SecurityContextHolder in setUp. It was passing by luck, not correctness.
  3. [OrderFulfillmentIntegrationTest.java](order-service/src/test/java/code/with/vanilson/orderservice/integration/OrderFulfillmentIntegrationTest.java) — I'd also passed the EmbeddedKafka broker as a literal ${...} placeholder via DynamicPropertySource; moved to @SpringBootTest(properties=…) matching the proven saga test. That one would have failed too once surefire let failsafe run — caught it by re-reading against the working example rather than waiting for you to hit it.

I verified the real signature (Optional<Order> findByOrderIdAndTenantId(Integer, String)) instead of assuming it.

Task 5.3 — frontend (done)

File Change
[types.ts](frontend/src/api/types.ts) OrderStatus +SHIPPED/DELIVERED/REFUNDED; new FulfillmentStatus
[OrderStatusBadge.tsx](frontend/src/components/order/OrderStatusBadge.tsx) +3 configs (compile-enforced by the Record<OrderStatus,…>)
[orders.api.ts](frontend/src/api/orders.api.ts) +updateStatus(orderId, status)
[OrderManagement.tsx](frontend/src/pages/seller/OrderManagement.tsx) Status column + offers only the legal next step + ConfirmDialog
[AdminOrdersPage.tsx](frontend/src/pages/admin/AdminOrdersPage.tsx) New — uses the previously-unused ordersApi.getAll; admin advances any order
routes/constants/AdminLayout /admin/orders route + Receipt nav item

I verified LocalShipping/TaskAlt/Receipt actually exist in @mui/icons-material (the F3 *Outline*Outlined trap) and checked DataTable/ConfirmDialog prop contracts against their real source.

One intentional deviation: the plan wanted i18n status labels, but OrderStatusBadge is a shared hardcoded-English WORKING_STATE component — I extended it in its existing style rather than refactor it. Tell me if you want the full i18n pass.

You run

mvn verify -pl order-service
cd frontend && npm run build && npm run lint

Then rebuild order-service (Flyway V1_14) + frontend. Plan checkboxes, API.md, and memory are all updated.

@edsonwade edsonwade self-assigned this Jul 14, 2026
@edsonwade edsonwade added bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request dependencies Pull requests that update a dependency file labels Jul 14, 2026
@edsonwade
edsonwade merged commit 8e2e064 into main Jul 14, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant