From da023759a152ae57ac590cd36b32372013113811 Mon Sep 17 00:00:00 2001 From: xiong Date: Wed, 22 Jul 2026 11:17:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=90=8E=E7=AB=AF=20C?= =?UTF-8?q?I=20=E6=A3=80=E6=9F=A5=E4=B8=8E=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/ci.yml | 2 +- apps/server/eslint.config.mjs | 25 ++++++++++++++++++- apps/server/package.json | 3 ++- .../src/ai-config/ai-config.service.spec.ts | 12 +++++++++ .../classroom-rentals.service.spec.ts | 19 +++++++++----- .../src/database/database-migrations.spec.ts | 8 +++--- 6 files changed, 56 insertions(+), 13 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index e4a3a34..265fde3 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: - name: Lint run: | npm run lint -w @gongxue/admin - npm run lint -w @gongxue/server -- --no-fix + npm run lint -w @gongxue/server -- --quiet - name: Type check run: npm run typecheck diff --git a/apps/server/eslint.config.mjs b/apps/server/eslint.config.mjs index 2058fd0..903fd5f 100644 --- a/apps/server/eslint.config.mjs +++ b/apps/server/eslint.config.mjs @@ -5,7 +5,7 @@ import tseslint from 'typescript-eslint'; export default tseslint.config( { - ignores: ['eslint.config.mjs'], + ignores: ['eslint.config.mjs', 'dist/**'], }, eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, @@ -27,6 +27,29 @@ export default tseslint.config( '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-floating-promises': 'warn', '@typescript-eslint/no-unsafe-argument': 'warn', + '@typescript-eslint/no-unsafe-assignment': 'warn', + '@typescript-eslint/no-unsafe-call': 'warn', + '@typescript-eslint/no-unsafe-enum-comparison': 'warn', + '@typescript-eslint/no-unsafe-member-access': 'warn', + '@typescript-eslint/no-unsafe-return': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/no-base-to-string': 'warn', + '@typescript-eslint/no-require-imports': 'warn', + '@typescript-eslint/no-unnecessary-type-assertion': 'warn', + '@typescript-eslint/restrict-template-expressions': 'warn', + 'no-empty': ['warn', { allowEmptyCatch: true }], + 'no-useless-escape': 'warn', }, }, + { + files: ['**/*.spec.ts', '**/*.test.ts', 'test/**/*.ts'], + extends: [tseslint.configs.disableTypeChecked], + }, ); diff --git a/apps/server/package.json b/apps/server/package.json index f35d955..3ba970e 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -14,7 +14,8 @@ "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "generate:student-import": "ts-node -r tsconfig-paths/register -P tsconfig.json scripts/generate-student-import-xlsx.ts", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"", + "lint:fix": "npm run lint -- --fix", "typecheck": "tsc -p tsconfig.build.json --noEmit", "test": "jest", "test:watch": "jest --watch", diff --git a/apps/server/src/ai-config/ai-config.service.spec.ts b/apps/server/src/ai-config/ai-config.service.spec.ts index 2a0058b..de7f4ff 100644 --- a/apps/server/src/ai-config/ai-config.service.spec.ts +++ b/apps/server/src/ai-config/ai-config.service.spec.ts @@ -8,6 +8,16 @@ import { randomBytes, } from 'node:crypto'; +jest.mock('node:dns', () => ({ + lookup: jest.fn( + ( + _hostname: string, + _options: unknown, + callback: (error: null, addresses: Array<{ address: string; family: number }>) => void, + ) => callback(null, [{ address: '203.0.113.10', family: 4 }]), + ), +})); + import { AiConfigService } from './ai-config.service'; import { AiConfig, AiProvider, SINGLETON_KEY } from './ai-config.entity'; @@ -105,6 +115,8 @@ describe('AiConfigService', () => { afterEach(() => { delete process.env.AI_CONFIG_ENCRYPTION_KEY; delete process.env.AI_API_KEY; + delete process.env.AI_ALLOW_PRIVATE_BASE_URL; + delete process.env.NODE_ENV; }); // ── Encryption ──────────────────────────────────────────────────────── diff --git a/apps/server/src/classroom-rentals/classroom-rentals.service.spec.ts b/apps/server/src/classroom-rentals/classroom-rentals.service.spec.ts index e5058bc..1378587 100644 --- a/apps/server/src/classroom-rentals/classroom-rentals.service.spec.ts +++ b/apps/server/src/classroom-rentals/classroom-rentals.service.spec.ts @@ -362,7 +362,7 @@ describe('ClassroomRentalsService — rental schedule sync', () => { expect(scheduleRepo.delete).not.toHaveBeenCalled(); }); - it('deletes the RENTAL schedule row when the rental is cancelled', async () => { + it('deactivates the RENTAL schedule row when the rental is cancelled', async () => { const rental = { id: 1, classroomId: 1, @@ -379,7 +379,10 @@ describe('ClassroomRentalsService — rental schedule sync', () => { await service.cancel(1); expect(rentalRepo.update).toHaveBeenCalledWith(1, { status: 'cancelled' }); - expect(scheduleRepo.delete).toHaveBeenCalledWith({ rentalId: 1, scheduleType: 'RENTAL' }); + expect(scheduleRepo.update).toHaveBeenCalledWith( + { rentalId: 1, scheduleType: 'RENTAL' }, + { status: 'inactive' }, + ); }); }); @@ -419,14 +422,14 @@ describe('ClassroomRentalsService — rental schedule sync', () => { }); describe('remove()', () => { - it('deletes the RENTAL schedule row and the rental', async () => { + it('archives the rental and deactivates its RENTAL schedule row', async () => { const rental = { id: 1, classroomId: 1, lesseeOrganizationId: 2, startDate: '2026-03-01', endDate: '2026-03-31', - status: 'cancelled', + status: 'active', lesseeOrganization: { id: 2, name: 'Organization A' } as Organization, } as ClassroomRental; @@ -434,8 +437,12 @@ describe('ClassroomRentalsService — rental schedule sync', () => { await service.remove(1); - expect(scheduleRepo.delete).toHaveBeenCalledWith({ rentalId: 1, scheduleType: 'RENTAL' }); - expect(rentalRepo.delete).toHaveBeenCalledWith(1); + expect(rentalRepo.update).toHaveBeenCalledWith(1, { status: 'cancelled' }); + expect(scheduleRepo.update).toHaveBeenCalledWith( + { rentalId: 1, scheduleType: 'RENTAL' }, + { status: 'inactive' }, + ); + expect(rentalRepo.delete).not.toHaveBeenCalled(); }); }); }); diff --git a/apps/server/src/database/database-migrations.spec.ts b/apps/server/src/database/database-migrations.spec.ts index dcdd7f7..1d218c0 100644 --- a/apps/server/src/database/database-migrations.spec.ts +++ b/apps/server/src/database/database-migrations.spec.ts @@ -197,7 +197,7 @@ describe('DatabaseMigrationsService — course attendance schema', () => { ], getTable: { name: 'attendance_records', columns: [{ name: 'id' }] }, }); - await bootstrapCourseAttendance(runner); + const service = await bootstrapCourseAttendance(runner); await service.ensureCourseAttendanceSchema(); @@ -223,7 +223,7 @@ describe('DatabaseMigrationsService — course attendance schema', () => { ? { name, columns: [{ name: 'id' }] } : { name, columns: [{ name: 'id' }, { name: 'schedule_id' }, { name: 'attendance_session_id' }] }, ); - await bootstrapCourseAttendance(runner); + const service = await bootstrapCourseAttendance(runner); await service.ensureCourseAttendanceSchema(); @@ -237,7 +237,7 @@ describe('DatabaseMigrationsService — course attendance schema', () => { getTables: [{ name: 'attendance_records', columns: [{ name: 'id' }] }], getTable: { name: 'attendance_records', columns: [{ name: 'id' }] }, }); - await bootstrapCourseAttendance(runner); + const service = await bootstrapCourseAttendance(runner); await service.ensureCourseAttendanceSchema(); const createSql: string = (runner.query as jest.Mock).mock.calls @@ -497,5 +497,5 @@ async function bootstrapCourseAttendance(runner: MockRunner) { { provide: getDataSourceToken(), useValue: dataSource }, ], }).compile(); - service = module.get(DatabaseMigrationsService); + return module.get(DatabaseMigrationsService); }