Browse Source

feat: add onboarding data model migrations

backup/master-local-20261012
pwklppg 1 month ago
parent
commit
7fe96cc7a8
  1. 50
      changes/n04-onboard/.superpowers/sdd/task-briefs/T1.md
  2. 53
      changes/n04-onboard/.superpowers/sdd/task-briefs/T2.md
  3. 57
      mydreamjob-server/src/main/resources/db/migration/V38__onboarding_checklist_and_company_knowledge.sql
  4. 31
      mydreamjob-server/src/main/resources/db/migration/V39__onboarding_standard_checklist_seed.sql
  5. 157
      mydreamjob-server/src/test/java/com/mydreamjob/onboarding/OnboardingMigrationTest.java
  6. 105
      mydreamjob-server/src/test/java/com/mydreamjob/onboarding/OnboardingSeedDataIntegrationTest.java

50
changes/n04-onboard/.superpowers/sdd/task-briefs/T1.md

@ -0,0 +1,50 @@
# T1 任务简报:V38/V39 迁移与通用清单种子
## Where This Fits
- Wave: W1
- Depends on: 无
- Contract: `changes/n04-onboard/execution-contract.md`
- Specs: `changes/n04-onboard/specs/onboarding-checklist/spec.md`、`changes/n04-onboard/specs/company-knowledge/spec.md`
- Design: `changes/n04-onboard/design.md` 的 Data Model 与 Architecture 部分
## Objective
新增 V38 建表迁移和 V39 通用清单种子,并先用失败测试锁定表结构、约束、通用项数量和稳定排序。
## Files In Scope
- `mydreamjob-server/src/main/resources/db/migration/V38__onboarding_checklist_and_company_knowledge.sql`
- `mydreamjob-server/src/main/resources/db/migration/V39__onboarding_standard_checklist_seed.sql`
- `mydreamjob-server/src/test/java/com/mydreamjob/onboarding/OnboardingMigrationTest.java`
- `mydreamjob-server/src/test/java/com/mydreamjob/onboarding/OnboardingSeedDataIntegrationTest.java`
- 如项目迁移测试需要共享测试配置,仅可修改同一测试包内的辅助文件。
## Required Behavior
- 创建 `onboarding_checklist_items`、`student_onboarding_progress`、`company_knowledge`。
- `onboarding_checklist_items` 至少包含合同规定的字段、唯一键、活动/排序索引、`company_id` 对 `companies(id)` 的外键。
- `student_onboarding_progress` 必须对学生与条目唯一,并建立 `users(id)`、`onboarding_checklist_items(id)` 外键。
- `company_knowledge.company_id` 唯一,并建立 `companies(id)` 外键。
- V39 必须写入稳定的通用清单;每一步使用明确 `item_key`、分类、排序和必填标记,不得使用随机值。
- 迁移必须兼容 H2 `MODE=MySQL` 与 MySQL 8.4。
## TDD Requirement
1. 先写 `OnboardingMigrationTest`、`OnboardingSeedDataIntegrationTest`,确认因表或种子不存在而失败。
2. 再写 V38/V39 让测试通过。
3. 不得先写迁移再补测试。
## Evidence Commands
```powershell
cd D:\1\zhi\zhilai\mydreamjob-server
mvn -Dtest=OnboardingMigrationTest,OnboardingSeedDataIntegrationTest test
```
## Done Definition
- 两条证据测试通过。
- 测试断言三张表存在、关键唯一约束/外键/索引可验证、通用种子数量大于 0 且排序稳定。
- 不修改 V1-V37。
- 不创建 W2 实体、Repository、Service 或 Controller。

53
changes/n04-onboard/.superpowers/sdd/task-briefs/T2.md

