v2: модульный монолит — очередь, персистентность, безопасность, SSE, тесты
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:
2026-08-27 12:52:27 +07:00
parent 1c36a21e0c
commit 401969cfdb
68 changed files with 9368 additions and 4085 deletions

105
test/images.test.js Normal file
View 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);
});
});