test(account): cover missing picker business date

This commit is contained in:
sechmachine
2026-08-15 14:36:11 +07:00
parent e70159a81b
commit 432389220f
2 changed files with 74 additions and 2 deletions
@@ -3,8 +3,8 @@
- **Test type:** Integration - **Test type:** Integration
- **Requirement IDs:** ACC-014, ACC-019ACC-021, AUTH-001, PRJ-017, TST-001TST-010 - **Requirement IDs:** ACC-014, ACC-019ACC-021, AUTH-001, PRJ-017, TST-001TST-010
- **Scenario IDs:** AC-ACC-009, AC-ACC-010, AC-PRJ-010 (selection-eligibility support) - **Scenario IDs:** AC-ACC-009, AC-ACC-010, AC-PRJ-010 (selection-eligibility support)
- **Test class/method:** com.lab.labtimesheet.feature.account.service.EligibleInternOptionIntegrationTest#listsOnlyActiveInternsWithActiveInclusiveInternshipsInPickerOrder - **Test class/method:** com.lab.labtimesheet.feature.account.service.EligibleInternOptionIntegrationTest#listsOnlyActiveInternsWithActiveInclusiveInternshipsInPickerOrder; #rejectsMissingBusinessDate
- **Implementation commit:** Pending - **Implementation commit:** e70159a81b6445825f6d5f912ecf3c4aa3c1aa85
## Protected behavior ## Protected behavior
@@ -12,6 +12,8 @@ Pending, locked, deactivated, non-Intern, not-started, completed, and date-expir
Account-owned Intern picker. An option is selectable only when both account and internship are ACTIVE and the Account-owned Intern picker. An option is selectable only when both account and internship are ACTIVE and the
explicit business date lies within the inclusive internship range. The returned numeric user ID is the internal explicit business date lies within the inclusive internship range. The returned numeric user ID is the internal
submission identity, and options sort by display name then student code. submission identity, and options sort by display name then student code.
The public query rejects a missing business date with the documented actionable message instead of issuing an
ambiguous null-bound database query.
## Test method ## Test method
@@ -19,12 +21,16 @@ The PostgreSQL 18.4 integration test persists valid account/profile combinations
entities and repositories. It uses SQL only as a test fixture for future lock, deactivation, and completion states entities and repositories. It uses SQL only as a test fixture for future lock, deactivation, and completion states
whose production transitions are outside this change. It calls the public Account service query and compares the whose production transitions are outside this change. It calls the public Account service query and compares the
complete immutable DTO sequence, including both inclusive date boundaries and unique user IDs. complete immutable DTO sequence, including both inclusive date boundaries and unique user IDs.
Its separate null-date regression calls the same public service method and asserts the exact
<code>IllegalArgumentException</code> message documented by that method.
## Hand-derived expected result ## Hand-derived expected result
For business date 2026-08-14, profiles starting on that date and ending on that date remain eligible. The only For business date 2026-08-14, profiles starting on that date and ending on that date remain eligible. The only
expected options are Alpha / STU-100, Alpha / STU-200, and Zeta / STU-300, in that order. Every other seeded expected options are Alpha / STU-100, Alpha / STU-200, and Zeta / STU-300, in that order. Every other seeded
row fails at least one account role/state, internship state, or inclusive date condition. row fails at least one account role/state, internship state, or inclusive date condition.
For a missing business date, the service must immediately throw
<code>IllegalArgumentException("Business date is required")</code>.
## RED ## RED
@@ -65,6 +71,49 @@ Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS BUILD SUCCESS
~~~ ~~~
## Review follow-up: missing business date
The public guard was temporarily removed solely to prove the new regression fails for the intended reason, then
restored exactly before the GREEN checks. The follow-up commit contains only the regression test and evidence.
### RED
**Command**
~~~text
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
export PATH="$JAVA_HOME/bin:$PATH"
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
./mvnw '-Dtest=EligibleInternOptionIntegrationTest#rejectsMissingBusinessDate' test
~~~
**Observed result**
~~~text
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
java.lang.AssertionError: Expecting code to raise a throwable.
BUILD FAILURE
~~~
### GREEN
**Command**
~~~text
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
export PATH="$JAVA_HOME/bin:$PATH"
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
./mvnw '-Dtest=EligibleInternOptionIntegrationTest#rejectsMissingBusinessDate' test
~~~
**Observed result**
~~~text
PostgreSQL 18.4 Testcontainers started and Flyway applied V1 baseline.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
~~~
## Affected suite ## Affected suite
**Command and result** **Command and result**
@@ -90,6 +139,21 @@ Tests run: 105, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS BUILD SUCCESS
~~~ ~~~
### Review follow-up affected account-service checks
~~~text
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
export PATH="$JAVA_HOME/bin:$PATH"
export DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock
./mvnw '-Dtest=EligibleInternOptionIntegrationTest,AccountActivationIntegrationTest,BootstrapIntegrationTest' test
EligibleInternOptionIntegrationTest: 2 tests, 0 failures, 0 errors, 0 skipped
BootstrapIntegrationTest: 4 tests, 0 failures, 0 errors, 0 skipped
AccountActivationIntegrationTest: 2 tests, 0 failures, 0 errors, 0 skipped
Selected account-service reports: 8 tests, 0 failures, 0 errors, 0 skipped.
BUILD SUCCESS
~~~
## External-test boundaries ## External-test boundaries
This query does not authorize Project membership itself; the consuming Project transaction must still recheck This query does not authorize Project membership itself; the consuming Project transaction must still recheck
@@ -1,6 +1,7 @@
package com.lab.labtimesheet.feature.account.service; package com.lab.labtimesheet.feature.account.service;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant; import java.time.Instant;
@@ -83,6 +84,13 @@ class EligibleInternOptionIntegrationTest {
assertThat(options).extracting(EligibleInternOption::userId).doesNotHaveDuplicates(); assertThat(options).extracting(EligibleInternOption::userId).doesNotHaveDuplicates();
} }
@Test
void rejectsMissingBusinessDate() {
assertThatThrownBy(() -> accounts.eligibleInternOptions(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Business date is required");
}
private long activeIntern(String displayName, String studentCode, LocalDate startDate, LocalDate endDate) { private long activeIntern(String displayName, String studentCode, LocalDate startDate, LocalDate endDate) {
long userId = activeUser(GlobalRole.INTERN, displayName); long userId = activeUser(GlobalRole.INTERN, displayName);
activeProfile(userId, studentCode, startDate, endDate); activeProfile(userId, studentCode, startDate, endDate);