@ -0,0 +1,53 @@
# T2 任务简报:Onboarding 实体、Repository 与评价聚合查询
## Where This Fits
- Wave: W1
- Depends on: T1
- Contract: `changes/n04-onboard/execution-contract.md`
- Design: `changes/n04-onboard/design.md` 的 Architecture、Data Model、Decisions
## Objective
在 V38/V39 表结构之上新增实体、Repository 和公司评价聚合查询,先写持久化失败测试,再实现最小映射。
## Files In Scope
- `mydreamjob-server/src/main/java/com/mydreamjob/onboarding/OnboardingChecklistItem.java`
- `mydreamjob-server/src/main/java/com/mydreamjob/onboarding/StudentOnboardingProgress.java`
- `mydreamjob-server/src/main/java/com/mydreamjob/onboarding/CompanyKnowledge.java`
- `mydreamjob-server/src/main/java/com/mydreamjob/onboarding/OnboardingChecklistItemRepository.java`
- `mydreamjob-server/src/main/java/com/mydreamjob/onboarding/StudentOnboardingProgressRepository.java`
- `mydreamjob-server/src/main/java/com/mydreamjob/onboarding/CompanyKnowledgeRepository.java`
- 必要时只读扩展 `mydreamjob-server/src/main/java/com/mydreamjob/review/JobReviewRepository.java` 和 `JobReviewDimensionScoreRepository.java` 的公司级聚合查询。
- `mydreamjob-server/src/test/java/com/mydreamjob/onboarding/OnboardingRepositoryIntegrationTest.java`
## Required Behavior
- `OnboardingChecklistItem` 映射 `onboarding_checklist_items`,含 `scopeType`、`scopeId`、`companyId`、`itemKey`、title、description、category、sortOrder、required、active、idempotencyKey、requestHash、version、审计时间。
- `StudentOnboardingProgress` 映射 `student_onboarding_progress`,含 studentUserId、itemId、completed、completedAt、version、审计时间。
- `CompanyKnowledge` 映射 `company_knowledge`,含 companyId、culture、workPolicies、onboardingGuide、version、审计时间。
- 清单 Repository 支持按 `companyId` 查询活动通用项+活动企业项,并按 `sortOrder ASC, id ASC` 稳定排序。
- 进度 Repository 支持按学生和多个 itemId 批量读取,并按学生+条目唯一查询。
- 知识 Repository 支持按 companyId 查询。
- 评价聚合查询支持公司下全部 `PUBLISHED` 且 `AUTO_APPROVED/APPROVED` 的评价评分分布、tags 原始列和八维汇总;不得逐岗位或逐评价产生 N+1。
## TDD Requirement
1. 先写 `OnboardingRepositoryIntegrationTest`,确认新实体/Repository 不存在或查询失败。
2. 再实现最小实体和 Repository 使测试通过。
3. 不创建 Service、Controller、DTO 或业务状态机。
## Evidence Commands
```powershell
cd D:\1\zhi\zhilai\mydreamjob-server
mvn -Dtest=OnboardingRepositoryIntegrationTest test
```
## Done Definition
- 新实体与三张表映射通过 Hibernate `ddl-auto=validate`。
- 合并查询只返回活动通用项和当前企业活动项。
- 多学生、多企业、软删除项和排序测试通过。
- 公司评分/tags/八维聚合仅统计公开评价。

57
mydreamjob-server/src/main/resources/db/migration/V38__onboarding_checklist_and_company_knowledge.sql

