AI Implementation feature(898): Admin Area General Settings and Categories 1.16 (#38)
This commit was merged in pull request #38.
This commit is contained in:
@@ -6,6 +6,7 @@ process.env.UPLOAD_SIZE_LIMIT = '1mb';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import sharp from 'sharp';
|
||||
|
||||
import { safeFilename, parseUploadSizeLimit } from '../../backend/src/common/utils/upload';
|
||||
|
||||
@@ -124,22 +125,75 @@ describe('Uploads endpoint integration', () => {
|
||||
expect([401, 403]).toContain(res.status);
|
||||
});
|
||||
|
||||
it('uploads a small file successfully', async () => {
|
||||
it('uploads a valid image and writes a deterministic 128x128 PNG', async () => {
|
||||
const { agent, csrf } = await primeCsrf();
|
||||
const png = await sharp({
|
||||
create: { width: 64, height: 64, channels: 3, background: { r: 1, g: 2, b: 3 } },
|
||||
}).png().toBuffer();
|
||||
const res = await agent
|
||||
.post('/api/v1/uploads/category-icon')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.set('X-CSRF-Token', csrf)
|
||||
.field('categoryId', 'icon-test-cat')
|
||||
.attach('file', png, 'icon.png');
|
||||
expect(res.status).toBe(201);
|
||||
expect(res.body.id).toBe('icon-test-cat.png');
|
||||
expect(res.body.publicUrl).toBe('/uploads/icons/icon-test-cat.png');
|
||||
expect(res.body.width).toBe(128);
|
||||
expect(res.body.height).toBe(128);
|
||||
expect(res.body.mimeType).toBe('image/png');
|
||||
const written = path.join(process.env.UPLOAD_DIR!, 'icons', 'icon-test-cat.png');
|
||||
expect(fs.existsSync(written)).toBe(true);
|
||||
const decoded = await sharp(written).metadata();
|
||||
expect(decoded.format).toBe('png');
|
||||
expect(decoded.width).toBe(128);
|
||||
expect(decoded.height).toBe(128);
|
||||
await request(app.getHttpServer()).get(res.body.publicUrl).expect(200);
|
||||
});
|
||||
|
||||
it('rejects a plain-text payload labeled as a category icon and does not overwrite a valid icon', async () => {
|
||||
const iconsDir = path.join(process.env.UPLOAD_DIR!, 'icons');
|
||||
fs.mkdirSync(iconsDir, { recursive: true });
|
||||
const target = path.join(iconsDir, 'icon-tx-cat.png');
|
||||
const validPng = await sharp({
|
||||
create: { width: 32, height: 32, channels: 3, background: { r: 9, g: 9, b: 9 } },
|
||||
}).png().toBuffer();
|
||||
fs.writeFileSync(target, validPng);
|
||||
const beforeBytes = fs.readFileSync(target);
|
||||
|
||||
const { agent, csrf } = await primeCsrf();
|
||||
const res = await agent
|
||||
.post('/api/v1/uploads/category-icon')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.set('X-CSRF-Token', csrf)
|
||||
.attach('file', Buffer.from('hello world'), 'icon.png');
|
||||
expect(res.status).toBe(201);
|
||||
expect(res.body.id).toMatch(/^icon-[0-9a-f]{8}\.png$/);
|
||||
expect(res.body.publicUrl).toBe(`/uploads/icons/${res.body.id}`);
|
||||
// File actually written under UPLOAD_DIR/icons
|
||||
const written = path.join(process.env.UPLOAD_DIR!, 'icons', res.body.id);
|
||||
expect(fs.existsSync(written)).toBe(true);
|
||||
// Public-served via /uploads static handler.
|
||||
const fetched = await request(app.getHttpServer()).get(res.body.publicUrl).expect(200);
|
||||
expect(fetched.body?.toString()).toBe('hello world');
|
||||
.field('categoryId', 'icon-tx-cat')
|
||||
.attach('file', Buffer.from('not an image'), { filename: 'icon.png', contentType: 'image/png' });
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body?.message).toMatch(/valid PNG|JPEG|GIF|WebP/i);
|
||||
expect(fs.existsSync(target)).toBe(true);
|
||||
expect(fs.readFileSync(target).equals(beforeBytes)).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects a corrupt PNG payload and does not overwrite a valid icon', async () => {
|
||||
const iconsDir = path.join(process.env.UPLOAD_DIR!, 'icons');
|
||||
const target = path.join(iconsDir, 'icon-corrupt-cat.png');
|
||||
const validPng = await sharp({
|
||||
create: { width: 32, height: 32, channels: 3, background: { r: 5, g: 5, b: 5 } },
|
||||
}).png().toBuffer();
|
||||
fs.writeFileSync(target, validPng);
|
||||
const beforeBytes = fs.readFileSync(target);
|
||||
|
||||
const { agent, csrf } = await primeCsrf();
|
||||
const corrupt = Buffer.concat([Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), Buffer.alloc(53, 0x00)]);
|
||||
const res = await agent
|
||||
.post('/api/v1/uploads/category-icon')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.set('X-CSRF-Token', csrf)
|
||||
.field('categoryId', 'icon-corrupt-cat')
|
||||
.attach('file', corrupt, { filename: 'corrupt.png', contentType: 'image/png' });
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body?.message).toMatch(/valid PNG|JPEG|GIF|WebP/i);
|
||||
expect(fs.readFileSync(target).equals(beforeBytes)).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects oversize uploads (limit=1mb, file=2mb)', async () => {
|
||||
|
||||
Reference in New Issue
Block a user