node.js health api请求测试通过zod验证失败?

问题描述 投票:0回答:1

我有一个 Node.js Express 应用程序,我正在尝试使用 vitest 和 supertest 进行测试。但是,我遇到了缺少环境变量的问题。这是我的设置:

环境配置(environment.ts):

import z from 'zod';

const envSchema = z.object({
  HOST: z
    .string()
    .trim()
    .refine(
      host => host.startsWith('http') || host.startsWith('https'),
      'Invalid URL format',
    ),
  PORT: z.coerce
    .number({
      description:
        '.env files convert numbers to strings, therefore we have to enforce them to be numbers',
    })
    .positive()
    .max(65536, `options.port should be >= 0 and < 65536`)
    .default(3000),
  BASE_URL: z.string().trim().min(1).default('/api/v1'),
  DATABASE_URL: z
    .string()
    .url()
    .trim()
    .refine(url => url.startsWith('mongodb://'), 'Invalid Database URL format'),
  GITHUB_API_URL: z
    .string()
    .url()
    .trim()
    .refine(url => url.startsWith('https://'), 'Invalid Github URL format')
    .default('https://api.github.com'),
  GITHUB_SECRET: z.string().trim().min(1),
  LOG_DIR: z.string().optional(),
  CLIENT_URL: z
    .string()
    .url()
    .trim()
    .refine(url => url.startsWith('http://'), 'Invalid client URL format')
    .default('http://localhost:5173/'),
  CORS_ORIGIN: z.string().trim().min(1).default('*'),
  CORS_CREDENTIALS: z.boolean().default(true),
  NODE_ENV: z
    .enum(['development', 'production', 'test'])
    .default('development'),
});

const envServer = envSchema.safeParse({
  HOST: process.env.HOST,
  PORT: process.env.PORT,
  BASE_URL: process.env.BASE_URL,
  DATABASE_URL: process.env.DATABASE_URL,
  REDIS_HOST: process.env.REDIS_HOST,
  REDIS_PORT: process.env.REDIS_PORT,
  REDIS_TTL: process.env.REDIS_TTL,
  REDIS_TIMEOUT: process.env.REDIS_TIMEOUT,
  GITHUB_API_URL: process.env.GITHUB_API_URL,
  GITHUB_SECRET: process.env.GITHUB_SECRET,
  LOG_DIR: process.env.LOG_DIR,
  CLIENT_URL: process.env.CLIENT_URL,
  CORS_ORIGIN: process.env.CORS_ORIGIN,
  CORS_CREDENTIALS: process.env.CORS_CREDENTIALS,
  NODE_ENV: process.env.NODE_ENV,
});

if (!envServer.success) {
  console.error(envServer.error.issues);
  throw new Error('There is an error with the server environment variables');
}

export const serverSchema = envServer.data;

服务器设置(server.ts):


import compression from 'compression';
import cookieParser from 'cookie-parser';
import cors from 'cors';
import { Application, json, urlencoded } from 'express';
import helmet from 'helmet';
import hpp from 'hpp';
import swaggerJSDoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';

import { ErrorMiddleware } from '@/libs/shared/middlewares/error.middleware';

import logger from '@/utils/logger';

import { MongoDBInstance as dbConnection } from '@/config/database';
import { serverSchema } from '@/config/environment';

import applicationRoutes from '@/routes/index';

export class Server {
  private readonly app: Application;

  constructor(app: Application) {
    this.app = app;
  }

  public start(): void {
    this.connectDatabase();
    this.securityMiddleware(this.app);
    this.routesMiddleware(this.app);
    this.globalErrorHandler(this.app);
    this.startServer(this.app);
    this.initializeSwagger();
  }

  public getServer() {
    return this.app;
  }

  private securityMiddleware(app: Application): void {
    app.use(hpp());
    app.use(helmet());
    app.use(compression());
    app.use(json());
    app.use(urlencoded({ extended: true, limit: '50mb' }));
    app.use(cookieParser());
    app.use(
      cors({
        origin: serverSchema.CLIENT_URL,
        credentials: serverSchema.CORS_CREDENTIALS,
        optionsSuccessStatus: 200,
        methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
      }),
    );
  }

