V12__add_guest_order_columns.sql used PostgreSQL partial indexes:
CREATE UNIQUE INDEX ... ON orders(guest_token) WHERE guest_token IS NOT NULL
CREATE INDEX ... ON orders(guest_email) WHERE guest_email IS NOT NULL
H2 (the in-memory DB used by tests/dev, per application.yml) does not support
partial indexes -- the WHERE clause throws JdbcSQLSyntaxErrorException. Flyway
therefore failed to run V12 at Spring context startup, so the ApplicationContext
could not load, failing all 80 @SpringBootTest tests (:backend:test) and
aborting CI before coverage verification ever ran. This was the actual root
cause of the PR's red CI -- not a coverage shortfall.
Verified locally (Temurin JDK 21): ./gradlew :backend:jacocoTestCoverageVerification
now BUILD SUCCESSFUL; all 188 tests pass; bundle coverage 80.8% line / 64.9%
branch (thresholds 70% / 60%).
Semantics preserved: both H2 and PostgreSQL treat NULLs as distinct in a
UNIQUE index, so user-owned orders (NULL guest_token) never collide while
non-NULL guest tokens stay unique -- the same guarantee the partial index
provided, but portable across both databases.
Migration is not yet on master, so editing V12 in this PR is safe (no
checksum mismatch against origin/master).
GuestOrderControllerTest#shouldGetGuestOrderByToken called
Order.setCreatedAt(Instant.parse(...)), but the Order entity has no
setCreatedAt setter — createdAt is assigned only inside @PrePersist
onCreate(). This was a compile error (cannot find symbol) that made
compileTestJava fail, so the jacocoTestCoverageVerification CI step
aborted before running any tests — hence coverage never improved and
CI stayed red after the previous commit (a0cefb2).
Why E2E passed but lint-and-test failed: the e2e backend image builds
with `./gradlew :backend:bootJar`, which compiles only main sources
and never compiles/runs tests. The lint-and-test job runs
`./gradlew :backend:jacocoTestCoverageVerification`, which depends on
test -> compileTestJava, which is where this failed.
Changes:
- Remove the Order.setCreatedAt(Instant) call (no such method).
- Remove the order.setAmountPaid(BigDecimal) setup line.
- Remove the jsonPath $.amountPaid assertion (depended on that setup;
the test still asserts id, plate, status, guestToken).
- Drop the now-unused java.math.BigDecimal and java.time.Instant
imports.
No behavioral change to production code; test-only fix.
Adds an anonymous guest checkout flow so a customer can order a bilhälsning
without creating an account. Payment via Swish (QR + payment link).
Backend:
- GuestOrderController: POST /api/guest-orders (public, no auth)
- CreateGuestOrderRequest / GuestOrderResponse DTOs
- Order entity: guest_email, guest_token (UUID), nullable user_id
- OrderRepository: findByGuestToken, findByGuestEmail
- OrderService: createGuestOrder, getGuestOrder by token
- SecurityConfig: /api/guest-orders/** permitAll
- V12 migration: drops user_id NOT NULL, adds guest_email + guest_token
with partial unique index (backfill-safe for existing user orders)
Frontend:
- GuestCheckoutPage: plate lookup + order form (no login)
- GuestPaymentRedirect: Swish QR + payment link + status polling
- GuestOrderPage: order status by guest token
- guestOrders.ts API client
- router: /guest/* public routes
- vite.config: dev proxy for /api/guest-orders
Verification:
- [x] vue-tsc type-check passes (exit 0)
- [ ] Backend Java compiles (no JDK/docker in agent sandbox)
- [ ] Flyway V12 migration applies cleanly
- [ ] End-to-end POST /api/guest-orders -> 201 -> Swish -> status
Frontend type-checks but backend has NOT been compiled or run yet. This
PR is for review; backend smoke test pending in a docker environment.