fix: remove unused imports and fix logger test isolation

- Remove unused imports (run, trackDuration, PerformanceTracker,
  ConfigManager, disconnectDb) flagged by ESLint
- Remove unused isSlow variable in performance-monitor catch block
- Add eslint-disable for require() in Playwright JS script
- Fix logger-performance test flakiness by using vi.resetModules()
  with dynamic imports to prevent cached logger references across
  test files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-04-05 12:15:18 +08:00
parent e54d94fce2
commit e2669af870
14 changed files with 157 additions and 66 deletions

View File

@@ -20,7 +20,7 @@ import { dirname } from 'path'
import { app } from 'electron' import { app } from 'electron'
import yaml from 'js-yaml' import yaml from 'js-yaml'
import { z } from 'zod' import { z } from 'zod'
import { createLogger, applyLoggingConfig, trackDuration } from '../logger' import { createLogger, applyLoggingConfig } from '../logger'
import { applyAuditConfig } from '../logger/audit-logger' import { applyAuditConfig } from '../logger/audit-logger'
import { import {
fullConfigSchema, fullConfigSchema,
@@ -315,9 +315,12 @@ export class ConfigManager {
const { activeType, mysql, sqlserver, postgresql } = this.config.database const { activeType, mysql, sqlserver, postgresql } = this.config.database
switch (activeType) { switch (activeType) {
case 'postgresql': return postgresql case 'postgresql':
case 'sqlserver': return sqlserver return postgresql
default: return mysql case 'sqlserver':
return sqlserver
default:
return mysql
} }
} }

View File

@@ -22,9 +22,12 @@ function getDatabaseType(): 'mysql' | 'mssql' | 'postgres' {
const configManager = ConfigManager.getInstance() const configManager = ConfigManager.getInstance()
const dbType = configManager.getDatabaseType() const dbType = configManager.getDatabaseType()
switch (dbType) { switch (dbType) {
case 'sqlserver': return 'mssql' case 'sqlserver':
case 'postgresql': return 'postgres' return 'mssql'
default: return 'mysql' case 'postgresql':
return 'postgres'
default:
return 'mysql'
} }
} }

View File

@@ -10,7 +10,7 @@
import { create, type IDatabaseService } from './index' import { create, type IDatabaseService } from './index'
import { createDialect, type SqlDialect } from './dialects' import { createDialect, type SqlDialect } from './dialects'
import { createLogger, run, getRequestId, trackDuration } from '../logger' import { createLogger, getRequestId, trackDuration } from '../logger'
const log = createLogger('DiscreteMaterialPlanDAO') const log = createLogger('DiscreteMaterialPlanDAO')

View File

