Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export async function send(
encodingExt = '.gz';
}

if (extensions && !path.basename(filePath).includes('.')) {
if (extensions && !(await isPathExists(filePath))) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
for (let ext of extensions) {
if (typeof ext !== 'string')
throw new TypeError(
Expand Down
5 changes: 3 additions & 2 deletions src/send.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ export async function isPathExists(targetPath: string) {
try {
await asyncFs.access(targetPath);
return true;
} catch {
return false;
} catch (err) {
if ((err as {code?: string}).code === 'ENOENT') return false;
throw err;
}
}

Expand Down
1 change: 1 addition & 0 deletions test/fixtures/hello.world.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
world
37 changes: 37 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
import asyncFs from 'node:fs/promises';
import path from 'node:path';

import Koa from 'koa';
import request from 'supertest';
import { decompress } from 'brotli';

import { send } from '../src';
import { isPathExists } from '../src/send.utils';

describe('isPathExists(targetPath)', () => {
afterEach(() => {
jest.restoreAllMocks();
});

it('returns false when access reports ENOENT', async () => {
const error = Object.assign(new Error('missing'), { code: 'ENOENT' });
jest.spyOn(asyncFs, 'access').mockRejectedValueOnce(error);

await expect(isPathExists('missing')).resolves.toBe(false);
});

it('rethrows non-ENOENT access errors', async () => {
const error = Object.assign(new Error('denied'), { code: 'EACCES' });
jest.spyOn(asyncFs, 'access').mockRejectedValueOnce(error);

await expect(isPathExists('denied')).rejects.toBe(error);
});
});

describe('send(ctx, file)', () => {
let server: ReturnType<InstanceType<typeof Koa>['listen']>;
Expand Down Expand Up @@ -690,6 +712,21 @@ describe('send(ctx, file)', () => {
await request(server).get('/').expect(200);
});
});

describe('when trying to get a file without extension with matching .extensions suffixed with a dot in the basename', () => {
it('should 200', async () => {
const app = new Koa();

app.use(async (ctx) => {
await send(ctx, 'test/fixtures/hello.world', {
extensions: ['txt'],
});
});

server = app.listen();
await request(server).get('/').expect(200).expect('world\n');
});
});
});

it('should set the Content-Type', async () => {
Expand Down
Loading