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 { z } from '@hono/zod-openapi'; import { createCrudRoutes } from '../../src/routes/generic-crud.routes'; import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; // 测试用户实体 @Entity() class TestUser { @PrimaryGeneratedColumn() id!: number; @Column('varchar') username!: string; @Column('varchar') password!: string; @Column('varchar') nickname!: string; @Column('varchar') registrationSource!: string; } // 测试实体类 @Entity() class TestEntity { @PrimaryGeneratedColumn() id!: number; @Column('varchar') name!: string; @Column('int') userId!: number; @Column('int', { nullable: true }) createdBy?: number; @Column('int', { nullable: true }) updatedBy?: number; } // 定义测试实体的Schema const createTestSchema = z.object({ name: z.string().min(1, '名称不能为空'), userId: z.number().optional() }); const updateTestSchema = z.object({ name: z.string().min(1, '名称不能为空').optional() }); const getTestSchema = z.object({ id: z.number(), name: z.string(), userId: z.number(), createdBy: z.number().nullable().optional(), updatedBy: z.number().nullable().optional() }); const listTestSchema = z.object({ id: z.number(), name: z.string(), userId: z.number(), createdBy: z.number().nullable().optional(), updatedBy: z.number().nullable().optional() }); // 设置集成测试钩子 setupIntegrationDatabaseHooksWithEntities([TestUser, TestEntity]) describe('共享CRUD数据权限控制集成测试', () => { let client: any; let testToken1: string; let testToken2: string; let testUser1: TestUser; let testUser2: TestUser; let mockAuthMiddleware: any; beforeEach(async () => { // 获取数据源 const dataSource = await IntegrationTestDatabase.getDataSource(); // 创建测试用户1 const userRepository = dataSource.getRepository(TestUser); testUser1 = userRepository.create({ username: `test_user_1_${Date.now()}`, password: 'test_password', nickname: '测试用户1', registrationSource: 'web' }); await userRepository.save(testUser1); // 创建测试用户2 testUser2 = userRepository.create({ username: `test_user_2_${Date.now()}`, password: 'test_password', nickname: '测试用户2', registrationSource: 'web' }); await userRepository.save(testUser2); // 生成测试用户的token testToken1 = JWTUtil.generateToken({ id: testUser1.id, username: testUser1.username, roles: [{name:'user'}] }); testToken2 = JWTUtil.generateToken({ id: testUser2.id, username: testUser2.username, roles: [{name:'user'}] }); // 创建模拟认证中间件 mockAuthMiddleware = async (c: any, next: any) => { const authHeader = c.req.header('Authorization'); if (authHeader && authHeader.startsWith('Bearer ')) { const token = authHeader.substring(7); try { // 简单模拟用户解析 if (token === testToken1) { c.set('user', { id: testUser1.id, username: testUser1.username }); } else if (token === testToken2) { c.set('user', { id: testUser2.id, username: testUser2.username }); } } catch (error) { // token解析失败 } } else { // 没有认证信息,返回401 return c.json({ code: 401, message: '认证失败' }, 401); } await next(); }; // 创建测试路由 - 启用数据权限控制 const testRoutes = createCrudRoutes({ entity: TestEntity, createSchema: createTestSchema, updateSchema: updateTestSchema, getSchema: getTestSchema, listSchema: listTestSchema, middleware: [mockAuthMiddleware], dataPermission: { enabled: true, userIdField: 'userId' } }); client = testClient(testRoutes); }); describe('GET / - 列表查询权限过滤', () => { it('应该只返回当前用户的数据', async () => { // 创建测试数据 const dataSource = await IntegrationTestDatabase.getDataSource(); const testRepository = dataSource.getRepository(TestEntity); // 为用户1创建数据 const user1Data1 = testRepository.create({ name: '用户1的数据1', userId: testUser1.id }); await testRepository.save(user1Data1); const user1Data2 = testRepository.create({ name: '用户1的数据2', userId: testUser1.id }); await testRepository.save(user1Data2); // 为用户2创建数据 const user2Data = testRepository.create({ name: '用户2的数据', userId: testUser2.id }); await testRepository.save(user2Data); // 用户1查询列表 const response = await client.index.$get({ query: { page: 1, pageSize: 10 } }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); console.debug('列表查询响应状态:', response.status); if (response.status !== 200) { const errorData = await response.json(); console.debug('列表查询错误信息:', errorData); } 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); expect(data.data).toHaveLength(2); // 应该只返回用户1的2条数据 // 验证所有返回的数据都属于用户1 data.data.forEach((item: any) => { expect(item.userId).toBe(testUser1.id); }); } }); it('应该拒绝未认证用户的访问', async () => { const response = await client.index.$get({ query: { page: 1, pageSize: 10 } }); expect(response.status).toBe(401); }); }); describe('POST / - 创建操作权限验证', () => { it('应该成功创建属于当前用户的数据', async () => { const createData = { name: '测试创建数据', userId: testUser1.id // 用户ID与当前用户匹配 }; const response = await client.index.$post({ json: createData }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); 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.userId).toBe(testUser1.id); } }); it('应该拒绝创建不属于当前用户的数据', async () => { const createData = { name: '测试创建数据', userId: testUser2.id // 用户ID与当前用户不匹配 }; const response = await client.index.$post({ json: createData }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); console.debug('创建无权数据响应状态:', response.status); expect(response.status).toBe(403); // 权限验证失败返回403 Forbidden if (response.status === 403) { const data = await response.json(); expect(data.message).toContain('无权'); } }); }); describe('GET /:id - 获取详情权限验证', () => { it('应该成功获取属于当前用户的数据详情', async () => { // 先创建测试数据 const dataSource = await IntegrationTestDatabase.getDataSource(); const testRepository = dataSource.getRepository(TestEntity); const testData = testRepository.create({ name: '测试数据详情', userId: testUser1.id }); await testRepository.save(testData); const response = await client[':id'].$get({ param: { id: testData.id } }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); console.debug('获取详情响应状态:', response.status); if (response.status !== 200) { const errorData = await response.json(); console.debug('获取详情错误信息:', errorData); } expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(data.id).toBe(testData.id); expect(data.name).toBe(testData.name); expect(data.userId).toBe(testUser1.id); } }); it('应该拒绝获取不属于当前用户的数据详情', async () => { // 先创建属于用户2的数据 const dataSource = await IntegrationTestDatabase.getDataSource(); const testRepository = dataSource.getRepository(TestEntity); const testData = testRepository.create({ name: '用户2的数据', userId: testUser2.id }); await testRepository.save(testData); // 用户1尝试获取用户2的数据 const response = await client[':id'].$get({ param: { id: testData.id } }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); console.debug('获取无权详情响应状态:', response.status); expect(response.status).toBe(403); // 权限验证失败返回403 }); it('应该处理不存在的资源', async () => { const response = await client[':id'].$get({ param: { id: 999999 } }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); expect(response.status).toBe(404); }); }); describe('PUT /:id - 更新操作权限验证', () => { it('应该成功更新属于当前用户的数据', async () => { // 先创建测试数据 const dataSource = await IntegrationTestDatabase.getDataSource(); const testRepository = dataSource.getRepository(TestEntity); const testData = testRepository.create({ name: '原始数据', userId: testUser1.id }); await testRepository.save(testData); const updateData = { name: '更新后的数据' }; const response = await client[':id'].$put({ param: { id: testData.id }, json: updateData }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); 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.userId).toBe(testUser1.id); } }); it('应该拒绝更新不属于当前用户的数据', async () => { // 先创建属于用户2的数据 const dataSource = await IntegrationTestDatabase.getDataSource(); const testRepository = dataSource.getRepository(TestEntity); const testData = testRepository.create({ name: '用户2的数据', userId: testUser2.id }); await testRepository.save(testData); const updateData = { name: '尝试更新的数据' }; // 用户1尝试更新用户2的数据 const response = await client[':id'].$put({ param: { id: testData.id }, json: updateData }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); console.debug('更新无权数据响应状态:', response.status); expect(response.status).toBe(403); // 权限验证失败返回403 Forbidden if (response.status === 403) { const data = await response.json(); expect(data.message).toContain('无权'); } }); }); describe('DELETE /:id - 删除操作权限验证', () => { it('应该成功删除属于当前用户的数据', async () => { // 先创建测试数据 const dataSource = await IntegrationTestDatabase.getDataSource(); const testRepository = dataSource.getRepository(TestEntity); const testData = testRepository.create({ name: '待删除数据', userId: testUser1.id }); await testRepository.save(testData); const response = await client[':id'].$delete({ param: { id: testData.id } }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); console.debug('删除数据响应状态:', response.status); expect(response.status).toBe(204); // 验证数据确实被删除 const deletedData = await testRepository.findOne({ where: { id: testData.id } }); expect(deletedData).toBeNull(); }); it('应该拒绝删除不属于当前用户的数据', async () => { // 先创建属于用户2的数据 const dataSource = await IntegrationTestDatabase.getDataSource(); const testRepository = dataSource.getRepository(TestEntity); const testData = testRepository.create({ name: '用户2的数据', userId: testUser2.id }); await testRepository.save(testData); // 用户1尝试删除用户2的数据 const response = await client[':id'].$delete({ param: { id: testData.id } }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); console.debug('删除无权数据响应状态:', response.status); expect(response.status).toBe(403); // 权限验证失败返回403 Forbidden if (response.status === 403) { const data = await response.json(); expect(data.message).toContain('无权'); } // 验证数据没有被删除 const existingData = await testRepository.findOne({ where: { id: testData.id } }); expect(existingData).not.toBeNull(); }); }); describe('禁用数据权限控制的情况', () => { it('当数据权限控制禁用时应该允许跨用户访问', async () => { // 创建禁用数据权限控制的路由 const noPermissionRoutes = createCrudRoutes({ entity: TestEntity, createSchema: createTestSchema, updateSchema: updateTestSchema, getSchema: getTestSchema, listSchema: listTestSchema, middleware: [mockAuthMiddleware], dataPermission: { enabled: false, // 禁用权限控制 userIdField: 'userId' } }); const noPermissionClient = testClient(noPermissionRoutes); // 创建属于用户2的数据 const dataSource = await IntegrationTestDatabase.getDataSource(); const testRepository = dataSource.getRepository(TestEntity); const testData = testRepository.create({ name: '用户2的数据', userId: testUser2.id }); await testRepository.save(testData); // 用户1应该能够访问用户2的数据(权限控制已禁用) const response = await noPermissionClient[':id'].$get({ param: { id: testData.id } }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); console.debug('禁用权限控制时的响应状态:', response.status); if (response.status !== 200) { try { const errorData = await response.json(); console.debug('禁用权限控制时的错误信息:', errorData); } catch (e) { const text = await response.text(); console.debug('禁用权限控制时的响应文本:', text); } } expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(data.id).toBe(testData.id); expect(data.userId).toBe(testUser2.id); } }); it('当不传递dataPermission配置时应该允许跨用户访问', async () => { // 创建不传递数据权限控制的路由 const noPermissionRoutes = createCrudRoutes({ entity: TestEntity, createSchema: createTestSchema, updateSchema: updateTestSchema, getSchema: getTestSchema, listSchema: listTestSchema, middleware: [mockAuthMiddleware] // 不传递 dataPermission 配置 }); const noPermissionClient = testClient(noPermissionRoutes); // 创建属于用户2的数据 const dataSource = await IntegrationTestDatabase.getDataSource(); const testRepository = dataSource.getRepository(TestEntity); const testData = testRepository.create({ name: '用户2的数据(无权限配置)', userId: testUser2.id }); await testRepository.save(testData); // 用户1应该能够访问用户2的数据(没有权限控制配置) console.debug('测试数据ID(无权限配置):', testData.id); const response = await noPermissionClient[':id'].$get({ param: { id: testData.id } }, { headers: { 'Authorization': `Bearer ${testToken1}` } }); console.debug('无权限配置时的响应状态:', response.status); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(data.id).toBe(testData.id); expect(data.userId).toBe(testUser2.id); } }); }); });