|
@@ -0,0 +1,319 @@
|
|
|
|
|
+import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
|
|
|
+import { testClient } from 'hono/testing';
|
|
|
|
|
+import {
|
|
|
|
|
+ IntegrationTestDatabase,
|
|
|
|
|
+ setupIntegrationDatabaseHooksWithEntities
|
|
|
|
|
+} from '@d8d/shared-test-util';
|
|
|
|
|
+import { systemConfigRoutesMt } from '../../src/routes/system-config.routes.mt';
|
|
|
|
|
+import { SystemConfigMt } from '../../src/entities/system-config.entity.mt';
|
|
|
|
|
+import { SystemConfigServiceMt } from '../../src/services/system-config.service.mt';
|
|
|
|
|
+import { UserEntityMt, RoleMt } from '@d8d/core-module-mt/user-module-mt';
|
|
|
|
|
+import { FileMt } from '@d8d/core-module-mt/file-module-mt';
|
|
|
|
|
+import { TestDataFactory } from '../utils/integration-test-db';
|
|
|
|
|
+import { AuthService } from '@d8d/core-module-mt/auth-module-mt';
|
|
|
|
|
+import { UserServiceMt } from '@d8d/core-module-mt/user-module-mt';
|
|
|
|
|
+
|
|
|
|
|
+// 设置集成测试钩子
|
|
|
|
|
+setupIntegrationDatabaseHooksWithEntities([SystemConfigMt, UserEntityMt, RoleMt, FileMt])
|
|
|
|
|
+
|
|
|
|
|
+describe('系统配置路由API集成测试 (使用hono/testing)', () => {
|
|
|
|
|
+ let client: ReturnType<typeof testClient<typeof systemConfigRoutesMt>>;
|
|
|
|
|
+ let authService: AuthService;
|
|
|
|
|
+ let userService: UserServiceMt;
|
|
|
|
|
+ let testToken: string;
|
|
|
|
|
+ let testUser: any;
|
|
|
|
|
+
|
|
|
|
|
+ beforeEach(async () => {
|
|
|
|
|
+ // 创建测试客户端
|
|
|
|
|
+ client = testClient(systemConfigRoutesMt);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取数据源
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ if (!dataSource) throw new Error('Database not initialized');
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化服务
|
|
|
|
|
+ userService = new UserServiceMt(dataSource);
|
|
|
|
|
+ authService = new AuthService(userService);
|
|
|
|
|
+
|
|
|
|
|
+ // 创建测试用户并生成token
|
|
|
|
|
+ testUser = await TestDataFactory.createTestUser(dataSource, {
|
|
|
|
|
+ username: 'testuser_systemconfig',
|
|
|
|
|
+ password: 'TestPassword123!',
|
|
|
|
|
+ email: 'testuser_systemconfig@example.com'
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 生成测试用户的token
|
|
|
|
|
+ testToken = authService.generateToken(testUser);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('CRUD Operations', () => {
|
|
|
|
|
+ it('应该成功创建系统配置', async () => {
|
|
|
|
|
+ const configData = {
|
|
|
|
|
+ configKey: 'app.login.enabled',
|
|
|
|
|
+ configValue: 'true',
|
|
|
|
|
+ description: '控制小程序登录功能是否开启'
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client.index.$post({
|
|
|
|
|
+ json: configData
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(201);
|
|
|
|
|
+ const result = await response.json();
|
|
|
|
|
+ if ('configKey' in result) {
|
|
|
|
|
+ expect(result.configKey).toBe(configData.configKey);
|
|
|
|
|
+ expect(result.configValue).toBe(configData.configValue);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该根据ID获取系统配置', async () => {
|
|
|
|
|
+ // 先创建配置
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ if (!dataSource) throw new Error('Database not initialized');
|
|
|
|
|
+
|
|
|
|
|
+ const systemConfigService = new SystemConfigServiceMt(dataSource);
|
|
|
|
|
+ const config = await systemConfigService.create({
|
|
|
|
|
+ configKey: 'app.payment.enabled',
|
|
|
|
|
+ configValue: 'true',
|
|
|
|
|
+ description: '控制支付功能是否开启',
|
|
|
|
|
+ tenantId: testUser.tenantId
|
|
|
|
|
+ } as SystemConfigMt);
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client[':id'].$get({
|
|
|
|
|
+ param: { id: config.id }
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(200);
|
|
|
|
|
+ const result = await response.json();
|
|
|
|
|
+ if ('id' in result) {
|
|
|
|
|
+ expect(result.id).toBe(config.id);
|
|
|
|
|
+ expect(result.configKey).toBe(config.configKey);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该更新系统配置', async () => {
|
|
|
|
|
+ // 先创建配置
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ if (!dataSource) throw new Error('Database not initialized');
|
|
|
|
|
+
|
|
|
|
|
+ const systemConfigService = new SystemConfigServiceMt(dataSource);
|
|
|
|
|
+ const config = await systemConfigService.create({
|
|
|
|
|
+ configKey: 'app.notification.enabled',
|
|
|
|
|
+ configValue: 'true',
|
|
|
|
|
+ description: '控制通知功能是否开启',
|
|
|
|
|
+ tenantId: testUser.tenantId
|
|
|
|
|
+ } as SystemConfigMt);
|
|
|
|
|
+
|
|
|
|
|
+ const updateData = {
|
|
|
|
|
+ configValue: 'false',
|
|
|
|
|
+ description: '通知功能已关闭'
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client[':id'].$put({
|
|
|
|
|
+ param: { id: config.id },
|
|
|
|
|
+ json: updateData
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(200);
|
|
|
|
|
+ const result = await response.json();
|
|
|
|
|
+ if ('configValue' in result) {
|
|
|
|
|
+ expect(result.configValue).toBe(updateData.configValue);
|
|
|
|
|
+ expect(result.description).toBe(updateData.description);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该删除系统配置', async () => {
|
|
|
|
|
+ // 先创建配置
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ if (!dataSource) throw new Error('Database not initialized');
|
|
|
|
|
+
|
|
|
|
|
+ const systemConfigService = new SystemConfigServiceMt(dataSource);
|
|
|
|
|
+ const config = await systemConfigService.create({
|
|
|
|
|
+ configKey: 'app.analytics.enabled',
|
|
|
|
|
+ configValue: 'true',
|
|
|
|
|
+ description: '控制分析功能是否开启',
|
|
|
|
|
+ tenantId: testUser.tenantId
|
|
|
|
|
+ } as SystemConfigMt);
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client[':id'].$delete({
|
|
|
|
|
+ param: { id: config.id }
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(204);
|
|
|
|
|
+
|
|
|
|
|
+ // 验证配置已被删除
|
|
|
|
|
+ const deletedConfig = await systemConfigService.getById(config.id);
|
|
|
|
|
+ expect(deletedConfig).toBeNull();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该列出系统配置列表', async () => {
|
|
|
|
|
+ // 创建多个配置
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ if (!dataSource) throw new Error('Database not initialized');
|
|
|
|
|
+
|
|
|
|
|
+ const systemConfigService = new SystemConfigServiceMt(dataSource);
|
|
|
|
|
+ await systemConfigService.create({
|
|
|
|
|
+ configKey: 'app.feature1.enabled',
|
|
|
|
|
+ configValue: 'true',
|
|
|
|
|
+ tenantId: testUser.tenantId
|
|
|
|
|
+ } as SystemConfigMt);
|
|
|
|
|
+
|
|
|
|
|
+ await systemConfigService.create({
|
|
|
|
|
+ configKey: 'app.feature2.enabled',
|
|
|
|
|
+ configValue: 'false',
|
|
|
|
|
+ tenantId: testUser.tenantId
|
|
|
|
|
+ } as SystemConfigMt);
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client.index.$get({
|
|
|
|
|
+ query: {}
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(200);
|
|
|
|
|
+ const result = await response.json();
|
|
|
|
|
+ if ('data' in result) {
|
|
|
|
|
+ expect(result.data).toHaveLength(2);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('Multi-tenant Isolation', () => {
|
|
|
|
|
+ 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: 'tenant1_user_systemconfig',
|
|
|
|
|
+ password: 'TestPassword123!',
|
|
|
|
|
+ email: 'tenant1_systemconfig@example.com',
|
|
|
|
|
+ tenantId: 1
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 创建租户2的用户
|
|
|
|
|
+ tenant2User = await TestDataFactory.createTestUser(dataSource, {
|
|
|
|
|
+ username: 'tenant2_user_systemconfig',
|
|
|
|
|
+ password: 'TestPassword123!',
|
|
|
|
|
+ email: 'tenant2_systemconfig@example.com',
|
|
|
|
|
+ tenantId: 2
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 生成租户用户的token
|
|
|
|
|
+ tenant1Token = authService.generateToken(tenant1User);
|
|
|
|
|
+ tenant2Token = authService.generateToken(tenant2User);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该按租户隔离配置', async () => {
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ if (!dataSource) throw new Error('Database not initialized');
|
|
|
|
|
+
|
|
|
|
|
+ const systemConfigService = new SystemConfigServiceMt(dataSource);
|
|
|
|
|
+
|
|
|
|
|
+ // 为租户1创建配置
|
|
|
|
|
+ await systemConfigService.create({
|
|
|
|
|
+ configKey: 'app.shared.config',
|
|
|
|
|
+ configValue: 'tenant1-value',
|
|
|
|
|
+ tenantId: 1
|
|
|
|
|
+ } as SystemConfigMt);
|
|
|
|
|
+
|
|
|
|
|
+ // 为租户2创建配置
|
|
|
|
|
+ await systemConfigService.create({
|
|
|
|
|
+ configKey: 'app.shared.config',
|
|
|
|
|
+ configValue: 'tenant2-value',
|
|
|
|
|
+ tenantId: 2
|
|
|
|
|
+ } as SystemConfigMt);
|
|
|
|
|
+
|
|
|
|
|
+ // 查询租户1的配置
|
|
|
|
|
+ const response1 = await client.index.$get({
|
|
|
|
|
+ query: {}
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${tenant1Token}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response1.status).toBe(200);
|
|
|
|
|
+ const result1 = await response1.json();
|
|
|
|
|
+ if ('data' in result1) {
|
|
|
|
|
+ expect(result1.data).toHaveLength(1);
|
|
|
|
|
+ expect(result1.data[0].configValue).toBe('tenant1-value');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查询租户2的配置
|
|
|
|
|
+ const response2 = await client.index.$get({
|
|
|
|
|
+ query: {}
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${tenant2Token}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response2.status).toBe(200);
|
|
|
|
|
+ const result2 = await response2.json();
|
|
|
|
|
+ if ('data' in result2) {
|
|
|
|
|
+ expect(result2.data).toHaveLength(1);
|
|
|
|
|
+ expect(result2.data[0].configValue).toBe('tenant2-value');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('Data Validation', () => {
|
|
|
|
|
+ it('应该验证必填字段', async () => {
|
|
|
|
|
+ const invalidData = {
|
|
|
|
|
+ configKey: '', // 空字符串
|
|
|
|
|
+ configValue: 'true'
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client.index.$post({
|
|
|
|
|
+ json: invalidData
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(400);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该验证字段长度', async () => {
|
|
|
|
|
+ const invalidData = {
|
|
|
|
|
+ configKey: 'a'.repeat(256), // 超过255字符
|
|
|
|
|
+ configValue: 'true'
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const response = await client.index.$post({
|
|
|
|
|
+ json: invalidData
|
|
|
|
|
+ }, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ expect(response.status).toBe(400);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+});
|