  private routesMiddleware(app: Application): void {
    applicationRoutes(app);
  }

  private globalErrorHandler(app: Application): void {
    app.use(ErrorMiddleware);
  }

  private connectDatabase(): void {
    dbConnection.getInstance();
  }

  private startServer(app: Application): void {
    logger.info(`------ NODE_ENV: ${serverSchema.NODE_ENV} ------`);
    logger.info(`Server has started with process ${process.pid}`);
    app.listen(serverSchema.PORT, () => {
      logger.info(`Server listening on port ${serverSchema.PORT}`);
    });
  }

  private initializeSwagger() {
    const options = {
      swaggerDefinition: {
        openapi: '3.0.3',
        info: {
          title: 'API Documentation',
          version: '1.0.0',
          description: 'REST API docs',
        },
        servers: [
          {
            url: `${serverSchema.HOST}:${serverSchema.PORT}${serverSchema.BASE_URL}`,
          },
        ],
      },
      apis: ['./swagger.yaml'],
    };

    const swaggerSpecs = swaggerJSDoc(options);
    this.app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));

    logger.info(
      `Docs are available at ${serverSchema.HOST}:${serverSchema.PORT}/api-docs`,
    );
  }
}

健康控制器(health.controller.ts):

import express, { Application } from 'express';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';

import HealthController from '@/controllers/health.controller';

import { Server } from '@/server';

describe('HealthController', () => {
  let app: Application;

  beforeAll(() => {
    app = new Server(express()).getServer();

    const controller = new HealthController();
    app.get('/health', controller.getHealth);
  });

  it('GET /health should return status 200 and { health: "OK!" } when GET /health', async () => {
    const response = await request(app).get('/health');

    expect(response.status).toEqual(200);
    expect(response.body).toEqual({ health: 'OK!' });
  });
});

路线设置(health.router.ts):

import express, { Router } from 'express';

import HealthController from '@/controllers/health.controller';

export class HealthRoutes {
  private router: Router;

  constructor() {
    this.router = express.Router();
  }

  public getRoutes(): Router {
    const controller = new HealthController();

    this.router.get('/health', controller.getHealth);

    return this.router;
  }
}

export const healthRoutes = new HealthRoutes();

路由器索引.ts:

import { Application } from 'express';

import { serverSchema } from '@/config/environment';

import { healthRoutes } from './health.router';

const applicationRoutes = (app: Application) => {
  const routes = () => {
    app.use(serverSchema.BASE_URL, healthRoutes.getRoutes());
  };

  routes();
};

export default applicationRoutes;

当我运行测试时,出现以下错误:

stderr | src/config/environment.ts:65:11
[
  {
    code: 'invalid_type',
    expected: 'string',
    received: 'undefined',
    path: [ 'HOST' ],
    message: 'Required'
  },
  {
    code: 'invalid_type',
    expected: 'string',
    received: 'undefined',
    path: [ 'DATABASE_URL' ],
    message: 'Required'
  },
  {
    code: 'invalid_type',
    expected: 'string',
    received: 'undefined',
    path: [ 'GITHUB_SECRET' ],
    message: 'Required'
  }
]

似乎某些环境变量丢失或定义不正确。如何确保在测试和应用程序运行时正确加载所有必需的环境变量?

所有环境变量都在我的 .env.development.local 文件中设置。 我在 package.json 脚本中使用 --env-file 来加载环境变量。 zod 模式正确定义并验证变量。

如何确保我的 Node.js 应用程序在测试和运行时正确加载并验证所有必需的环境变量?

我遗漏了什么或做错了什么?

node.js testing zod
1个回答
0
投票

好吧,我刚刚发现错误发生是因为我没有为 zod 模式中的那些 env 变量设置默认值。

我添加了默认值并且测试正确通过。

© www.soinside.com 2019 - 2024. All rights reserved.