Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b13c547068 | ||
|
|
a9fb948769 | ||
|
|
7bac998866 | ||
|
|
28ecfabe37 | ||
|
|
c8735c8ab8 | ||
|
|
6de463221b | ||
|
|
0a139d5470 |
@@ -0,0 +1,83 @@
|
||||
# Test Evidence: development-profile attendance policy time hydration
|
||||
|
||||
- **Test type:** Integration
|
||||
- **Requirement IDs:** `ATT-002`, `ATT-003`, `I1-ATT-01`
|
||||
- **Scenario IDs:** `AC-ATT-001`
|
||||
- **Test class/method:** `com.lab.labtimesheet.feature.attendance.controller.CalendarDevelopmentProfileWebIntegrationTest#v1SeededPolicyLetsFormAuthenticatedAdminOpenCalendarInAsiaHoChiMinhDevelopmentProfile`
|
||||
- **Implementation commit:** pending
|
||||
|
||||
## Protected behavior
|
||||
|
||||
The unmodified V1 attendance policy must hydrate its `time` schedule as the configured local wall-clock values when the development profile runs in `Asia/Ho_Chi_Minh`. A form-authenticated Admin can therefore open the calendar without weakening the policy rule that requires the checkout cutoff to be before local midnight.
|
||||
|
||||
## Test method
|
||||
|
||||
The test starts the application with the real `dev` profile plus isolated test configuration, forces the JVM default zone to `Asia/Ho_Chi_Minh` before JPA starts, and uses PostgreSQL 18.4 Testcontainers with Flyway V1. It bootstraps an Admin through the form, logs in through the form, and requests `/attendance/calendar`, which resolves the current policy through `AttendanceApplicationService.currentBusinessDate`.
|
||||
|
||||
## Hand-derived expected result
|
||||
|
||||
V1 explicitly stores `scheduled_start = 08:30`, `scheduled_end = 15:30`, and `checkout_grace_minutes = 30`. The checkout cutoff is therefore `16:00`, which is strictly before local midnight, so the calendar request returns HTTP 200.
|
||||
|
||||
## RED
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=CalendarDevelopmentProfileWebIntegrationTest' test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
PostgreSQL 18.4 Testcontainers applied Flyway V1, then the form-authenticated GET /attendance/calendar failed.
|
||||
BUILD FAILURE: CalendarDevelopmentProfileWebIntegrationTest ... ServletException caused by
|
||||
IllegalArgumentException: checkout cutoff must be before local midnight
|
||||
at AttendancePolicy.java:58 via AttendancePolicyEntity.toDomain and AttendanceApplicationService.currentBusinessDate.
|
||||
```
|
||||
|
||||
## GREEN
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=CalendarDevelopmentProfileWebIntegrationTest' test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
PostgreSQL 18.4 Testcontainers applied Flyway V1.
|
||||
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
```
|
||||
|
||||
## Affected suite
|
||||
|
||||
**Command and result**
|
||||
|
||||
```text
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=CalendarAuthorizationWebIntegrationTest,CalendarDevelopmentProfileWebIntegrationTest,RoleDashboardWebIntegrationTest,AttendancePersistenceIntegrationTest,AttendancePolicyTest' test
|
||||
|
||||
Tests run: 13, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=*Attendance*Test,*Calendar*Test,Dashboard*Test,RoleDashboardWebIntegrationTest,AdminDashboardWebTest' test
|
||||
|
||||
Tests run: 60, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
```
|
||||
|
||||
## Java 26 smoke
|
||||
|
||||
**Command and result**
|
||||
|
||||
```text
|
||||
env JAVA_HOME=/Users/sechmachine/Library/Java/JavaVirtualMachines/corretto-26.0.2/Contents/Home PATH=/Users/sechmachine/Library/Java/JavaVirtualMachines/corretto-26.0.2/Contents/Home/bin:/opt/homebrew/bin:/usr/bin:/bin ./mvnw clean compile -DskipTests
|
||||
|
||||
Amazon Corretto 26.0.2 compiled 129 source files with release 25.
|
||||
BUILD SUCCESS
|
||||
```
|
||||
|
||||
## External-test boundaries
|
||||
|
||||
This integration test proves the fresh Flyway/JPA/real-login calendar path under the development profile and Vietnam JVM zone. It does not operate the already-running browser-gate application or exercise the Intern dashboard UI itself; both paths resolve the same policy timeline.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Test Evidence: Windows legacy Vietnam timezone startup
|
||||
|
||||
- **Test type:** Integration
|
||||
- **Requirement IDs:** `ARC-001`, `ARC-003`, `GOV-011`, `ATT-002`, `TST-001`, `TST-005`
|
||||
- **Scenario IDs:** `N/A — user-reported cross-platform startup defect`
|
||||
- **Test class/method:** `com.lab.labtimesheet.ApplicationTimeZoneIntegrationTest.mainCanonicalizesLegacyAliasBeforeStartingSpring`, `com.lab.labtimesheet.ApplicationTimeZoneIntegrationTest.canonicalizesLegacyVietnamAliasBeforePostgresConnects`, `com.lab.labtimesheet.ApplicationTimeZoneIntegrationTest.leavesSupportedSystemTimeZoneUnchanged`
|
||||
- **Implementation commit:** `a9fb9487692e84f5a7e7923570cbded58c362a2f`
|
||||
|
||||
## Protected behavior
|
||||
|
||||
The executable entry point replaces the legacy Windows JVM timezone ID `Asia/Saigon` with the canonical business timezone ID `Asia/Ho_Chi_Minh` before pgJDBC opens a PostgreSQL connection. Other supported operating-system timezone IDs remain unchanged.
|
||||
|
||||
## Test method
|
||||
|
||||
The entry-point test replaces Spring startup with Mockito's existing static test seam, invokes the real `main` method with a legacy JVM default, and checks that normalization happens before Spring starts. The PostgreSQL test starts a real PostgreSQL 18.4 Testcontainer, proves pgJDBC 42.7.11 is rejected while the JVM default is `Asia/Saigon`, invokes the same startup normalization, and then opens a valid JDBC connection. A negative test verifies that an unrelated supported timezone is not overwritten.
|
||||
|
||||
## Hand-derived expected result
|
||||
|
||||
PostgreSQL does not accept `Asia/Saigon` as a startup `TimeZone`, while the approved business timezone is `Asia/Ho_Chi_Minh`. Therefore only the legacy alias is replaced, the following connection succeeds, and a supported non-Vietnam timezone remains unchanged.
|
||||
|
||||
## RED
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw -Dtest=ApplicationTimeZoneIntegrationTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
BUILD FAILURE during test compilation.
|
||||
ApplicationTimeZoneIntegrationTest.java: cannot find symbol normalizeDefaultTimeZone()
|
||||
```
|
||||
|
||||
The failing test established that the application had no pre-Spring normalization boundary.
|
||||
|
||||
A second mutation check temporarily removed the new call from `main` and ran:
|
||||
|
||||
```text
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin ./mvnw '-Dtest=ApplicationTimeZoneIntegrationTest#mainCanonicalizesLegacyAliasBeforeStartingSpring' test
|
||||
```
|
||||
|
||||
It failed `1/1` with `expected: "Asia/Ho_Chi_Minh" but was: "Asia/Saigon"`, proving the test protects the entry-point ordering rather than only the helper.
|
||||
|
||||
## GREEN
|
||||
|
||||
**Command**
|
||||
|
||||
```text
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw -Dtest=ApplicationTimeZoneIntegrationTest test
|
||||
```
|
||||
|
||||
**Observed result**
|
||||
|
||||
```text
|
||||
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
```
|
||||
|
||||
## Affected suite
|
||||
|
||||
**Command and result**
|
||||
|
||||
```text
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw '-Dtest=ApplicationTimeZoneIntegrationTest,LabtimesheetApplicationTests,PlatformFoundationTest,TimeConfigurationTest,CalendarDevelopmentProfileWebIntegrationTest' test
|
||||
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin DOCKER_HOST=unix:///Users/sechmachine/.orbstack/run/docker.sock ./mvnw test
|
||||
Tests run: 217, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
|
||||
env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/bin:/bin ./mvnw -DskipTests -Ddoclint=all javadoc:javadoc
|
||||
BUILD SUCCESS
|
||||
```
|
||||
|
||||
A local Java 25 process was also started with `-Duser.timezone=Asia/Saigon` against a disposable PostgreSQL 18.4 database on port `55439`. Hikari connected, Flyway migrated the fresh database, Tomcat started on port `18080`, and Spring reported `Started LabtimesheetApplication`. The process shut down cleanly and the disposable database container was removed.
|
||||
|
||||
## External-test boundaries
|
||||
|
||||
The regression executes the installed pgJDBC version against PostgreSQL 18.4 and reproduces the exact rejected timezone value from the Windows report. It does not run the Windows JVM itself; the supplied Windows log is the evidence that its OS/JDK mapping produced `Asia/Saigon`.
|
||||
@@ -46,3 +46,7 @@ No browser loop or Impeccable detector is run on this branch; the root owner per
|
||||
After merging exact reviewed `main` `32c8a2d315d2175760c5d4792988cd0aa5ab6dd0`, `npm ci`, the 1/1 UI test, frontend build, compile, Project-scoped Javadoc/doclint, and diff check all succeeded. The first affected Java command added the updated shared `UiContractWebTest` and passed 45/45 tests with no failures, errors, or skips.
|
||||
|
||||
The bounded post-review affected command reran `ProjectControllerTest,ProjectEntityTest,ProjectPersistenceStructureTest,ProjectServiceIntegrationTest,ProjectTaskMutationContextTest,LayerStructureTest,UiContractWebTest` and passed 47/47 with no failures, errors, or skips, including 9/9 Project service tests against PostgreSQL 18.4. The UI test remained 1/1; frontend build, compile, Project-scoped Javadoc/doclint, and `git diff --check` also succeeded.
|
||||
|
||||
The root-owned final full suite then exposed a branch-induced MVC-slice fixture RED: 213 tests ran with 0 failures and 3 errors, all `ProjectTaskFormAccessibilityWebTest` context errors because the slice did not provide the new ProjectController AccountService dependency. A focused reproduction ran the class at 3 tests, 0 failures, 3 errors and reported the same missing AccountService constructor dependency.
|
||||
|
||||
The smallest test-only correction supplies the controller's AccountService and Clock dependencies and the existing ProjectQueryService mock's authenticated Mentor response. The intermediate focused runs exposed each dependency in order; no production code changed. Final focused GREEN: `env JAVA_HOME=/opt/homebrew/opt/openjdk@25 PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ./mvnw '-Dtest=ProjectTaskFormAccessibilityWebTest' test` passed 3/3 with no failures, errors, or skips. The root owner retains the broader rerun.
|
||||
|
||||
@@ -1,23 +1,43 @@
|
||||
package com.lab.labtimesheet;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
import com.lab.labtimesheet.config.SecurityProperties;
|
||||
|
||||
/** Application entry point and root component-scan boundary for Lab Timesheet. */
|
||||
/**
|
||||
* Application entry point and root component-scan boundary for Lab Timesheet.
|
||||
*
|
||||
* <p>The entry point also canonicalizes the legacy Windows Vietnam timezone alias before database
|
||||
* drivers inspect the JVM default timezone.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
public class LabtimesheetApplication {
|
||||
private static final String LEGACY_VIETNAM_TIME_ZONE = "Asia/Saigon";
|
||||
private static final String BUSINESS_TIME_ZONE = "Asia/Ho_Chi_Minh";
|
||||
|
||||
/**
|
||||
* Starts the standalone Spring Boot process.
|
||||
* Canonicalizes the process timezone and starts the standalone Spring Boot process.
|
||||
*
|
||||
* @param args command-line arguments forwarded to Spring Boot
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
normalizeDefaultTimeZone();
|
||||
SpringApplication.run(LabtimesheetApplication.class, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the legacy Windows Vietnam alias before pgJDBC sends it to PostgreSQL as a startup
|
||||
* parameter. Other supported system timezones remain unchanged.
|
||||
*/
|
||||
static void normalizeDefaultTimeZone() {
|
||||
if (LEGACY_VIETNAM_TIME_ZONE.equals(TimeZone.getDefault().getID())) {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(BUSINESS_TIME_ZONE));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ spring:
|
||||
hibernate:
|
||||
ddl-auto: validate
|
||||
open-in-view: false
|
||||
properties:
|
||||
hibernate:
|
||||
jdbc:
|
||||
time_zone: UTC
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: classpath:db/migration
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.lab.labtimesheet;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.testcontainers.postgresql.PostgreSQLContainer;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
class ApplicationTimeZoneIntegrationTest {
|
||||
|
||||
@Test
|
||||
void mainCanonicalizesLegacyAliasBeforeStartingSpring() {
|
||||
TimeZone originalTimeZone = TimeZone.getDefault();
|
||||
String[] args = {"--spring.profiles.active=test"};
|
||||
|
||||
try (MockedStatic<SpringApplication> springApplication = mockStatic(SpringApplication.class)) {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Saigon"));
|
||||
|
||||
LabtimesheetApplication.main(args);
|
||||
|
||||
assertThat(TimeZone.getDefault().getID()).isEqualTo("Asia/Ho_Chi_Minh");
|
||||
springApplication.verify(() -> SpringApplication.run(LabtimesheetApplication.class, args));
|
||||
} finally {
|
||||
TimeZone.setDefault(originalTimeZone);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void canonicalizesLegacyVietnamAliasBeforePostgresConnects() throws SQLException {
|
||||
TimeZone originalTimeZone = TimeZone.getDefault();
|
||||
|
||||
try (PostgreSQLContainer postgres =
|
||||
new PostgreSQLContainer(DockerImageName.parse("postgres:18.4"))) {
|
||||
postgres.start();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Saigon"));
|
||||
|
||||
assertThat(TimeZone.getDefault().getID()).isEqualTo("Asia/Saigon");
|
||||
assertThatThrownBy(() -> openConnection(postgres))
|
||||
.isInstanceOf(SQLException.class)
|
||||
.hasMessageContaining("Asia/Saigon");
|
||||
|
||||
LabtimesheetApplication.normalizeDefaultTimeZone();
|
||||
|
||||
assertThat(TimeZone.getDefault().getID()).isEqualTo("Asia/Ho_Chi_Minh");
|
||||
try (Connection connection = openConnection(postgres)) {
|
||||
assertThat(connection.isValid(1)).isTrue();
|
||||
}
|
||||
} finally {
|
||||
TimeZone.setDefault(originalTimeZone);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void leavesSupportedSystemTimeZoneUnchanged() {
|
||||
TimeZone originalTimeZone = TimeZone.getDefault();
|
||||
|
||||
try {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris"));
|
||||
|
||||
LabtimesheetApplication.normalizeDefaultTimeZone();
|
||||
|
||||
assertThat(TimeZone.getDefault().getID()).isEqualTo("Europe/Paris");
|
||||
} finally {
|
||||
TimeZone.setDefault(originalTimeZone);
|
||||
}
|
||||
}
|
||||
|
||||
private static Connection openConnection(PostgreSQLContainer postgres) throws SQLException {
|
||||
return DriverManager.getConnection(postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword());
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.lab.labtimesheet.feature.attendance.controller;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.lab.labtimesheet.config.TestcontainersConfiguration;
|
||||
import java.util.TimeZone;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@Import(TestcontainersConfiguration.class)
|
||||
@SpringBootTest(properties = {
|
||||
"LAB_SMTP_HOST=localhost",
|
||||
"LAB_SMTP_PORT=1025",
|
||||
"LAB_SERVER_PORT=0",
|
||||
"LAB_FORWARD_HEADERS_STRATEGY=none",
|
||||
"LAB_PUBLIC_ORIGIN=http://localhost:8080",
|
||||
"LAB_SECURITY_MASTER_KEY=AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="
|
||||
})
|
||||
@AutoConfigureMockMvc
|
||||
@ActiveProfiles({"dev", "test"})
|
||||
@ContextConfiguration(initializers = CalendarDevelopmentProfileWebIntegrationTest.AsiaHoChiMinhTimeZoneInitializer.class)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
class CalendarDevelopmentProfileWebIntegrationTest {
|
||||
private static final String ADMIN_EMAIL = "admin@example.test";
|
||||
private static final String PASSWORD = "correct horse battery staple";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
void v1SeededPolicyLetsFormAuthenticatedAdminOpenCalendarInAsiaHoChiMinhDevelopmentProfile() throws Exception {
|
||||
mockMvc.perform(post("/bootstrap")
|
||||
.with(csrf())
|
||||
.param("email", ADMIN_EMAIL)
|
||||
.param("displayName", "Admin")
|
||||
.param("password", PASSWORD))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("/admin/smtp?onboarding"));
|
||||
|
||||
var login = mockMvc.perform(post("/login")
|
||||
.with(csrf())
|
||||
.param("username", ADMIN_EMAIL)
|
||||
.param("password", PASSWORD))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(authenticated().withUsername(ADMIN_EMAIL))
|
||||
.andReturn();
|
||||
|
||||
mockMvc.perform(get("/attendance/calendar")
|
||||
.session((MockHttpSession) login.getRequest().getSession(false)))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void restoreSystemDefaultTimeZone() {
|
||||
TimeZone.setDefault(AsiaHoChiMinhTimeZoneInitializer.originalDefaultTimeZone());
|
||||
}
|
||||
|
||||
static final class AsiaHoChiMinhTimeZoneInitializer
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
private static final TimeZone ORIGINAL_DEFAULT_TIME_ZONE = TimeZone.getDefault();
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Ho_Chi_Minh"));
|
||||
}
|
||||
|
||||
static TimeZone originalDefaultTimeZone() {
|
||||
return ORIGINAL_DEFAULT_TIME_ZONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -8,14 +8,20 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.lab.labtimesheet.feature.account.service.AccountService;
|
||||
import com.lab.labtimesheet.feature.integration.service.SmtpConfigurationService;
|
||||
import com.lab.labtimesheet.feature.project.controller.ProjectController;
|
||||
import com.lab.labtimesheet.feature.project.model.dto.ProjectActorView;
|
||||
import com.lab.labtimesheet.feature.project.service.ProjectQueryService;
|
||||
import com.lab.labtimesheet.feature.project.service.ProjectService;
|
||||
import com.lab.labtimesheet.feature.task.controller.TaskController;
|
||||
import com.lab.labtimesheet.feature.task.model.dto.TaskAssigneeChoice;
|
||||
import com.lab.labtimesheet.feature.task.service.TaskService;
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
|
||||
@@ -34,12 +40,26 @@ class ProjectTaskFormAccessibilityWebTest {
|
||||
@MockitoBean
|
||||
private ProjectService projects;
|
||||
|
||||
@MockitoBean
|
||||
private AccountService accounts;
|
||||
|
||||
@MockitoBean
|
||||
private Clock clock;
|
||||
|
||||
@MockitoBean
|
||||
private TaskService tasks;
|
||||
|
||||
@MockitoBean
|
||||
private SmtpConfigurationService smtpConfiguration;
|
||||
|
||||
@BeforeEach
|
||||
void mentorActor() {
|
||||
given(projectQueries.authenticatedActor("mentor@example.test"))
|
||||
.willReturn(new ProjectActorView(10L, "MENTOR"));
|
||||
given(clock.instant()).willReturn(Instant.parse("2026-08-15T01:00:00Z"));
|
||||
given(clock.getZone()).willReturn(ZoneId.of("Asia/Ho_Chi_Minh"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void projectFieldErrorsHaveStableIdsAndInputAssociations() throws Exception {
|
||||
mvc.perform(post("/projects")
|
||||
|
||||
Reference in New Issue
Block a user