v2: модульный монолит — очередь, персистентность, безопасность, SSE, тесты
Some checks failed
test / test (push) Failing after 24s
Some checks failed
test / test (push) Failing after 24s
Реализация целевого дизайна (docs/architecture/target-design.md): - src/: модульная структура (config, logger, errors, http, core: auth/history/jobs/engines/comfy/codex/images) - Очередь заданий: FIFO, кониурентность codex=1 / comfy-upscale=2, MAX_QUEUED_JOBS=5, JSONL-журнал и восстановление после рестарта (running → interrupted, upscale requeue) - Персистентные сессии (sha256-хеши токенов), scrypt-хеш пароля, rate-limit входа, Origin-проверка, magic-byte валидация аплоадов - Атомарная запись манифеста истории, каскадное удаление, URL с фактическим расширением + 302-алиас /image.jpg, миграция history/ → data/history - Даунскейл sharp до 1024px перед Codex + масштабирование области референса - SSE /api/events с фолбэком на polling, фронтенд переведён на ES-модули (public/js/) - Тесты node:test + supertest (27), CI workflow test.yml, Docker/деплой: том kadr-data - Документация: README, docs/architecture/
This commit is contained in:
105
test/images.test.js
Normal file
105
test/images.test.js
Normal file
@@ -0,0 +1,105 @@
|
||||
"use strict";
|
||||
|
||||
const { test, describe } = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const sharp = require("sharp");
|
||||
const { getImageDimensions, scaleForLimit } = require("../src/core/images/dimensions");
|
||||
const { sniffImageType } = require("../src/core/images/sniff");
|
||||
const { scaleRegion } = require("../src/core/codex/prompts");
|
||||
const { downscaleImage } = require("../src/core/images/resize");
|
||||
|
||||
describe("images", () => {
|
||||
test("sniffImageType recognizes JPEG, PNG, WebP and rejects invalid data", async () => {
|
||||
// Generate valid test images via sharp
|
||||
const pngBuffer = await sharp({
|
||||
create: { width: 100, height: 80, channels: 3, background: { r: 255, g: 0, b: 0 } },
|
||||
}).png().toBuffer();
|
||||
|
||||
const jpegBuffer = await sharp({
|
||||
create: { width: 100, height: 80, channels: 3, background: { r: 0, g: 255, b: 0 } },
|
||||
}).jpeg().toBuffer();
|
||||
|
||||
const webpBuffer = await sharp({
|
||||
create: { width: 100, height: 80, channels: 3, background: { r: 0, g: 0, b: 255 } },
|
||||
}).webp().toBuffer();
|
||||
|
||||
assert.equal(sniffImageType(pngBuffer), "png");
|
||||
assert.equal(sniffImageType(jpegBuffer), "jpeg");
|
||||
assert.equal(sniffImageType(webpBuffer), "webp");
|
||||
|
||||
// HEIC ftyp box mock
|
||||
const heicMock = Buffer.alloc(32);
|
||||
heicMock.writeUInt32BE(32, 0);
|
||||
heicMock.write("ftyp", 4, "ascii");
|
||||
heicMock.write("heic", 8, "ascii");
|
||||
assert.equal(sniffImageType(heicMock), "heic");
|
||||
|
||||
// Non-image garbage
|
||||
assert.equal(sniffImageType(Buffer.from("not an image at all")), null);
|
||||
assert.equal(sniffImageType(Buffer.alloc(4)), null);
|
||||
});
|
||||
|
||||
test("getImageDimensions parses PNG, JPEG and WebP dimensions", async () => {
|
||||
const pngBuffer = await sharp({
|
||||
create: { width: 320, height: 240, channels: 3, background: "white" },
|
||||
}).png().toBuffer();
|
||||
|
||||
const jpegBuffer = await sharp({
|
||||
create: { width: 640, height: 480, channels: 3, background: "white" },
|
||||
}).jpeg().toBuffer();
|
||||
|
||||
const webpBuffer = await sharp({
|
||||
create: { width: 128, height: 96, channels: 3, background: "white" },
|
||||
}).webp().toBuffer();
|
||||
|
||||
assert.deepEqual(getImageDimensions(pngBuffer), { width: 320, height: 240 });
|
||||
assert.deepEqual(getImageDimensions(jpegBuffer), { width: 640, height: 480 });
|
||||
assert.deepEqual(getImageDimensions(webpBuffer), { width: 128, height: 96 });
|
||||
});
|
||||
|
||||
test("scaleRegion scales coordinates and preserves bounds", () => {
|
||||
const region = { x: 100, y: 50, w: 200, h: 150 };
|
||||
|
||||
// factor = 1 -> identical
|
||||
assert.deepEqual(scaleRegion(region, 1), region);
|
||||
|
||||
// factor = 0.5
|
||||
assert.deepEqual(scaleRegion(region, 0.5), { x: 50, y: 25, w: 100, h: 75 });
|
||||
|
||||
// factor = 0.333
|
||||
const scaled = scaleRegion(region, 0.333333);
|
||||
assert.equal(scaled.x, 33);
|
||||
assert.equal(scaled.y, 17);
|
||||
assert.equal(scaled.w, 67);
|
||||
assert.equal(scaled.h, 50);
|
||||
|
||||
// null region
|
||||
assert.equal(scaleRegion(null, 0.5), null);
|
||||
});
|
||||
|
||||
test("downscaleImage resizes images exceeding EDIT_MAX_DIMENSION", async () => {
|
||||
const largeBuffer = await sharp({
|
||||
create: { width: 2000, height: 1500, channels: 3, background: "red" },
|
||||
}).jpeg().toBuffer();
|
||||
|
||||
const result = await downscaleImage(largeBuffer, 1024);
|
||||
assert.equal(result.downscaled, true);
|
||||
assert.equal(result.originalWidth, 2000);
|
||||
assert.equal(result.originalHeight, 1500);
|
||||
assert.equal(result.width, 1024);
|
||||
assert.equal(result.height, 768);
|
||||
assert.ok(result.factor < 1);
|
||||
assert.ok(result.buffer.length < largeBuffer.length);
|
||||
|
||||
// Small image is NOT downscaled
|
||||
const smallBuffer = await sharp({
|
||||
create: { width: 800, height: 600, channels: 3, background: "blue" },
|
||||
}).jpeg().toBuffer();
|
||||
|
||||
const smallResult = await downscaleImage(smallBuffer, 1024);
|
||||
assert.equal(smallResult.downscaled, false);
|
||||
assert.equal(smallResult.width, 800);
|
||||
assert.equal(smallResult.height, 600);
|
||||
assert.equal(smallResult.factor, 1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user