@ -0,0 +1,57 @@
-- Flyway V38: 入职清单与企业知识表结构
-- 本迁移只建立三张业务表;通用清单内容由 V39 导入。
CREATE TABLE onboarding_checklist_items (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
scope_type VARCHAR(20) NOT NULL COMMENT 'STANDARD 或 COMPANY',
scope_id BIGINT NOT NULL COMMENT '通用项为 0,企业项为 company_id',
company_id BIGINT NULL COMMENT '企业项所属企业;通用项为空',
item_key VARCHAR(80) NOT NULL COMMENT '清单项稳定业务键',
title VARCHAR(120) NOT NULL COMMENT '标题',
description VARCHAR(1000) NULL COMMENT '说明',
category VARCHAR(30) NOT NULL COMMENT 'DOCUMENT/ADMIN/EQUIPMENT/TRAINING/LOGISTICS/OTHER',
sort_order INT NOT NULL DEFAULT 1000 COMMENT '排序值',
required BOOLEAN NOT NULL DEFAULT FALSE COMMENT '是否必填',
active BOOLEAN NOT NULL DEFAULT TRUE COMMENT '是否生效',
idempotency_key VARCHAR(64) NULL COMMENT '企业创建幂等键',
request_hash CHAR(64) NULL COMMENT '规范化创建请求 SHA-256',
version BIGINT NOT NULL DEFAULT 0 COMMENT '乐观锁版本',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT ck_onboarding_checklist_scope_type CHECK (scope_type IN ('STANDARD', 'COMPANY')),
CONSTRAINT ck_onboarding_checklist_category CHECK (
category IN ('DOCUMENT', 'ADMIN', 'EQUIPMENT', 'TRAINING', 'LOGISTICS', 'OTHER')
),
CONSTRAINT ck_onboarding_checklist_sort_order CHECK (sort_order BETWEEN 0 AND 9999),
CONSTRAINT uk_onboarding_checklist_scope_item UNIQUE (scope_type, scope_id, item_key),
CONSTRAINT uk_onboarding_checklist_company_idempotency UNIQUE (company_id, idempotency_key),
CONSTRAINT fk_onboarding_checklist_company FOREIGN KEY (company_id) REFERENCES companies (id),
INDEX idx_onboarding_checklist_scope_active_sort (scope_type, scope_id, active, sort_order, id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入职清单项';
CREATE TABLE student_onboarding_progress (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
student_user_id BIGINT NOT NULL COMMENT '学生用户',
item_id BIGINT NOT NULL COMMENT '清单项',
completed BOOLEAN NOT NULL DEFAULT FALSE COMMENT '是否完成',
completed_at DATETIME NULL COMMENT '完成时间;取消完成时为空',
version BIGINT NOT NULL DEFAULT 0 COMMENT '乐观锁版本',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uk_student_onboarding_progress_student_item UNIQUE (student_user_id, item_id),
CONSTRAINT fk_student_onboarding_progress_student FOREIGN KEY (student_user_id) REFERENCES users (id),
CONSTRAINT fk_student_onboarding_progress_item FOREIGN KEY (item_id) REFERENCES onboarding_checklist_items (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生入职清单完成状态';
CREATE TABLE company_knowledge (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
company_id BIGINT NOT NULL COMMENT '企业',
culture VARCHAR(2000) NULL COMMENT '企业文化',
work_policies VARCHAR(4000) NULL COMMENT '工作制度',
onboarding_guide VARCHAR(4000) NULL COMMENT '入职指南',
version BIGINT NOT NULL DEFAULT 0 COMMENT '乐观锁版本',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uk_company_knowledge_company UNIQUE (company_id),
CONSTRAINT fk_company_knowledge_company FOREIGN KEY (company_id) REFERENCES companies (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='企业知识介绍';

31
mydreamjob-server/src/main/resources/db/migration/V39__onboarding_standard_checklist_seed.sql

@ -0,0 +1,31 @@
-- Flyway V39: 通用入职准备清单种子
-- 通用项使用 scope_id=0、company_id=NULL,内容、分类、顺序和必填标记均为固定值。
INSERT INTO onboarding_checklist_items
(scope_type, scope_id, company_id, item_key, title, description, category, sort_order,
required, active, idempotency_key, request_hash, version)
VALUES
('STANDARD', 0, NULL, 'standard-id-card', '准备身份证件',
'准备身份证原件或有效身份证明,用于入职登记。', 'DOCUMENT', 100, TRUE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-education-proof', '准备学历学位证明',
'按公司要求准备学历、学位证书或学信网证明。', 'DOCUMENT', 110, TRUE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-id-photo', '准备证件照',
'准备符合公司要求的近期证件照。', 'DOCUMENT', 120, FALSE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-bank-account', '提供工资卡信息',
'按公司要求提供本人工资卡账户信息。', 'ADMIN', 200, TRUE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-emergency-contact', '填写紧急联系人',
'填写必要的紧急联系人和联系方式。', 'ADMIN', 210, TRUE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-background-check', '完成背景核验',
'按公司通知配合完成背景信息核验。', 'ADMIN', 220, FALSE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-medical-check', '确认入职体检要求',
'确认岗位是否需要体检及指定机构要求。', 'ADMIN', 230, FALSE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-equipment-request', '确认办公设备需求',
'提前确认工位、电脑或其他办公设备需求。', 'EQUIPMENT', 300, FALSE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-report-location', '确认报到地址和时间',
'与 HR 确认入职日期、报到地址和接待人。', 'LOGISTICS', 400, TRUE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-commute-plan', '规划通勤路线',
'提前规划到岗路线和交通时间。', 'LOGISTICS', 410, FALSE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-compliance-training', '完成合规与保密培训',
'完成公司要求的合规、安全和保密培训。', 'TRAINING', 500, TRUE, TRUE, NULL, NULL, 0),
('STANDARD', 0, NULL, 'standard-onboarding-guide', '阅读入职指南',
'阅读公司入职指南及相关准备说明。', 'TRAINING', 510, FALSE, TRUE, NULL, NULL, 0);

157
mydreamjob-server/src/test/java/com/mydreamjob/onboarding/OnboardingMigrationTest.java

@ -0,0 +1,157 @@
package com.mydreamjob.onboarding;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static org.junit.jupiter.api.Assertions.assertTrue;
@SpringBootTest
@TestPropertySource(properties =
"spring.datasource.url=jdbc:h2:mem:integrationdb_onboarding_migration;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
@ActiveProfiles("integration-test")
class OnboardingMigrationTest {
@Autowired
private DataSource dataSource;
@MockBean
private RedisTemplate<String, Object> redisTemplate;
@Test
void migrationCreatesOnboardingTablesColumnsConstraintsAndIndexes() throws SQLException {
assertTableAndColumns("onboarding_checklist_items",
"id", "scope_type", "scope_id", "company_id", "item_key", "title",
"description", "category", "sort_order", "required", "active",
"idempotency_key", "request_hash", "version", "created_at", "updated_at");
assertTableAndColumns("student_onboarding_progress",
"id", "student_user_id", "item_id", "completed", "completed_at",
"version", "created_at", "updated_at");
assertTableAndColumns("company_knowledge",
"id", "company_id", "culture", "work_policies", "onboarding_guide",
"version", "created_at", "updated_at");
assertUniqueIndex("onboarding_checklist_items", "scope_type", "scope_id", "item_key");
assertUniqueIndex("onboarding_checklist_items", "company_id", "idempotency_key");
assertUniqueIndex("student_onboarding_progress", "student_user_id", "item_id");
assertUniqueIndex("company_knowledge", "company_id");
assertForeignKey("onboarding_checklist_items", "company_id", "companies", "id");
assertForeignKey("student_onboarding_progress", "student_user_id", "users", "id");
assertForeignKey("student_onboarding_progress", "item_id", "onboarding_checklist_items", "id");
assertForeignKey("company_knowledge", "company_id", "companies", "id");
assertIndex("onboarding_checklist_items",
"scope_type", "scope_id", "active", "sort_order", "id");
}
private void assertTableAndColumns(String table, String... columns) throws SQLException {
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData metadata = connection.getMetaData();
String actualTable = findTable(metadata, table);
if (actualTable == null) {
throw new AssertionError("Missing table " + table);
}
List<String> actualColumns = new ArrayList<>();
try (ResultSet resultSet = metadata.getColumns(null, null, actualTable, null)) {
while (resultSet.next()) {
actualColumns.add(resultSet.getString("COLUMN_NAME").toLowerCase());
}
}
for (String column : columns) {
assertTrue(actualColumns.contains(column.toLowerCase()),
"Missing column " + table + "." + column + "; actual=" + actualColumns);
}
}
}
private void assertUniqueIndex(String table, String... columns) throws SQLException {
assertTrue(hasIndex(table, true, columns),
"Missing unique index on " + table + "(" + String.join(", ", columns) + ")");
}
private void assertIndex(String table, String... columns) throws SQLException {
assertTrue(hasIndex(table, false, columns),
"Missing index on " + table + "(" + String.join(", ", columns) + ")");
}
private boolean hasIndex(String table, boolean unique, String... expectedColumns)
throws SQLException {
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData metadata = connection.getMetaData();
String actualTable = findTable(metadata, table);
if (actualTable == null) {
return false;
}
Map<String, TreeMap<Short, String>> indexes = new LinkedHashMap<>();
try (ResultSet resultSet = metadata.getIndexInfo(null, null, actualTable, false, false)) {
while (resultSet.next()) {
String indexName = resultSet.getString("INDEX_NAME");
if (indexName == null || resultSet.getBoolean("NON_UNIQUE") == unique) {
continue;
}
indexes.computeIfAbsent(indexName, ignored -> new TreeMap<>())
.put(resultSet.getShort("ORDINAL_POSITION"),
resultSet.getString("COLUMN_NAME").toLowerCase());
}
}
List<String> expected = new ArrayList<>();
for (String column : expectedColumns) {
expected.add(column.toLowerCase());
}
return indexes.values().stream()
.map(TreeMap::values)
.map(values -> new ArrayList<>(values))
.anyMatch(expected::equals);
}
}
private void assertForeignKey(String childTable, String childColumn,
String parentTable, String parentColumn) throws SQLException {
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData metadata = connection.getMetaData();
String actualChildTable = findTable(metadata, childTable);
String actualParentTable = findTable(metadata, parentTable);
if (actualChildTable == null || actualParentTable == null) {
throw new AssertionError("Missing table for foreign key " + childTable + "." + childColumn);
}
try (ResultSet resultSet = metadata.getImportedKeys(null, null, actualChildTable)) {
while (resultSet.next()) {
if (actualParentTable.equalsIgnoreCase(resultSet.getString("PKTABLE_NAME"))
&& childColumn.equalsIgnoreCase(resultSet.getString("FKCOLUMN_NAME"))
&& parentColumn.equalsIgnoreCase(resultSet.getString("PKCOLUMN_NAME"))) {
return;
}
}
}
}
throw new AssertionError("Missing foreign key " + childTable + "." + childColumn
+ " -> " + parentTable + "." + parentColumn);
}
private String findTable(DatabaseMetaData metadata, String expected) throws SQLException {
try (ResultSet resultSet = metadata.getTables(null, null, null, new String[]{"TABLE"})) {
while (resultSet.next()) {
if (expected.equalsIgnoreCase(resultSet.getString("TABLE_NAME"))) {
return resultSet.getString("TABLE_NAME");
}
}
}
return null;
}
}

105
mydreamjob-server/src/test/java/com/mydreamjob/onboarding/OnboardingSeedDataIntegrationTest.java

@ -0,0 +1,105 @@
package com.mydreamjob.onboarding;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.TestPropertySource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@SpringBootTest
@TestPropertySource(properties =
"spring.datasource.url=jdbc:h2:mem:integrationdb_onboarding_seed;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
@ActiveProfiles("integration-test")
class OnboardingSeedDataIntegrationTest {
private static final Set<String> SUPPORTED_CATEGORIES = Set.of(
"DOCUMENT", "ADMIN", "EQUIPMENT", "TRAINING", "LOGISTICS", "OTHER");
private static final List<ExpectedStandardItem> EXPECTED_ITEMS = List.of(
new ExpectedStandardItem("standard-id-card", "准备身份证件", "DOCUMENT", 100, true),
new ExpectedStandardItem("standard-education-proof", "准备学历学位证明", "DOCUMENT", 110, true),
new ExpectedStandardItem("standard-id-photo", "准备证件照", "DOCUMENT", 120, false),
new ExpectedStandardItem("standard-bank-account", "提供工资卡信息", "ADMIN", 200, true),
new ExpectedStandardItem("standard-emergency-contact", "填写紧急联系人", "ADMIN", 210, true),
new ExpectedStandardItem("standard-background-check", "完成背景核验", "ADMIN", 220, false),
new ExpectedStandardItem("standard-medical-check", "确认入职体检要求", "ADMIN", 230, false),
new ExpectedStandardItem("standard-equipment-request", "确认办公设备需求", "EQUIPMENT", 300, false),
new ExpectedStandardItem("standard-report-location", "确认报到地址和时间", "LOGISTICS", 400, true),
new ExpectedStandardItem("standard-commute-plan", "规划通勤路线", "LOGISTICS", 410, false),
new ExpectedStandardItem("standard-compliance-training", "完成合规与保密培训", "TRAINING", 500, true),
new ExpectedStandardItem("standard-onboarding-guide", "阅读入职指南", "TRAINING", 510, false)
);
@Autowired
private JdbcTemplate jdbcTemplate;
@MockBean
private RedisTemplate<String, Object> redisTemplate;
@Test
void standardChecklistSeedIsStableAndComplete() {
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
"SELECT item_key, title, category, sort_order, required, active, " +
"scope_type, scope_id, company_id " +
"FROM onboarding_checklist_items " +
"WHERE scope_type = 'STANDARD' ORDER BY sort_order ASC, id ASC");
assertEquals(EXPECTED_ITEMS.size(), rows.size());
for (int index = 0; index < EXPECTED_ITEMS.size(); index++) {
ExpectedStandardItem expected = EXPECTED_ITEMS.get(index);
Map<String, Object> actual = rows.get(index);
assertEquals(expected.itemKey(), actual.get("item_key"));
assertEquals(expected.title(), actual.get("title"));
assertEquals(expected.category(), actual.get("category"));
assertEquals(expected.sortOrder(), ((Number) actual.get("sort_order")).intValue());
assertEquals(expected.required(), asBoolean(actual.get("required")));
assertTrue(asBoolean(actual.get("active")));
assertEquals("STANDARD", actual.get("scope_type"));
assertEquals(0L, ((Number) actual.get("scope_id")).longValue());
assertNull(actual.get("company_id"));
assertTrue(SUPPORTED_CATEGORIES.contains(expected.category()));
assertNotNull(actual.get("title"));
}
assertEquals(EXPECTED_ITEMS.size(), count(
"SELECT COUNT(DISTINCT item_key) FROM onboarding_checklist_items " +
"WHERE scope_type = 'STANDARD'"));
}
private int count(String sql) {
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count == null ? 0 : count;
}
private boolean asBoolean(Object value) {
if (value instanceof Boolean booleanValue) {
return booleanValue;
}
if (value instanceof Number numberValue) {
return numberValue.intValue() != 0;
}
return Boolean.parseBoolean(String.valueOf(value));
}
private record ExpectedStandardItem(
String itemKey,
String title,
String category,
int sortOrder,
boolean required
) {
}
}
Loading…
Cancel
Save