Osc*_*anz 5 redis node.js express typescript jestjs
我正在创建一个 Express API,一切都很顺利,直到我开始遇到一些测试问题。我使用 Jest 和 Supertest 来测试端点,但我不知道为什么在运行测试时开始出现此消息。
\n FAIL src/modules/ingredients/tests/updateingredient.test.ts\n \xe2\x97\x8f Test suite failed to run\n\n A jest worker process (pid=672269) was terminated by another process: signal=SIGSEGV, exitCode=null. Operating system logs may contain more information on why this occurred.\n\n at ChildProcessWorker._onExit (node_modules/jest-worker/build/workers/ChildProcessWorker.js:366:23)\n\n(node:672290) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 open listeners added to [_0x323dee]. Use emitter.setMaxListeners() to increase limit\n(Use `node --trace-warnings ...` to show where the warning was created)\nRun Code Online (Sandbox Code Playgroud)\n我正在使用 Redis 进行缓存和速率限制,我知道它必须与其相关,但我不知道如何修复它。这些错误仅在端点测试时发生。最奇怪的是,这些错误仅在我在 VSCode 打开的情况下运行测试时显示,当我从外部终端运行测试并且 VSCode 关闭时,所有测试都会通过。
\n我已经使用 --detectOpenHandles 运行测试来查看问题出在哪里,当我打开 VSCode 时,输出是:
\nSegmentation fault (core dumped) \nRun Code Online (Sandbox Code Playgroud)\nVSCode 关闭后是:
\nTCPWRAP\n\n 5 | const redisDb = config.get<number>('redis_db');\n 6 |\n > 7 | export const redis = new Redis({\n | ^\n 8 | port: 6379,\n 9 | host: '127.0.0.1',\n 10 | db: redisDb,\n\n at node_modules/ioredis/built/connectors/StandaloneConnector.js:44:21\n at StandaloneConnector.connect (node_modules/ioredis/built/connectors/StandaloneConnector.js:43:16)\n at node_modules/ioredis/built/Redis.js:121:64\n at EventEmitter.connect (node_modules/ioredis/built/Redis.js:103:25)\n at new Redis (node_modules/ioredis/built/Redis.js:77:18)\n at Object.<anonymous> (src/utils/redis.ts:7:22)\n at Object.<anonymous> (src/modules/allergens/tests/listAllergens.test.ts:5:1)\nRun Code Online (Sandbox Code Playgroud)\n我尝试使用 jest hooks 在所有测试之后、所有测试之前以及每次测试之后和之前以几种我发现的方式关闭 Redis 连接,但它不起作用。
\nafterAll(async () => {\n await new Promise((resolve) => {\n redis.quit();\n redis.on('end', resolve);\n });\n});\nRun Code Online (Sandbox Code Playgroud)\n或者
\nafterAll(async () => {\n await redis.quit();\n});\nRun Code Online (Sandbox Code Playgroud)\n我重新安装了 VSCode、Node.js,使用了不同版本的 Node,运行 npm重建,卸载了 VSCode 的 jest 扩展,删除了 node_modules 并安装了 npm,但没有任何效果。
\n这些是我的文件:
\nredis.ts
\nimport Redis from 'ioredis';\nimport config from 'config';\nimport { Logger } from 'src/logger/logger';\n\nconst redisDb = config.get<number>('redis_db');\n\nexport const redis = new Redis({\n port: 6379,\n host: '127.0.0.1',\n db: redisDb,\n});\n\nredis.on('ready', () => Logger.info('redis ready'));\nredis.on('error', (error) => {\n Logger.error(`error with redis connection: ${error}`);\n return redis.disconnect();\n});\nRun Code Online (Sandbox Code Playgroud)\n速率限制器.ts
\nimport { NextFunction, Request, Response } from 'express';\nimport { redis } from 'src/utils/redis';\n\nexport interface Limiter {\n windowSize: number;\n allowedRequests: number;\n}\nexport const rateLimiter =\n ({ windowSize, allowedRequests }: Limiter) =>\n async (req: Request, res: Response, next: NextFunction) => {\n const ip = (req.headers['x-forwarded-for'] || req.socket.remoteAddress) as string;\n\n const formatIp = (ipAddress: string) => {\n if (ipAddress.substring(0, 7) === '::ffff:') {\n return ipAddress.replace('::ffff:', '');\n }\n return ipAddress;\n };\n const formattedIp = formatIp(ip);\n\n const requests = await redis.incr(formattedIp);\n\n if (requests === 1) {\n await redis.expire(formattedIp, windowSize);\n } else {\n await redis.ttl(formattedIp);\n }\n\n if (requests > allowedRequests) {\n return res.status(429).send({ error: 'too many requests' });\n }\n return next();\n };\nRun Code Online (Sandbox Code Playgroud)\n应用程序.ts
\nimport express from 'express';\nimport helmet from 'helmet';\nimport cors from 'cors';\nimport { httpLogger } from './logger/httpLogger';\nimport { Logger } from './logger/logger';\nimport { errorHandler, isTrustedError } from './error/error-handler';\nimport { apiRouter } from './routes';\n\nexport const createApp = () => {\n const app = express();\n\n app.use(helmet());\n app.use(cors());\n app.use(express.json());\n app.use(express.urlencoded({ extended: false }));\n app.use(httpLogger);\n\n app.use('/api/v1', apiRouter);\n\n app.use(errorHandler);\n\n process.on('uncaughtException', async (error: Error) => {\n Logger.error(error);\n if (!isTrustedError(error)) {\n process.exit(1);\n }\n });\n process.on('unhandledRejection', (reason: string) => {\n Logger.error(reason);\n process.exit(1);\n });\n\n return app;\n};\nRun Code Online (Sandbox Code Playgroud)\n路线.ts
\nimport { Request, Response, Router } from 'express';\nimport { rateLimiter } from './middlewares/rateLimiter';\nimport { allergensRouter } from './modules/allergens/allergens.router';\nimport { ingredientsRouter } from './modules/ingredients/ingredients.router';\nimport { recipesRouter } from './modules/recipes/recipes.routes';\n\nexport const apiRouter = Router();\n\napiRouter.get(\n '/health',\n\n (_req: Request, res: Response) => {\n const data = {\n uptime: process.uptime(),\n responseTime: process.hrtime(),\n message: 'ok',\n date: new Date(),\n };\n return res.status(200).send({ data });\n }\n);\napiRouter.use(rateLimiter({ windowSize: 20, allowedRequests: 4 }));\napiRouter.use('/allergens', allergensRouter);\napiRouter.use('/ingredients', ingredientsRouter);\napiRouter.use('/recipes', recipesRouter);\nRun Code Online (Sandbox Code Playgroud)\n成分.router.ts
\nimport { Router } from 'express';\nimport { validate } from 'src/middlewares/validationRequest';\nimport {\n deleteAllIngredients,\n deleteIngredientById,\n findIngredientById,\n findIngredients,\n makeIngredient,\n patchIngredient,\n} from './ingredients.controller';\nimport {\n createIngredientSchema,\n deleteIngredientSchema,\n deleteIngredientByIdSchema,\n getIngredientByIdSchema,\n getIngredientSchema,\n updateIngredientSchema,\n} from './ingredients.schema';\nimport { cache } from 'src/middlewares/cache.middleware';\n\nexport const ingredientsRouter = Router();\n\ningredientsRouter.get('/', validate(getIngredientSchema), cache, findIngredients);\ningredientsRouter.get('/:id', validate(getIngredientByIdSchema), cache, findIngredientById);\n\ningredientsRouter.post('/', validate(createIngredientSchema), makeIngredient);\n\ningredientsRouter.delete('/', validate(deleteIngredientSchema), deleteAllIngredients);\ningredientsRouter.delete('/:id', validate(deleteIngredientByIdSchema), deleteIngredientById);\n\ningredientsRouter.patch('/:id', validate(updateIngredientSchema), patchIngredient);\nRun Code Online (Sandbox Code Playgroud)\n成分.controller.ts
\nimport { NextFunction, Request, Response } from 'express';\nimport { filterProperties } from 'src/utils/filterProperties';\nimport { isValidId } from 'src/utils/idValidation';\nimport { ApiError } from '../../error/ApiError';\nimport { redis } from 'src/utils/redis';\nimport {\n getIngredients,\n getIngredientById,\n getIngredientsByAllergen,\n createIngredient,\n updateIngredient,\n removeIngredientById,\n removeAllIngredients,\n} from './ingredients.service';\n\nexport const findIngredients = async (req: Request, res: Response, next: NextFunction) => {\n const { allergenNames } = req.query;\n\n const properties = ['name', 'category', 'hasAllergens', 'allergens', 'allergenNames'];\n\n const filteredQuery = filterProperties(properties, req.query);\n\n if (allergenNames) {\n const parsedNames = Array.isArray(allergenNames) ? allergenNames : [allergenNames.toString()];\n\n try {\n const foundIngredients = await getIngredientsByAllergen(parsedNames);\n\n redis.setex(`ingredients_allergen_${parsedNames}`, 3600, JSON.stringify(foundIngredients));\n\n return res.status(200).send({ data: foundIngredients });\n } catch (error) {\n return next(error);\n }\n }\n try {\n const foundIngredients = await getIngredients(filteredQuery);\n\n Object.keys(filteredQuery).length > 0\n ? redis.setex(\n `ingredients_${Object.keys(filteredQuery)}`,\n 3600,\n JSON.stringify(foundIngredients)\n )\n : redis.setex('ingredients', 3600, JSON.stringify(foundIngredients));\n\n return res.status(200).send({ data: foundIngredients });\n } catch (error) {\n return next(error);\n }\n};\n... more code\nRun Code Online (Sandbox Code Playgroud)\nlistIngredients.test.ts
\nimport * as IngredientsService from '../ingredients.service';\nimport { IngredientInput } from '../ingredients.model';\nimport { createApp } from '../../../app';\nimport { redis } from 'src/utils/redis';\n\nconst app = createApp();\n\nconst ingredientInput: IngredientInput = {\n name: 'ingredient 1',\n category: 'eggs',\n hasAllergens: true,\n allergens: ['639eea5a049fc933bddebab3'],\n allergenNames: ['celery'],\n};\n\nconst ingredientPayload = {\n _id: '639eea5a049fc933bddebab2',\n name: 'ingredient 1',\n category: 'eggs',\n hasAllergens: true,\n allergens: ['639eea5a049fc933bddebab3'],\n allergenNames: ['celery'],\n};\n\nconst baseApiUrl = '/api/v1/ingredients';\n\nconst getIngredientsServiceMock = jest.spyOn(IngredientsService, 'getIngredients');\n\nconst getIngredientsByAllergenServiceMock = jest.spyOn(\n IngredientsService,\n 'getIngredientsByAllergen'\n);\n\nconst getIngredientByIdServiceMock = jest.spyOn(IngredientsService, 'getIngredientById');\n\nbeforeEach(() => {\n jest.clearAllMocks();\n jest.resetAllMocks();\n redis.flushdb();\n});\n\nafterAll(async () => {\n await redis.quit();\n});\n\n... tests start here\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
1020 次 |
| 最近记录: |