chore: import upstream snapshot with attribution
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
|
||||
import { setDownloadHeaders } from '../../../src/server/utils/downloadHelpers';
|
||||
import type { Response } from 'express';
|
||||
|
||||
describe('downloadHelpers', () => {
|
||||
describe('setDownloadHeaders', () => {
|
||||
let mockRes: Partial<Response>;
|
||||
let setHeaderMock: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
setHeaderMock = vi.fn().mockReturnThis();
|
||||
mockRes = {
|
||||
setHeader: setHeaderMock,
|
||||
} as Partial<Response>;
|
||||
});
|
||||
|
||||
it('should set correct headers for CSV download', () => {
|
||||
setDownloadHeaders(mockRes as Response, 'test-file.csv', 'text/csv');
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledWith('Content-Type', 'text/csv');
|
||||
expect(setHeaderMock).toHaveBeenCalledWith(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="test-file.csv"',
|
||||
);
|
||||
expect(setHeaderMock).toHaveBeenCalledWith(
|
||||
'Cache-Control',
|
||||
'no-cache, no-store, must-revalidate',
|
||||
);
|
||||
expect(setHeaderMock).toHaveBeenCalledWith('Pragma', 'no-cache');
|
||||
expect(setHeaderMock).toHaveBeenCalledWith('Expires', '0');
|
||||
});
|
||||
|
||||
it('should set correct headers for JSON download', () => {
|
||||
setDownloadHeaders(mockRes as Response, 'data.json', 'application/json');
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledWith('Content-Type', 'application/json');
|
||||
expect(setHeaderMock).toHaveBeenCalledWith(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="data.json"',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle special characters in filename', () => {
|
||||
setDownloadHeaders(mockRes as Response, 'file with spaces & special.csv', 'text/csv');
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledWith(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="file with spaces & special.csv"',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle very long filenames', () => {
|
||||
const longFileName = 'a'.repeat(255) + '.csv';
|
||||
setDownloadHeaders(mockRes as Response, longFileName, 'text/csv');
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledWith(
|
||||
'Content-Disposition',
|
||||
`attachment; filename="${longFileName}"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle Unicode characters in filename', () => {
|
||||
setDownloadHeaders(mockRes as Response, '测试文件.csv', 'text/csv');
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledWith(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="测试文件.csv"',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle different content types', () => {
|
||||
const testCases = [
|
||||
{ fileName: 'file.pdf', contentType: 'application/pdf' },
|
||||
{ fileName: 'file.xml', contentType: 'application/xml' },
|
||||
{ fileName: 'file.txt', contentType: 'text/plain' },
|
||||
{ fileName: 'file.yaml', contentType: 'application/x-yaml' },
|
||||
];
|
||||
|
||||
testCases.forEach(({ fileName, contentType }) => {
|
||||
setHeaderMock.mockClear();
|
||||
setDownloadHeaders(mockRes as Response, fileName, contentType);
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledWith('Content-Type', contentType);
|
||||
expect(setHeaderMock).toHaveBeenCalledWith(
|
||||
'Content-Disposition',
|
||||
`attachment; filename="${fileName}"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set all cache control headers', () => {
|
||||
setDownloadHeaders(mockRes as Response, 'test.csv', 'text/csv');
|
||||
|
||||
// Verify all cache-preventing headers are set
|
||||
const calls = setHeaderMock.mock.calls;
|
||||
const headerMap = new Map(calls.map((call) => [call[0] as string, call[1]]));
|
||||
|
||||
expect(headerMap.get('Cache-Control')).toBe('no-cache, no-store, must-revalidate');
|
||||
expect(headerMap.get('Pragma')).toBe('no-cache');
|
||||
expect(headerMap.get('Expires')).toBe('0');
|
||||
});
|
||||
|
||||
it('should be called exactly 5 times for all headers', () => {
|
||||
setDownloadHeaders(mockRes as Response, 'test.csv', 'text/csv');
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledTimes(5);
|
||||
});
|
||||
|
||||
it('should handle empty filename', () => {
|
||||
setDownloadHeaders(mockRes as Response, '', 'text/csv');
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledWith('Content-Disposition', 'attachment; filename=""');
|
||||
});
|
||||
|
||||
it('should handle filename with quotes', () => {
|
||||
setDownloadHeaders(mockRes as Response, 'file"with"quotes.csv', 'text/csv');
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledWith(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="file"with"quotes.csv"',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle filename with path separators', () => {
|
||||
setDownloadHeaders(mockRes as Response, 'path/to/file.csv', 'text/csv');
|
||||
|
||||
expect(setHeaderMock).toHaveBeenCalledWith(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="path/to/file.csv"',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user