import type { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import request from 'supertest'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { ShortTermMarketController } from '../src/market-overview/short-term.controller'; import { AKTOOLS_CLIENT_PORT, ShortTermMarketService, } from '../src/market-overview/short-term.service'; describe('短线行情 HTTP API', () => { let app: INestApplication | undefined; beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date('2026-08-24T03:00:00.000Z')); }); afterEach(async () => { try { await app?.close(); } finally { app = undefined; vi.useRealTimers(); } }); it('返回结构化超短情绪指标和 AKShare 来源', async () => { const client = { getLimitUpPool: vi.fn().mockResolvedValue([{ 代码: '000001', 连板数: 4 }]), getBrokenBoardPool: vi.fn().mockResolvedValue([{ 代码: '000002' }]), getPreviousLimitUpPool: vi.fn().mockResolvedValue([{ 代码: '000003', 涨跌幅: 1.5 }]), }; const moduleRef = await Test.createTestingModule({ controllers: [ShortTermMarketController], providers: [ShortTermMarketService, { provide: AKTOOLS_CLIENT_PORT, useValue: client }], }).compile(); app = moduleRef.createNestApplication(); app.setGlobalPrefix('api/v1'); await app.init(); const response = await request(app.getHttpServer()) .get('/api/v1/market-overview/short-term/emotion') .expect(200); expect(response.body).toMatchObject({ source: 'akshare', status: 'fresh', tradeDate: '20260824', data: { limitUpCount: 1, maxBoard: 4, brokenBoardCount: 1 }, }); expect(JSON.stringify(response.body)).not.toContain('127.0.0.1'); }); it('全部 AKTools 数据不可用时返回 unavailable 而不是伪造空数据', async () => { const client = { getLimitUpPool: vi.fn().mockRejectedValue(new Error('private detail')), getBrokenBoardPool: vi.fn().mockRejectedValue(new Error('private detail')), getPreviousLimitUpPool: vi.fn().mockRejectedValue(new Error('private detail')), }; const moduleRef = await Test.createTestingModule({ controllers: [ShortTermMarketController], providers: [ShortTermMarketService, { provide: AKTOOLS_CLIENT_PORT, useValue: client }], }).compile(); app = moduleRef.createNestApplication(); app.setGlobalPrefix('api/v1'); await app.init(); const response = await request(app.getHttpServer()) .get('/api/v1/market-overview/short-term/conclusion') .expect(200); expect(response.body).toMatchObject({ source: 'akshare', status: 'unavailable', data: null }); expect(JSON.stringify(response.body)).not.toContain('private detail'); }); });