fix(db): add CHECK constraint for userId XOR guestToken invariant (#23) #26

Open
hermes wants to merge 1 commit from fix/order-user-guest-check-constraint into master
Showing only changes of commit 928a995298 - Show all commits

View file

@ -0,0 +1,17 @@
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- Enforce the Order entity invariant at the database level: exactly one of
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- (user_id, guest_token) must be set — never both, never neither.
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
--
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- The Order Javadoc states "Either userId or guestToken is set; never both,
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- never neither", but previously only the @PrePersist lifecycle callback
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- enforced this in Java. A stray INSERT (admin tooling, manual SQL) could
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- violate it silently.
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
--
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- The CHECK expression (user_id IS NULL) <> (guest_token IS NULL) evaluates:
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- TRUE when exactly one column is NULL (the other is set) — allowed
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- FALSE when both are NULL or both are set — rejected
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
--
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- Standard SQL CHECK constraint, supported by both H2 (tests/dev) and
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
-- PostgreSQL (prod).
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
ALTER TABLE orders
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
ADD CONSTRAINT chk_user_or_guest
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.
CHECK ((user_id IS NULL) <> (guest_token IS NULL));
Review

💡 Suggestion (non-blocking): worth a @DataJpaTest that asserts this constraint rejects invalid rows — persist an Order with both userId and guestToken set, expecting a DataIntegrityViolationException referencing chk_user_or_guest. The @PrePersist in Order.onCreate() already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.

💡 Suggestion (non-blocking): worth a `@DataJpaTest` that asserts this constraint rejects invalid rows — persist an Order with both `userId` and `guestToken` set, expecting a `DataIntegrityViolationException` referencing `chk_user_or_guest`. The `@PrePersist` in `Order.onCreate()` already prevents the neither-set case at the application layer, so the both-set rejection is the untested, highest-value path.