|
@@ -0,0 +1,423 @@
|
|
|
|
|
+import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
|
|
|
+import { testClient } from 'hono/testing';
|
|
|
|
|
+import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
|
|
|
|
|
+import { JWTUtil } from '@d8d/shared-utils';
|
|
|
|
|
+import { UserEntity, Role } from '@d8d/user-module';
|
|
|
|
|
+import { AreaEntity, AreaLevel } from '@d8d/geo-areas';
|
|
|
|
|
+import deliveryAddressRoutes from '../../src/routes';
|
|
|
|
|
+import { DeliveryAddress } from '../../src/entities';
|
|
|
|
|
+
|
|
|
|
|
+// 设置集成测试钩子
|
|
|
|
|
+setupIntegrationDatabaseHooksWithEntities([UserEntity, Role, AreaEntity, DeliveryAddress])
|
|
|
|
|
+
|
|
|
|
|
+describe('配送地址管理API集成测试', () => {
|
|
|
|
|
+ let client: ReturnType<typeof testClient<typeof deliveryAddressRoutes>>;
|
|
|
|
|
+ let testToken: string;
|
|
|
|
|
+ let testUser: UserEntity;
|
|
|
|
|
+ let testProvince: AreaEntity;
|
|
|
|
|
+ let testCity: AreaEntity;
|
|
|
|
|
+ let testDistrict: AreaEntity;
|
|
|
|
|
+
|
|
|
|
|
+ beforeEach(async () => {
|
|
|
|
|
+ // 创建测试客户端
|
|
|
|
|
+ client = testClient(deliveryAddressRoutes);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取数据源
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+
|
|
|
|
|
+ // 创建测试用户
|
|
|
|
|
+ const userRepository = dataSource.getRepository(UserEntity);
|
|
|
|
|
+ testUser = userRepository.create({
|
|
|
|
|
+ username: `test_user_${Date.now()}`,
|
|
|
|
|
+ password: 'test_password',
|
|
|
|
|
+ nickname: '测试用户',
|
|
|
|
|
+ registrationSource: 'web'
|
|
|
|
|
+ });
|
|
|
|
|
+ await userRepository.save(testUser);
|
|
|
|
|
+
|
|
|
|
|
+ // 创建测试地区数据 - 省
|
|
|
|
|
+ const areaRepository = dataSource.getRepository(AreaEntity);
|
|
|
|
|
+ testProvince = areaRepository.create({
|
|
|
|
|
+ name: '北京市',
|
|
|
|
|
+ code: '110000',
|
|
|
|
|
+ level: AreaLevel.PROVINCE,
|
|
|
|
|
+ parentId: null
|
|
|
|
|
+ });
|
|
|
|
|
+ await areaRepository.save(testProvince);
|
|
|
|
|
+
|
|
|
|
|
+ // 创建测试地区数据 - 市
|
|
|
|
|
+ testCity = areaRepository.create({
|
|
|
|
|
+ name: '北京市',
|
|
|
|
|
+ code: '110100',
|
|
|
|
|
+ level: AreaLevel.CITY,
|
|
|
|
|
+ parentId: testProvince.id
|
|
|
|
|
+ });
|
|
|
|
|
+ await areaRepository.save(testCity);
|
|
|
|
|
+
|
|
|
|
|
+ // 创建测试地区数据 - 区
|
|
|
|
|
+ testDistrict = areaRepository.create({
|
|
|
|
|
+ name: '朝阳区',
|
|
|
|
|
+ code: '110105',
|
|
|
|
|
+ level: AreaLevel.DISTRICT,
|
|
|
|
|
+ parentId: testCity.id
|
|
|
|
|
+ });
|
|
|
|
|
+ await areaRepository.save(testDistrict);
|
|
|
|
|
+
|
|
|
|
|
+ // 生成测试用户的token
|
|
|
|
|
+ testToken = JWTUtil.generateToken({
|
|
|
|
|
+ id: testUser.id,
|
|
|
|
|
+ username: testUser.username,
|
|
|
|
|
+ roles: [{name:'user'}]
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('GET /delivery-address', () => {
|
|
|
|
|
+ it('应该返回配送地址列表', async () => {
|
|
|
|
|
+ const response = await client.index.$get({
|
|
|
|
|
+ query: {}
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.debug('配送地址列表响应状态:', response.status);
|
|
|
|
|
+ expect(response.status).toBe(200);
|
|
|
|
|
+
|
|
|
|
|
+ if (response.status === 200) {
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+ expect(data).toHaveProperty('data');
|
|
|
|
|
+ expect(Array.isArray(data.data)).toBe(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该拒绝未认证用户的访问', async () => {
|
|
|
|
|
+ const response = await client.index.$get({
|
|
|
|
|
+ query: {}
|
|
|
|
|
+ });
|
|
|
|
|
+ expect(response.status).toBe(401);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('POST /delivery-address', () => {
|
|
|
|
|
+ it('应该成功创建配送地址', async () => {
|
|
|
|
|
+ const createData = {
|
|
|
|
|
+ name: '张三',
|
|
|
|
|
+ phone: '13800138000',
|
|
|
|
|
+ address: '朝阳区建国路88号',
|
|
|
|
|
+ receiverProvince: testProvince.id,
|
|
|
|
|
+ receiverCity: testCity.id,
|
|
|
|
|
+ receiverDistrict: testDistrict.id,
|
|
|
|
|
+ receiverTown: 0,
|
|
|
|
|
+ state: 1,
|
|
|
|
|
+ isDefault: 1
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client.index.$post({
|
|
|
|
|
+ json: createData
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.debug('创建配送地址响应状态:', response.status);
|
|
|
|
|
+ expect(response.status).toBe(201);
|
|
|
|
|
+
|
|
|
|
|
+ if (response.status === 201) {
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+ expect(data).toHaveProperty('id');
|
|
|
|
|
+ expect(data.name).toBe(createData.name);
|
|
|
|
|
+ expect(data.phone).toBe(createData.phone);
|
|
|
|
|
+ expect(data.address).toBe(createData.address);
|
|
|
|
|
+ expect(data.receiverProvince).toBe(createData.receiverProvince);
|
|
|
|
|
+ expect(data.receiverCity).toBe(createData.receiverCity);
|
|
|
|
|
+ expect(data.receiverDistrict).toBe(createData.receiverDistrict);
|
|
|
|
|
+ expect(data.isDefault).toBe(createData.isDefault);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该验证创建配送地址的必填字段', async () => {
|
|
|
|
|
+ const invalidData = {
|
|
|
|
|
+ // 缺少必填字段
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ phone: '',
|
|
|
|
|
+ address: '',
|
|
|
|
|
+ receiverProvince: 0,
|
|
|
|
|
+ receiverCity: 0,
|
|
|
|
|
+ receiverDistrict: 0
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client.index.$post({
|
|
|
|
|
+ json: invalidData
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(400);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该验证地区层级关系', async () => {
|
|
|
|
|
+ // 使用不存在的地区ID
|
|
|
|
|
+ const invalidAreaData = {
|
|
|
|
|
+ name: '李四',
|
|
|
|
|
+ phone: '13900139000',
|
|
|
|
|
+ address: '测试地址',
|
|
|
|
|
+ receiverProvince: 999999, // 不存在的省份
|
|
|
|
|
+ receiverCity: testCity.id,
|
|
|
|
|
+ receiverDistrict: testDistrict.id,
|
|
|
|
|
+ receiverTown: 0,
|
|
|
|
|
+ state: 1,
|
|
|
|
|
+ isDefault: 0
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client.index.$post({
|
|
|
|
|
+ json: invalidAreaData
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.debug('地区验证响应状态:', response.status);
|
|
|
|
|
+ expect(response.status).toBe(400);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('GET /delivery-address/:id', () => {
|
|
|
|
|
+ it('应该返回指定配送地址的详情', async () => {
|
|
|
|
|
+ // 先创建一个配送地址
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
|
|
|
|
|
+ const testDeliveryAddress = deliveryAddressRepository.create({
|
|
|
|
|
+ userId: testUser.id,
|
|
|
|
|
+ name: '王五',
|
|
|
|
|
+ phone: '13600136000',
|
|
|
|
|
+ address: '海淀区中关村大街1号',
|
|
|
|
|
+ receiverProvince: testProvince.id,
|
|
|
|
|
+ receiverCity: testCity.id,
|
|
|
|
|
+ receiverDistrict: testDistrict.id,
|
|
|
|
|
+ receiverTown: 0,
|
|
|
|
|
+ state: 1,
|
|
|
|
|
+ isDefault: 0,
|
|
|
|
|
+ createdBy: testUser.id
|
|
|
|
|
+ });
|
|
|
|
|
+ await deliveryAddressRepository.save(testDeliveryAddress);
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client[':id'].$get({
|
|
|
|
|
+ param: { id: testDeliveryAddress.id }
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.debug('配送地址详情响应状态:', response.status);
|
|
|
|
|
+ expect(response.status).toBe(200);
|
|
|
|
|
+
|
|
|
|
|
+ if (response.status === 200) {
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+ expect(data.id).toBe(testDeliveryAddress.id);
|
|
|
|
|
+ expect(data.name).toBe(testDeliveryAddress.name);
|
|
|
|
|
+ expect(data.phone).toBe(testDeliveryAddress.phone);
|
|
|
|
|
+ expect(data.address).toBe(testDeliveryAddress.address);
|
|
|
|
|
+
|
|
|
|
|
+ // 验证地区数据关联
|
|
|
|
|
+ expect(data.province).toBeDefined();
|
|
|
|
|
+ expect(data.province.id).toBe(testProvince.id);
|
|
|
|
|
+ expect(data.city).toBeDefined();
|
|
|
|
|
+ expect(data.city.id).toBe(testCity.id);
|
|
|
|
|
+ expect(data.district).toBeDefined();
|
|
|
|
|
+ expect(data.district.id).toBe(testDistrict.id);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该处理不存在的配送地址', async () => {
|
|
|
|
|
+ const response = await client[':id'].$get({
|
|
|
|
|
+ param: { id: 999999 }
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(404);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('PUT /delivery-address/:id', () => {
|
|
|
|
|
+ it('应该成功更新配送地址', async () => {
|
|
|
|
|
+ // 先创建一个配送地址
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
|
|
|
|
|
+ const testDeliveryAddress = deliveryAddressRepository.create({
|
|
|
|
|
+ userId: testUser.id,
|
|
|
|
|
+ name: '原始姓名',
|
|
|
|
|
+ phone: '13500135000',
|
|
|
|
|
+ address: '原始地址',
|
|
|
|
|
+ receiverProvince: testProvince.id,
|
|
|
|
|
+ receiverCity: testCity.id,
|
|
|
|
|
+ receiverDistrict: testDistrict.id,
|
|
|
|
|
+ receiverTown: 0,
|
|
|
|
|
+ state: 1,
|
|
|
|
|
+ isDefault: 0,
|
|
|
|
|
+ createdBy: testUser.id
|
|
|
|
|
+ });
|
|
|
|
|
+ await deliveryAddressRepository.save(testDeliveryAddress);
|
|
|
|
|
+
|
|
|
|
|
+ const updateData = {
|
|
|
|
|
+ name: '更新后的姓名',
|
|
|
|
|
+ phone: '13700137000',
|
|
|
|
|
+ address: '更新后的地址',
|
|
|
|
|
+ isDefault: 1
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client[':id'].$put({
|
|
|
|
|
+ param: { id: testDeliveryAddress.id },
|
|
|
|
|
+ json: updateData
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.debug('更新配送地址响应状态:', response.status);
|
|
|
|
|
+ expect(response.status).toBe(200);
|
|
|
|
|
+
|
|
|
|
|
+ if (response.status === 200) {
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+ expect(data.name).toBe(updateData.name);
|
|
|
|
|
+ expect(data.phone).toBe(updateData.phone);
|
|
|
|
|
+ expect(data.address).toBe(updateData.address);
|
|
|
|
|
+ expect(data.isDefault).toBe(updateData.isDefault);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('DELETE /delivery-address/:id', () => {
|
|
|
|
|
+ it('应该成功删除配送地址', async () => {
|
|
|
|
|
+ // 先创建一个配送地址
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
|
|
|
|
|
+ const testDeliveryAddress = deliveryAddressRepository.create({
|
|
|
|
|
+ userId: testUser.id,
|
|
|
|
|
+ name: '待删除地址',
|
|
|
|
|
+ phone: '13400134000',
|
|
|
|
|
+ address: '待删除地址',
|
|
|
|
|
+ receiverProvince: testProvince.id,
|
|
|
|
|
+ receiverCity: testCity.id,
|
|
|
|
|
+ receiverDistrict: testDistrict.id,
|
|
|
|
|
+ receiverTown: 0,
|
|
|
|
|
+ state: 1,
|
|
|
|
|
+ isDefault: 0,
|
|
|
|
|
+ createdBy: testUser.id
|
|
|
|
|
+ });
|
|
|
|
|
+ await deliveryAddressRepository.save(testDeliveryAddress);
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client[':id'].$delete({
|
|
|
|
|
+ param: { id: testDeliveryAddress.id }
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.debug('删除配送地址响应状态:', response.status);
|
|
|
|
|
+ expect(response.status).toBe(204);
|
|
|
|
|
+
|
|
|
|
|
+ // 验证配送地址确实被删除
|
|
|
|
|
+ const deletedDeliveryAddress = await deliveryAddressRepository.findOne({
|
|
|
|
|
+ where: { id: testDeliveryAddress.id }
|
|
|
|
|
+ });
|
|
|
|
|
+ expect(deletedDeliveryAddress).toBeNull();
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('省市区关联测试', () => {
|
|
|
|
|
+ it('应该正确关联省市区数据', async () => {
|
|
|
|
|
+ // 创建配送地址
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ const deliveryAddressRepository = dataSource.getRepository(DeliveryAddress);
|
|
|
|
|
+ const testDeliveryAddress = deliveryAddressRepository.create({
|
|
|
|
|
+ userId: testUser.id,
|
|
|
|
|
+ name: '关联测试',
|
|
|
|
|
+ phone: '13300133000',
|
|
|
|
|
+ address: '关联测试地址',
|
|
|
|
|
+ receiverProvince: testProvince.id,
|
|
|
|
|
+ receiverCity: testCity.id,
|
|
|
|
|
+ receiverDistrict: testDistrict.id,
|
|
|
|
|
+ receiverTown: 0,
|
|
|
|
|
+ state: 1,
|
|
|
|
|
+ isDefault: 0,
|
|
|
|
|
+ createdBy: testUser.id
|
|
|
|
|
+ });
|
|
|
|
|
+ await deliveryAddressRepository.save(testDeliveryAddress);
|
|
|
|
|
+
|
|
|
|
|
+ // 查询配送地址详情,验证地区关联
|
|
|
|
|
+ const response = await client[':id'].$get({
|
|
|
|
|
+ param: { id: testDeliveryAddress.id }
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(200);
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+
|
|
|
|
|
+ // 验证省市区关联数据
|
|
|
|
|
+ expect(data.province).toBeDefined();
|
|
|
|
|
+ expect(data.province.name).toBe('北京市');
|
|
|
|
|
+ expect(data.province.level).toBe(AreaLevel.PROVINCE);
|
|
|
|
|
+
|
|
|
|
|
+ expect(data.city).toBeDefined();
|
|
|
|
|
+ expect(data.city.name).toBe('北京市');
|
|
|
|
|
+ expect(data.city.level).toBe(AreaLevel.CITY);
|
|
|
|
|
+
|
|
|
|
|
+ expect(data.district).toBeDefined();
|
|
|
|
|
+ expect(data.district.name).toBe('朝阳区');
|
|
|
|
|
+ expect(data.district.level).toBe(AreaLevel.DISTRICT);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该验证地区层级关系', async () => {
|
|
|
|
|
+ // 创建另一个区级地区,但父级不是市级
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ const areaRepository = dataSource.getRepository(AreaEntity);
|
|
|
|
|
+
|
|
|
|
|
+ const invalidDistrict = areaRepository.create({
|
|
|
|
|
+ name: '无效区',
|
|
|
|
|
+ code: '999999',
|
|
|
|
|
+ level: AreaLevel.DISTRICT,
|
|
|
|
|
+ parentId: testProvince.id // 父级是省,不是市
|
|
|
|
|
+ });
|
|
|
|
|
+ await areaRepository.save(invalidDistrict);
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试使用无效的地区层级关系创建地址
|
|
|
|
|
+ const createData = {
|
|
|
|
|
+ name: '层级测试',
|
|
|
|
|
+ phone: '13200132000',
|
|
|
|
|
+ address: '层级测试地址',
|
|
|
|
|
+ receiverProvince: testProvince.id,
|
|
|
|
|
+ receiverCity: testCity.id,
|
|
|
|
|
+ receiverDistrict: invalidDistrict.id, // 这个区的父级不是testCity
|
|
|
|
|
+ receiverTown: 0,
|
|
|
|
|
+ state: 1,
|
|
|
|
|
+ isDefault: 0
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client.index.$post({
|
|
|
|
|
+ json: createData
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.debug('地区层级验证响应状态:', response.status);
|
|
|
|
|
+ // 这里期望返回400,因为地区层级关系不匹配
|
|
|
|
|
+ expect(response.status).toBe(400);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+});
|