package com.mydreamjob.integration; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; import java.util.Set; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.web.servlet.MockMvc; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** 合并后的真实 MySQL 迁移、实体校验及运行时接口集合门禁。 */ @Testcontainers(disabledWithoutDocker = false) @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("mysql-it") class LxqModulesMySqlIntegrationTest { @Container static final MySQLContainer MYSQL = new MySQLContainer<>("mysql:8.4") .withDatabaseName("lxq_module_integration") .withUsername("module_test").withPassword("module-test-only"); @DynamicPropertySource static void properties(DynamicPropertyRegistry r) { r.add("spring.datasource.url", MYSQL::getJdbcUrl); r.add("spring.datasource.username", MYSQL::getUsername); r.add("spring.datasource.password", MYSQL::getPassword); } @MockBean RedisTemplate redis; @Autowired JdbcTemplate jdbc; @Autowired MockMvc mvc; @Autowired ObjectMapper mapper; @Test void migrationsAndSeedsAreRealAndComplete() { assertThat(jdbc.queryForObject("select count(*) from flyway_schema_history where success=0", Long.class)).isZero(); assertThat(jdbc.queryForObject("select count(*) from flyway_schema_history where version in ('47','48','49','50','51','52','53') and success=1", Long.class)).isEqualTo(7); for (String table : Set.of("skills", "skill_industries", "skill_occupations", "occupation_skills", "onboarding_checklist_items", "salary_city_policy")) { assertThat(jdbc.queryForObject("select count(*) from " + table, Long.class)).as(table).isPositive(); } for (String table : Set.of("student_onboarding_progress", "company_knowledge", "salary_calculation_history")) { assertThat(jdbc.queryForObject("select count(*) from " + table, Long.class)).as(table).isZero(); } } @Test void runtimeAndStaticOperationsAreIdentical() throws Exception { JsonNode runtime = mapper.readTree(mvc.perform(get("/v3/api-docs")) .andExpect(status().isOk()).andReturn().getResponse().getContentAsByteArray()); JsonNode contract = mapper.readTree(Files.readString(Path.of("../docs/openapi-rc.json"))); assertThat(operations(runtime)).hasSize(254).isEqualTo(operations(contract)); assertThat(runtime.path("paths").size()).isEqualTo(206); assertThat(runtime.path("paths").path("/conversations/{id}/events")) .isEqualTo(contract.path("paths").path("/conversations/{id}/events")); for (String field : Set.of("clientMessageId", "version")) { String pointer = "/components/schemas/MessageResponse/properties/" + field; assertThat(runtime.at(pointer).isMissingNode()).as(pointer).isFalse(); assertThat(contract.at(pointer)).as(pointer).isEqualTo(runtime.at(pointer)); } String requestField = "/components/schemas/SendMessageRequest/properties/clientMessageId"; assertThat(runtime.at(requestField).isMissingNode()).isFalse(); assertThat(contract.at(requestField)).isEqualTo(runtime.at(requestField)); } private Set operations(JsonNode document) { Set result = new HashSet<>(); document.path("paths").fields().forEachRemaining(path -> path.getValue().fieldNames().forEachRemaining(method -> { if (Set.of("get", "post", "put", "patch", "delete", "head", "options", "trace").contains(method)) result.add(method + " " + path.getKey()); })); return result; } }