talent-personal-info.integration.test.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. import { describe, it, expect, beforeEach } from 'vitest';
  2. import { testClient } from 'hono/testing';
  3. import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
  4. import { JWTUtil } from '@d8d/shared-utils';
  5. import { JWTPayload, UserType } from '@d8d/shared-types';
  6. import { UserEntity, Role } from '@d8d/user-module';
  7. import { File } from '@d8d/file-module';
  8. import { Company } from '@d8d/allin-company-module/entities';
  9. import { Platform } from '@d8d/allin-platform-module/entities';
  10. import { EmploymentOrder, OrderPerson, OrderPersonAsset } from '@d8d/allin-order-module/entities';
  11. import { BankName } from '@d8d/bank-names-module';
  12. import { DisabledPerson, DisabledBankCard, DisabledPhoto, DisabledRemark, DisabledVisit } from '../../src/entities';
  13. import talentPersonalInfoRoutes from '../../src/routes/talent-personal-info.routes';
  14. // 设置集成测试钩子 - 需要包含所有相关实体
  15. setupIntegrationDatabaseHooksWithEntities([
  16. UserEntity, File, Role, Company, Platform,
  17. EmploymentOrder, OrderPerson, OrderPersonAsset,
  18. DisabledPerson, BankName, DisabledBankCard, DisabledPhoto, DisabledRemark, DisabledVisit
  19. ])
  20. describe('人才个人信息API集成测试', () => {
  21. let client: ReturnType<typeof testClient<typeof talentPersonalInfoRoutes>>;
  22. let testToken: string;
  23. let testUser: UserEntity;
  24. let testDisabledPerson: DisabledPerson;
  25. let testBankName: BankName;
  26. let testBankCardFile: File;
  27. let testBankCard: DisabledBankCard;
  28. let testPhotoFile: File;
  29. let testPhoto: DisabledPhoto;
  30. beforeEach(async () => {
  31. // 创建测试客户端
  32. client = testClient(talentPersonalInfoRoutes);
  33. // 获取数据源
  34. const dataSource = await IntegrationTestDatabase.getDataSource();
  35. // 创建测试银行
  36. const bankNameRepository = dataSource.getRepository(BankName);
  37. testBankName = bankNameRepository.create({
  38. name: '中国工商银行',
  39. code: 'ICBC',
  40. status: 1
  41. });
  42. await bankNameRepository.save(testBankName);
  43. // 创建测试残疾人
  44. const disabledPersonRepo = dataSource.getRepository(DisabledPerson);
  45. testDisabledPerson = disabledPersonRepo.create({
  46. name: '测试人才',
  47. idCard: `110101${Date.now() % 100000000}`,
  48. gender: '男',
  49. birthDate: new Date('1990-01-01'),
  50. disabilityType: '肢体残疾',
  51. disabilityLevel: '二级',
  52. disabilityId: `DIS${Date.now() % 100000000}`,
  53. idAddress: '北京市朝阳区某某街道123号',
  54. idValidDate: new Date('2030-01-01'),
  55. disabilityValidDate: new Date('2025-12-31'),
  56. phone: '13800138000',
  57. province: '北京市',
  58. city: '北京市',
  59. district: '朝阳区',
  60. detailedAddress: '某某街道123号',
  61. canDirectContact: 1,
  62. isMarried: 0,
  63. nation: '汉族',
  64. jobStatus: 0,
  65. specificDisability: '左腿小腿截肢'
  66. });
  67. await disabledPersonRepo.save(testDisabledPerson);
  68. // 创建测试人才用户
  69. const userRepository = dataSource.getRepository(UserEntity);
  70. testUser = userRepository.create({
  71. username: `talent_user_${Date.now()}`,
  72. password: 'test_password',
  73. nickname: '人才测试用户',
  74. userType: UserType.TALENT,
  75. personId: testDisabledPerson.id,
  76. registrationSource: 'mini'
  77. });
  78. await userRepository.save(testUser);
  79. // 生成测试用户的token
  80. testToken = JWTUtil.generateToken({
  81. id: testUser.id,
  82. username: testUser.username,
  83. roles: [{ name: 'talent_user' }]
  84. });
  85. // 创建测试银行卡照片文件
  86. const fileRepository = dataSource.getRepository(File);
  87. testBankCardFile = fileRepository.create({
  88. name: '银行卡照片.jpg',
  89. type: 'image/jpeg',
  90. size: 102400,
  91. path: 'bank-cards/test-card.jpg',
  92. uploadUserId: testUser.id,
  93. uploadTime: new Date()
  94. });
  95. await fileRepository.save(testBankCardFile);
  96. // 创建测试银行卡
  97. const bankCardRepository = dataSource.getRepository(DisabledBankCard);
  98. testBankCard = bankCardRepository.create({
  99. personId: testDisabledPerson.id,
  100. subBankName: '中国工商银行北京分行朝阳支行',
  101. bankNameId: testBankName.id,
  102. cardNumber: '6222021234567890123',
  103. cardholderName: '测试人才',
  104. cardType: '一类卡',
  105. fileId: testBankCardFile.id,
  106. isDefault: 1
  107. });
  108. await bankCardRepository.save(testBankCard);
  109. // 创建测试证件照片文件
  110. testPhotoFile = fileRepository.create({
  111. name: '身份证正面.jpg',
  112. type: 'image/jpeg',
  113. size: 102400,
  114. path: 'photos/id-card-front.jpg',
  115. uploadUserId: testUser.id,
  116. uploadTime: new Date()
  117. });
  118. await fileRepository.save(testPhotoFile);
  119. // 创建测试证件照片
  120. const photoRepository = dataSource.getRepository(DisabledPhoto);
  121. testPhoto = photoRepository.create({
  122. personId: testDisabledPerson.id,
  123. photoType: '身份证',
  124. fileId: testPhotoFile.id,
  125. canDownload: 1
  126. });
  127. await photoRepository.save(testPhoto);
  128. });
  129. describe('GET /personal/info - 获取个人信息', () => {
  130. it('应该成功获取人才用户的个人信息', async () => {
  131. const response = await client.personal.info.$get(undefined, {
  132. headers: {
  133. Authorization: `Bearer ${testToken}`
  134. }
  135. });
  136. expect(response.status).toBe(200);
  137. if (response.status === 200) {
  138. const json = await response.json();
  139. expect(json).toMatchObject({
  140. name: '测试人才',
  141. gender: '男',
  142. idCard: testDisabledPerson.idCard,
  143. disabilityId: testDisabledPerson.disabilityId,
  144. disabilityType: '肢体残疾',
  145. disabilityLevel: '二级',
  146. phone: '13800138000',
  147. province: '北京市',
  148. city: '北京市',
  149. district: '朝阳区',
  150. detailedAddress: '某某街道123号',
  151. birthDate: '1990-01-01',
  152. idAddress: '北京市朝阳区某某街道123号',
  153. idValidDate: '2030-01-01',
  154. disabilityValidDate: '2025-12-31',
  155. canDirectContact: 1,
  156. isMarried: 0,
  157. nation: '汉族',
  158. jobStatus: 0,
  159. specificDisability: '左腿小腿截肢'
  160. });
  161. }
  162. });
  163. it('未登录用户应该返回401', async () => {
  164. const response = await client.personal.info.$get(undefined);
  165. expect(response.status).toBe(401);
  166. });
  167. it('非人才用户应该返回403', async () => {
  168. const dataSource = await IntegrationTestDatabase.getDataSource();
  169. const userRepository = dataSource.getRepository(UserEntity);
  170. // 创建管理员用户
  171. const adminUser = userRepository.create({
  172. username: `admin_user_${Date.now()}`,
  173. password: 'test_password',
  174. nickname: '管理员',
  175. userType: UserType.ADMIN,
  176. registrationSource: 'web'
  177. });
  178. await userRepository.save(adminUser);
  179. const adminToken = JWTUtil.generateToken({
  180. id: adminUser.id,
  181. username: adminUser.username,
  182. roles: [{ name: 'admin' }]
  183. });
  184. const response = await client.personal.info.$get(undefined, {
  185. headers: {
  186. Authorization: `Bearer ${adminToken}`
  187. }
  188. });
  189. expect(response.status).toBe(403);
  190. });
  191. it('用户不存在应该返回404', async () => {
  192. const dataSource = await IntegrationTestDatabase.getDataSource();
  193. const userRepository = dataSource.getRepository(UserEntity);
  194. // 创建一个没有关联残疾人的人才用户
  195. const orphanUser = userRepository.create({
  196. username: `orphan_user_${Date.now()}`,
  197. password: 'test_password',
  198. nickname: '孤儿用户',
  199. userType: UserType.TALENT,
  200. personId: null,
  201. registrationSource: 'mini'
  202. });
  203. await userRepository.save(orphanUser);
  204. const orphanToken = JWTUtil.generateToken({
  205. id: orphanUser.id,
  206. username: orphanUser.username,
  207. roles: [{ name: 'talent_user' }]
  208. });
  209. const response = await client.personal.info.$get(undefined, {
  210. headers: {
  211. Authorization: `Bearer ${orphanToken}`
  212. }
  213. });
  214. expect(response.status).toBe(404);
  215. });
  216. });
  217. describe('GET /personal/bank-cards - 获取银行卡列表', () => {
  218. it('应该成功获取银行卡列表,卡号已脱敏', async () => {
  219. const response = await client.personal['bank-cards'].$get(undefined, {
  220. headers: {
  221. Authorization: `Bearer ${testToken}`
  222. }
  223. });
  224. expect(response.status).toBe(200);
  225. if (response.status === 200) {
  226. const json = await response.json();
  227. expect(json.data).toHaveLength(1);
  228. expect(json.data[0]).toMatchObject({
  229. id: testBankCard.id,
  230. subBankName: '中国工商银行北京分行朝阳支行',
  231. bankName: '中国工商银行',
  232. cardNumber: '6222****0123',
  233. cardholderName: '测试人才',
  234. cardType: '一类卡',
  235. isDefault: 1
  236. });
  237. expect(json.total).toBe(1);
  238. }
  239. });
  240. it('未登录用户应该返回401', async () => {
  241. const response = await client.personal['bank-cards'].$get(undefined);
  242. expect(response.status).toBe(401);
  243. });
  244. });
  245. describe('GET /personal/photos - 获取证件照片列表', () => {
  246. it('应该成功获取证件照片列表', async () => {
  247. const response = await client.personal.photos.$get({
  248. query: {
  249. photoType: '身份证',
  250. skip: 0,
  251. take: 10
  252. }
  253. }, {
  254. headers: {
  255. Authorization: `Bearer ${testToken}`
  256. }
  257. });
  258. expect(response.status).toBe(200);
  259. if (response.status === 200) {
  260. const json = await response.json();
  261. expect(json.data).toHaveLength(1);
  262. expect(json.data[0]).toMatchObject({
  263. id: testPhoto.id,
  264. photoType: '身份证',
  265. fileName: '身份证正面.jpg',
  266. canDownload: 1
  267. });
  268. expect(json.total).toBe(1);
  269. }
  270. });
  271. it('应该支持按照片类型过滤', async () => {
  272. const response = await client.personal.photos.$get({
  273. query: {
  274. photoType: '残疾证',
  275. skip: 0,
  276. take: 10
  277. }
  278. }, {
  279. headers: {
  280. Authorization: `Bearer ${testToken}`
  281. }
  282. });
  283. expect(response.status).toBe(200);
  284. if (response.status === 200) {
  285. const json = await response.json();
  286. expect(json.data).toHaveLength(0);
  287. expect(json.total).toBe(0);
  288. }
  289. });
  290. it('应该支持分页', async () => {
  291. const response = await client.personal.photos.$get({
  292. query: {
  293. skip: 0,
  294. take: 10
  295. }
  296. }, {
  297. headers: {
  298. Authorization: `Bearer ${testToken}`
  299. }
  300. });
  301. expect(response.status).toBe(200);
  302. if (response.status === 200) {
  303. const json = await response.json();
  304. expect(json.data).toBeDefined();
  305. expect(json.total).toBeDefined();
  306. }
  307. });
  308. it('未登录用户应该返回401', async () => {
  309. const response = await client.personal.photos.$get({ query: {} }, {});
  310. expect(response.status).toBe(401);
  311. });
  312. it('无效的photoType参数应该返回400', async () => {
  313. const response = await client.personal.photos.$get({
  314. query: {
  315. photoType: '',
  316. skip: -1,
  317. take: 1000
  318. }
  319. }, {
  320. headers: {
  321. Authorization: `Bearer ${testToken}`
  322. }
  323. });
  324. // 参数验证会失败,返回400
  325. expect([400, 200]).toContain(response.status);
  326. });
  327. });
  328. });