import { NextRequest } from 'next/server' import { Readable } from 'stream' import { GET } from '../route' jest.mock('@/lib/auth', () => ({ authOptions: {}, })) jest.mock('next-auth', () => ({ getServerSession: jest.fn(), })) jest.mock('@/lib/prisma', () => ({ prisma: { material: { findFirst: jest.fn(), }, }, })) jest.mock('@/lib/s3', () => ({ getS3ObjectStream: jest.fn(), isS3Url: jest.fn((value?: string | null) => /^https?:\/\//.test(value || '')), })) jest.mock('@/lib/material-share-token', () => ({ findValidMaterialShareToken: jest.fn(), })) jest.mock('@/lib/public-share-settings', () => ({ isPublicShareEnabled: jest.fn(), })) const { getServerSession } = require('next-auth') const { prisma } = require('@/lib/prisma') const { getS3ObjectStream, isS3Url } = require('@/lib/s3') const { findValidMaterialShareToken } = require('@/lib/material-share-token') const { isPublicShareEnabled } = require('@/lib/public-share-settings') describe('GET /api/materials/stream', () => { beforeEach(() => { jest.resetAllMocks() isS3Url.mockImplementation((value?: string | null) => /^https?:\/\//.test(value || '')) isPublicShareEnabled.mockResolvedValue(true) }) it('未登录且没有有效分享票据时拒绝播放', async () => { prisma.material.findFirst.mockResolvedValue({ id: 'material-1', zipFilePath: 'http://minio:9000/bucket/video.mp4', zipFileName: 'video.mp4', materialType: '视频', materialKind: 'VIDEO', }) getServerSession.mockResolvedValue(null) findValidMaterialShareToken.mockResolvedValue(null) const response = await GET(new NextRequest('http://localhost/api/materials/stream?id=material-1')) const data = await response.json() expect(response.status).toBe(401) expect(data.error).toBe('请先登录或使用有效分享链接') expect(getS3ObjectStream).not.toHaveBeenCalled() }) it('未登录但分享票据有效时允许播放', async () => { prisma.material.findFirst.mockResolvedValue({ id: 'material-1', zipFilePath: 'http://minio:9000/bucket/video.mp4', zipFileName: 'video.mp4', materialType: '视频', materialKind: 'VIDEO', }) getServerSession.mockResolvedValue(null) findValidMaterialShareToken.mockResolvedValue({ id: 'token-row', materialId: 'material-1' }) getS3ObjectStream.mockResolvedValue({ body: Readable.from([Buffer.from('video-body')]), contentLength: 10, contentType: 'video/mp4', acceptRanges: 'bytes', }) const response = await GET( new NextRequest('http://localhost/api/materials/stream?id=material-1&shareToken=share-token') ) const body = await response.text() expect(response.status).toBe(200) expect(body).toBe('video-body') expect(findValidMaterialShareToken).toHaveBeenCalledWith('share-token', 'material-1') expect(response.headers.get('access-control-allow-origin')).toBeNull() }) it('公开分享开关关闭时拒绝通过分享票据播放', async () => { prisma.material.findFirst.mockResolvedValue({ id: 'material-1', zipFilePath: 'http://minio:9000/bucket/video.mp4', zipFileName: 'video.mp4', materialType: '视频', materialKind: 'VIDEO', }) getServerSession.mockResolvedValue(null) isPublicShareEnabled.mockResolvedValue(false) const response = await GET( new NextRequest('http://localhost/api/materials/stream?id=material-1&shareToken=share-token') ) const data = await response.json() expect(response.status).toBe(403) expect(data.error).toBe('公开分享功能已关闭') expect(findValidMaterialShareToken).not.toHaveBeenCalled() expect(getS3ObjectStream).not.toHaveBeenCalled() }) })