@@ -11,7 +11,7 @@
import { create, type IDatabaseService } from './index' import { create, type IDatabaseService } from './index'
import { createDialect, type SqlDialect } from './dialects' import { createDialect, type SqlDialect } from './dialects'
import { createLogger, run, getRequestId, trackDuration } from '../logger' import { createLogger, getRequestId, trackDuration } from '../logger'
import type { import type {
OperationHistoryRecord, OperationHistoryRecord,
BatchStats, BatchStats,
@@ -202,7 +202,7 @@ export class ExtractorOperationHistoryDAO {
` `
const params = [status, batchId] const params = [status, batchId]
const result = await trackDuration(async () => await dbService.query(sqlString, params), { await trackDuration(async () => await dbService.query(sqlString, params), {
operationName: 'ExtractorOperationHistoryDAO.updateBatchStatus', operationName: 'ExtractorOperationHistoryDAO.updateBatchStatus',
context: { tableName, operationType: 'UPDATE', batchId } context: { tableName, operationType: 'UPDATE', batchId }
}) })

View File

@@ -10,7 +10,7 @@
import { create, type IDatabaseService } from './index' import { create, type IDatabaseService } from './index'
import { createDialect, type SqlDialect } from './dialects' import { createDialect, type SqlDialect } from './dialects'
import { createLogger, run, getRequestId, trackDuration } from '../logger' import { createLogger, getRequestId, trackDuration } from '../logger'
const log = createLogger('MaterialsToBeDeletedDAO') const log = createLogger('MaterialsToBeDeletedDAO')

View File

@@ -7,7 +7,7 @@
import { create, type IDatabaseService } from './index' import { create, type IDatabaseService } from './index'
import { createDialect, type SqlDialect } from './dialects' import { createDialect, type SqlDialect } from './dialects'
import { createLogger, run, getRequestId, trackDuration } from '../logger' import { createLogger, getRequestId, trackDuration } from '../logger'
const log = createLogger('MaterialsTypeToBeDeletedDAO') const log = createLogger('MaterialsTypeToBeDeletedDAO')

View File

@@ -18,37 +18,123 @@ export type { PostgreSqlConfig } from '../../types/database.types'
*/ */
const SQL_KEYWORDS = new Set([ const SQL_KEYWORDS = new Set([
// DML // DML
'SELECT', 'FROM', 'WHERE', 'AND', 'OR', 'NOT', 'IN', 'IS', 'NULL', 'SELECT',
'INSERT', 'INTO', 'VALUES', 'UPDATE', 'SET', 'DELETE', 'FROM',
'WHERE',
'AND',
'OR',
'NOT',
'IN',
'IS',
'NULL',
'INSERT',
'INTO',
'VALUES',
'UPDATE',
'SET',
'DELETE',
// Ordering & limiting // Ordering & limiting
'ORDER', 'BY', 'ASC', 'DESC', 'LIMIT', 'OFFSET', 'ORDER',
'FETCH', 'NEXT', 'ROWS', 'ONLY', 'BY',
'ASC',
'DESC',
'LIMIT',
'OFFSET',
'FETCH',
'NEXT',
'ROWS',
'ONLY',
// Joins // Joins
'JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER', 'CROSS', 'FULL', 'ON', 'JOIN',
'LEFT',
'RIGHT',
'INNER',
'OUTER',
'CROSS',
'FULL',
'ON',
// Set operations // Set operations
'UNION', 'ALL', 'INTERSECT', 'EXCEPT', 'UNION',
'ALL',
'INTERSECT',
'EXCEPT',
// Grouping // Grouping
'GROUP', 'HAVING', 'DISTINCT', 'GROUP',
'HAVING',
'DISTINCT',
// DDL // DDL
'CREATE', 'ALTER', 'DROP', 'TABLE', 'INDEX', 'COLUMN', 'CREATE',
'ADD', 'MODIFY', 'RENAME', 'TO', 'ALTER',
'DROP',
'TABLE',
'INDEX',
'COLUMN',
'ADD',
'MODIFY',
'RENAME',
'TO',
// PostgreSQL specific // PostgreSQL specific
'CONFLICT', 'DO', 'NOTHING', 'EXCLUDED', 'RETURNING', 'CONFLICT',
'MERGE', 'USING', 'MATCHED', 'WHEN', 'THEN', 'ELSE', 'END', 'DO',
'TARGET', 'SOURCE', 'NOTHING',
'EXCLUDED',
'RETURNING',
'MERGE',
'USING',
'MATCHED',
'WHEN',
'THEN',
'ELSE',
'END',
'TARGET',
'SOURCE',
// Functions // Functions
'COUNT', 'SUM', 'AVG', 'MIN', 'MAX', 'EXISTS', 'COUNT',
'CURRENT_TIMESTAMP', 'NOW', 'GETDATE', 'SUM',
'COALESCE', 'NULLIF', 'CAST', 'AS', 'AVG',
'MIN',
'MAX',
'EXISTS',
'CURRENT_TIMESTAMP',
'NOW',
'GETDATE',
'COALESCE',
'NULLIF',
'CAST',
'AS',
// Transaction // Transaction
'BEGIN', 'COMMIT', 'ROLLBACK', 'SAVEPOINT', 'BEGIN',
'COMMIT',
'ROLLBACK',
'SAVEPOINT',
// Types & values // Types & values
'TRUE', 'FALSE', 'DEFAULT', 'PRIMARY', 'KEY', 'TRUE',
'REFERENCES', 'FOREIGN', 'CONSTRAINT', 'UNIQUE', 'CHECK', 'FALSE',
'CASE', 'BETWEEN', 'LIKE', 'ILIKE', 'ANY', 'SOME', 'DEFAULT',
'PRIMARY',
'KEY',
'REFERENCES',
'FOREIGN',
'CONSTRAINT',
'UNIQUE',
'CHECK',
'CASE',
'BETWEEN',
'LIKE',
'ILIKE',
'ANY',
'SOME',
// Common // Common
'IF', 'WITH', 'RECURSIVE', 'OVER', 'PARTITION', 'WINDOW', 'IF',
'ROW', 'FIRST', 'AFTER', 'BEFORE' 'WITH',
'RECURSIVE',
'OVER',
'PARTITION',
'WINDOW',
'ROW',
'FIRST',
'AFTER',
'BEFORE'
]) ])
/** /**

View File

@@ -119,7 +119,6 @@ export async function trackDuration<T>(
return { result, durationMs, isSlow } return { result, durationMs, isSlow }
} catch (error) { } catch (error) {
const durationMs = performance.now() - startTime const durationMs = performance.now() - startTime
const isSlow = durationMs > slowThresholdMs
// Log the error with duration // Log the error with duration
logger.error(`${message} failed after ${durationMs.toFixed(2)}ms`, { logger.error(`${message} failed after ${durationMs.toFixed(2)}ms`, {

View File

@@ -15,7 +15,7 @@ import {
type GetObjectCommandInput, type GetObjectCommandInput,
type DeleteObjectCommandInput type DeleteObjectCommandInput
} from '@aws-sdk/client-s3' } from '@aws-sdk/client-s3'
import { createLogger, run, trackDuration, PerformanceTracker } from '../logger' import { createLogger } from '../logger'
import type { RustfsConfig } from '../../types/config.schema' import type { RustfsConfig } from '../../types/config.schema'
import * as fs from 'fs' import * as fs from 'fs'
import * as path from 'path' import * as path from 'path'

View File

@@ -1,6 +1,6 @@
import * as fs from 'fs' import * as fs from 'fs'
import { ConfigManager } from '../config/config-manager' import { ConfigManager } from '../config/config-manager'
import { createLogger, run, trackDuration, PerformanceTracker } from '../logger' import { createLogger } from '../logger'
import type { UpdateConfig } from '../../types/config.schema' import type { UpdateConfig } from '../../types/config.schema'
import type { UserType } from '../../types/user.types' import type { UserType } from '../../types/user.types'
import type { import type {

View File

@@ -8,13 +8,8 @@
* - Create, update, delete users * - Create, update, delete users
*/ */
import { import { create, type IDatabaseService } from '../database/index'
create,
disconnect as disconnectDb,
type IDatabaseService
} from '../database/index'
import { createDialect, type SqlDialect } from '../database/dialects' import { createDialect, type SqlDialect } from '../database/dialects'
import { ConfigManager } from '../config/config-manager'
import type { UserInfo } from '../../types/user.types' import type { UserInfo } from '../../types/user.types'
import { createLogger, logError } from '../logger' import { createLogger, logError } from '../logger'

View File

@@ -5,14 +5,6 @@
*/ */
import { describe, it, expect, vi, beforeEach, afterEach, type Mock } from 'vitest' import { describe, it, expect, vi, beforeEach, afterEach, type Mock } from 'vitest'
import {
trackDuration,
PerformanceTracker,
createPerformanceTracker,
DEFAULT_SLOW_THRESHOLD_MS,
type TrackDurationOptions
} from '../../src/main/services/logger/performance-monitor'
import logger from '../../src/main/services/logger/index'
// Mock the logger to avoid noisy output during tests // Mock the logger to avoid noisy output during tests
vi.mock('../../src/main/services/logger', () => ({ vi.mock('../../src/main/services/logger', () => ({
@@ -26,7 +18,23 @@ vi.mock('../../src/main/services/logger', () => ({
})) }))
describe('Performance Monitor', () => { describe('Performance Monitor', () => {
beforeEach(() => { let trackDuration: typeof import('../../src/main/services/logger/performance-monitor').trackDuration
let PerformanceTracker: typeof import('../../src/main/services/logger/performance-monitor').PerformanceTracker
let createPerformanceTracker: typeof import('../../src/main/services/logger/performance-monitor').createPerformanceTracker
let DEFAULT_SLOW_THRESHOLD_MS: typeof import('../../src/main/services/logger/performance-monitor').DEFAULT_SLOW_THRESHOLD_MS
let logger: typeof import('../../src/main/services/logger').default
beforeEach(async () => {
vi.resetModules()
const perfMod = await import('../../src/main/services/logger/performance-monitor')
const loggerMod = await import('../../src/main/services/logger/index')
trackDuration = perfMod.trackDuration
PerformanceTracker = perfMod.PerformanceTracker
createPerformanceTracker = perfMod.createPerformanceTracker
DEFAULT_SLOW_THRESHOLD_MS = perfMod.DEFAULT_SLOW_THRESHOLD_MS
logger = loggerMod.default
vi.clearAllMocks() vi.clearAllMocks()
}) })
@@ -230,7 +238,7 @@ describe('Performance Monitor', () => {
}) })
}) })
it('should log summary with aggregated metrics', () => { it('should log summary with aggregated metrics', async () => {
const tracker = new PerformanceTracker('TestService', 1000) const tracker = new PerformanceTracker('TestService', 1000)
tracker.recordDuration(100) tracker.recordDuration(100)
@@ -248,7 +256,7 @@ describe('Performance Monitor', () => {
}) })
}) })
it('should include slow percentage in summary', () => { it('should include slow percentage in summary', async () => {
const tracker = new PerformanceTracker('TestService', 50) const tracker = new PerformanceTracker('TestService', 50)
tracker.recordDuration(30) // Normal tracker.recordDuration(30) // Normal
@@ -261,7 +269,7 @@ describe('Performance Monitor', () => {
expect(summaryCall.slowPercentage).toContain('%') expect(summaryCall.slowPercentage).toContain('%')
}) })
it('should reset metrics when reset() is called', () => { it('should reset metrics when reset() is called', async () => {
const tracker = new PerformanceTracker('TestService', 1000) const tracker = new PerformanceTracker('TestService', 1000)
tracker.recordDuration(100) tracker.recordDuration(100)
@@ -275,7 +283,7 @@ describe('Performance Monitor', () => {
expect(metrics.slowOperationCount).toBe(0) expect(metrics.slowOperationCount).toBe(0)
}) })
it('should return zero metrics when no operations tracked', () => { it('should return zero metrics when no operations tracked', async () => {
const tracker = new PerformanceTracker('EmptyService') const tracker = new PerformanceTracker('EmptyService')
const metrics = tracker.getMetrics() const metrics = tracker.getMetrics()
@@ -288,7 +296,7 @@ describe('Performance Monitor', () => {
expect(metrics.slowOperationCount).toBe(0) expect(metrics.slowOperationCount).toBe(0)
}) })
it('should use custom logger if provided', () => { it('should use custom logger if provided', async () => {
const customLogger = { const customLogger = {
debug: vi.fn(), debug: vi.fn(),
info: vi.fn(), info: vi.fn(),
@@ -307,7 +315,7 @@ describe('Performance Monitor', () => {
}) })
describe('createPerformanceTracker', () => { describe('createPerformanceTracker', () => {
it('should create a tracker with default threshold', () => { it('should create a tracker with default threshold', async () => {
const tracker = createPerformanceTracker('MyService') const tracker = createPerformanceTracker('MyService')
expect(tracker).toBeInstanceOf(PerformanceTracker) expect(tracker).toBeInstanceOf(PerformanceTracker)
@@ -315,7 +323,7 @@ describe('Performance Monitor', () => {
expect(metrics.count).toBe(0) expect(metrics.count).toBe(0)
}) })
it('should create a tracker with custom threshold', () => { it('should create a tracker with custom threshold', async () => {
const tracker = createPerformanceTracker('FastService', 100) const tracker = createPerformanceTracker('FastService', 100)
tracker.recordDuration(150) tracker.recordDuration(150)

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-require-imports */
const { _electron: electron } = require('playwright') const { _electron: electron } = require('playwright')
;(async () => { ;(async () => {

View File

@@ -132,7 +132,7 @@ describe('prepareSql', () => {
it('should preserve string literals with escaped quotes', () => { it('should preserve string literals with escaped quotes', () => {
const sql = "WHERE UserName = 'O''Brien'" const sql = "WHERE UserName = 'O''Brien'"
const result = prepareSql(sql) const result = prepareSql(sql)
expect(result).toBe('WHERE "UserName" = \'O\'\'Brien\'') expect(result).toBe("WHERE \"UserName\" = 'O''Brien'")
}) })
it('should preserve $N parameter placeholders', () => { it('should preserve $N parameter placeholders', () => {
@@ -145,17 +145,13 @@ describe('prepareSql', () => {
it('should handle COUNT(*) correctly', () => { it('should handle COUNT(*) correctly', () => {
const sql = 'SELECT COUNT(*) as count FROM "dbo"."BIPUsers" WHERE UserName = $1' const sql = 'SELECT COUNT(*) as count FROM "dbo"."BIPUsers" WHERE UserName = $1'
const result = prepareSql(sql) const result = prepareSql(sql)
expect(result).toBe( expect(result).toBe('SELECT COUNT(*) as count FROM "dbo"."BIPUsers" WHERE "UserName" = $1')
'SELECT COUNT(*) as count FROM "dbo"."BIPUsers" WHERE "UserName" = $1'
)
}) })
it('should quote underscore-containing column names', () => { it('should quote underscore-containing column names', () => {
const sql = 'SELECT ERP_URL, ERP_Username, ERP_Password FROM "dbo"."BIPUsers"' const sql = 'SELECT ERP_URL, ERP_Username, ERP_Password FROM "dbo"."BIPUsers"'
const result = prepareSql(sql) const result = prepareSql(sql)
expect(result).toBe( expect(result).toBe('SELECT "ERP_URL", "ERP_Username", "ERP_Password" FROM "dbo"."BIPUsers"')
'SELECT "ERP_URL", "ERP_Username", "ERP_Password" FROM "dbo"."BIPUsers"'
)
}) })
it('should handle ON CONFLICT DO UPDATE SET with EXCLUDED', () => { it('should handle ON CONFLICT DO UPDATE SET with EXCLUDED', () => {
@@ -168,7 +164,7 @@ describe('prepareSql', () => {
}) })
it('should handle CURRENT_TIMESTAMP without quoting', () => { it('should handle CURRENT_TIMESTAMP without quoting', () => {
const sql = "INSERT INTO t (OperationTime) VALUES (CURRENT_TIMESTAMP)" const sql = 'INSERT INTO t (OperationTime) VALUES (CURRENT_TIMESTAMP)'
const result = prepareSql(sql) const result = prepareSql(sql)
expect(result).toBe('INSERT INTO "t" ("OperationTime") VALUES (CURRENT_TIMESTAMP)') expect(result).toBe('INSERT INTO "t" ("OperationTime") VALUES (CURRENT_TIMESTAMP)')
}) })