|
|
@@ -584,286 +584,4 @@ describe('文件路由API集成测试 (使用hono/testing)', () => {
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- describe('多租户数据隔离测试', () => {
|
|
|
- let tenant1User: any;
|
|
|
- let tenant2User: any;
|
|
|
- let tenant1Token: string;
|
|
|
- let tenant2Token: string;
|
|
|
-
|
|
|
- beforeEach(async () => {
|
|
|
- const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
- if (!dataSource) throw new Error('Database not initialized');
|
|
|
-
|
|
|
- // 创建用户1
|
|
|
- tenant1User = await TestDataFactory.createTestUser(dataSource, {
|
|
|
- username: 'user1',
|
|
|
- password: 'TestPassword123!',
|
|
|
- email: 'user1@example.com'
|
|
|
- });
|
|
|
-
|
|
|
- // 创建用户2
|
|
|
- tenant2User = await TestDataFactory.createTestUser(dataSource, {
|
|
|
- username: 'user2',
|
|
|
- password: 'TestPassword123!',
|
|
|
- email: 'user2@example.com'
|
|
|
- });
|
|
|
-
|
|
|
- // 生成租户用户的token
|
|
|
- tenant1Token = authService.generateToken(tenant1User);
|
|
|
- tenant2Token = authService.generateToken(tenant2User);
|
|
|
-
|
|
|
- // 清理文件数据
|
|
|
- const fileRepository = dataSource.getRepository(File);
|
|
|
- });
|
|
|
-
|
|
|
- describe('文件创建租户隔离', () => {
|
|
|
- it('应该为租户1创建文件并设置正确的租户ID', async () => {
|
|
|
- const fileData = {
|
|
|
- name: 'tenant1_file.pdf',
|
|
|
- type: 'application/pdf',
|
|
|
- size: 1024,
|
|
|
- path: 'test/path',
|
|
|
- description: '租户1的文件'
|
|
|
- };
|
|
|
-
|
|
|
- const response = await client['upload-policy'].$post({
|
|
|
- json: fileData
|
|
|
- }, {
|
|
|
- headers: {
|
|
|
- 'Authorization': `Bearer ${tenant1Token}`
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- expect(response.status).toBe(200);
|
|
|
- if (response.status === 200) {
|
|
|
- const responseData = await response.json();
|
|
|
- expect(responseData.file.name).toBe('tenant1_file.pdf');
|
|
|
- expect(responseData.file.uploadUserId).toBe(tenant1User.id);
|
|
|
-
|
|
|
- // 验证数据库中的租户ID
|
|
|
- const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
- if (!dataSource) throw new Error('Database not initialized');
|
|
|
- const fileRepository = dataSource.getRepository(File);
|
|
|
- const savedFile = await fileRepository.findOne({
|
|
|
- where: { name: fileData.name }
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- it('应该为租户2创建文件并设置正确的租户ID', async () => {
|
|
|
- const fileData = {
|
|
|
- name: 'tenant2_file.pdf',
|
|
|
- type: 'application/pdf',
|
|
|
- size: 2048,
|
|
|
- path: 'test/path',
|
|
|
- description: '租户2的文件'
|
|
|
- };
|
|
|
-
|
|
|
- const response = await client['upload-policy'].$post({
|
|
|
- json: fileData
|
|
|
- }, {
|
|
|
- headers: {
|
|
|
- 'Authorization': `Bearer ${tenant2Token}`
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- expect(response.status).toBe(200);
|
|
|
- if (response.status === 200) {
|
|
|
- const responseData = await response.json();
|
|
|
- expect(responseData.file.name).toBe('tenant2_file.pdf');
|
|
|
- expect(responseData.file.uploadUserId).toBe(tenant2User.id);
|
|
|
-
|
|
|
- // 验证数据库中的租户ID
|
|
|
- const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
- if (!dataSource) throw new Error('Database not initialized');
|
|
|
- const fileRepository = dataSource.getRepository(File);
|
|
|
- const savedFile = await fileRepository.findOne({
|
|
|
- where: { name: fileData.name }
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- describe('文件查询租户隔离', () => {
|
|
|
- beforeEach(async () => {
|
|
|
- const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
- if (!dataSource) throw new Error('Database not initialized');
|
|
|
-
|
|
|
- const fileRepository = dataSource.getRepository(File);
|
|
|
-
|
|
|
- // 创建租户1的文件
|
|
|
- await fileRepository.save([
|
|
|
- fileRepository.create({
|
|
|
- name: 'tenant1_file1.pdf',
|
|
|
- type: 'application/pdf',
|
|
|
- size: 1024,
|
|
|
- path: 'tenant1/path1',
|
|
|
- uploadUserId: tenant1User.id,
|
|
|
-
|
|
|
- uploadTime: new Date()
|
|
|
- }),
|
|
|
- fileRepository.create({
|
|
|
- name: 'tenant1_file2.jpg',
|
|
|
- type: 'image/jpeg',
|
|
|
- size: 2048,
|
|
|
- path: 'tenant1/path2',
|
|
|
- uploadUserId: tenant1User.id,
|
|
|
-
|
|
|
- uploadTime: new Date()
|
|
|
- })
|
|
|
- ]);
|
|
|
-
|
|
|
- // 创建租户2的文件
|
|
|
- await fileRepository.save([
|
|
|
- fileRepository.create({
|
|
|
- name: 'tenant2_file1.pdf',
|
|
|
- type: 'application/pdf',
|
|
|
- size: 3072,
|
|
|
- path: 'tenant2/path1',
|
|
|
- uploadUserId: tenant2User.id,
|
|
|
-
|
|
|
- uploadTime: new Date()
|
|
|
- })
|
|
|
- ]);
|
|
|
- });
|
|
|
-
|
|
|
- it('应该只返回租户1的文件列表', async () => {
|
|
|
- const response = await client.index.$get({
|
|
|
- query: {}
|
|
|
- }, {
|
|
|
- headers: {
|
|
|
- 'Authorization': `Bearer ${tenant1Token}`
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- expect(response.status).toBe(200);
|
|
|
- if (response.status === 200) {
|
|
|
- const responseData = await response.json();
|
|
|
- expect(Array.isArray(responseData.data)).toBe(true);
|
|
|
- expect(responseData.data).toHaveLength(2);
|
|
|
- expect(responseData.data.some((file: any) => file.name === 'tenant1_file1.pdf')).toBe(true);
|
|
|
- expect(responseData.data.some((file: any) => file.name === 'tenant1_file2.jpg')).toBe(true);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- it('应该只返回租户2的文件列表', async () => {
|
|
|
- const response = await client.index.$get({
|
|
|
- query: {}
|
|
|
- }, {
|
|
|
- headers: {
|
|
|
- 'Authorization': `Bearer ${tenant2Token}`
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- expect(response.status).toBe(200);
|
|
|
- if (response.status === 200) {
|
|
|
- const responseData = await response.json();
|
|
|
- expect(Array.isArray(responseData.data)).toBe(true);
|
|
|
- expect(responseData.data).toHaveLength(1);
|
|
|
- expect(responseData.data[0].name).toBe('tenant2_file1.pdf');
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- it('租户1不应该访问租户2的文件', async () => {
|
|
|
- const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
- if (!dataSource) throw new Error('Database not initialized');
|
|
|
- const fileRepository = dataSource.getRepository(File);
|
|
|
- const tenant2File = await fileRepository.findOneBy({ name: 'tenant2_file1.pdf' });
|
|
|
-
|
|
|
- if (tenant2File) {
|
|
|
- const response = await client[':id'].$get({
|
|
|
- param: { id: tenant2File.id }
|
|
|
- }, {
|
|
|
- headers: {
|
|
|
- 'Authorization': `Bearer ${tenant1Token}`
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 应该返回404,因为租户1不能访问租户2的文件
|
|
|
- expect(response.status).toBe(404);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- describe('文件删除租户隔离', () => {
|
|
|
- let tenant1File: File;
|
|
|
- let tenant2File: File;
|
|
|
-
|
|
|
- beforeEach(async () => {
|
|
|
- const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
- if (!dataSource) throw new Error('Database not initialized');
|
|
|
- const fileRepository = dataSource.getRepository(File);
|
|
|
-
|
|
|
- // 创建租户1的文件
|
|
|
- tenant1File = fileRepository.create({
|
|
|
- name: 'tenant1_delete_test.pdf',
|
|
|
- type: 'application/pdf',
|
|
|
- size: 1024,
|
|
|
- path: 'tenant1/delete_test',
|
|
|
- uploadUserId: tenant1User.id,
|
|
|
-
|
|
|
- uploadTime: new Date()
|
|
|
- });
|
|
|
-
|
|
|
- // 创建租户2的文件
|
|
|
- tenant2File = fileRepository.create({
|
|
|
- name: 'tenant2_delete_test.pdf',
|
|
|
- type: 'application/pdf',
|
|
|
- size: 2048,
|
|
|
- path: 'tenant2/delete_test',
|
|
|
- uploadUserId: tenant2User.id,
|
|
|
-
|
|
|
- uploadTime: new Date()
|
|
|
- });
|
|
|
-
|
|
|
- await fileRepository.save([tenant1File, tenant2File]);
|
|
|
- });
|
|
|
-
|
|
|
- it('应该允许租户1删除自己的文件', async () => {
|
|
|
- const response = await client[':id'].$delete({
|
|
|
- param: { id: tenant1File.id }
|
|
|
- }, {
|
|
|
- headers: {
|
|
|
- 'Authorization': `Bearer ${tenant1Token}`
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- expect(response.status).toBe(200);
|
|
|
-
|
|
|
- // 验证文件已被删除
|
|
|
- const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
- if (!dataSource) throw new Error('Database not initialized');
|
|
|
- const fileRepository = dataSource.getRepository(File);
|
|
|
- const deletedFile = await fileRepository.findOneBy({ id: tenant1File.id });
|
|
|
- expect(deletedFile).toBeNull();
|
|
|
- });
|
|
|
-
|
|
|
- it('不应该允许租户2删除租户1的文件', async () => {
|
|
|
- const response = await client[':id'].$delete({
|
|
|
- param: { id: tenant1File.id }
|
|
|
- }, {
|
|
|
- headers: {
|
|
|
- 'Authorization': `Bearer ${tenant2Token}`
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 调试输出
|
|
|
- console.debug(`租户2删除租户1文件响应状态: ${response.status}`);
|
|
|
- if (response.status as number !== 200) {
|
|
|
- const responseData = await response.json();
|
|
|
- console.debug(`响应数据:`, responseData);
|
|
|
- }
|
|
|
-
|
|
|
- // 应该返回404或403,因为租户2不能删除租户1的文件
|
|
|
- expect([404, 403]).toContain(response.status);
|
|
|
-
|
|
|
- // 验证文件仍然存在
|
|
|
- const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
- if (!dataSource) throw new Error('Database not initialized');
|
|
|
- const fileRepository = dataSource.getRepository(File);
|
|
|
- const existingFile = await fileRepository.findOneBy({ id: tenant1File.id });
|
|
|
- expect(existingFile).toBeDefined();
|
|
|
- });
|
|
|
- });
|
|
|
- });
|
|
|
});
|