mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: tolerate missing ImageMagick for all format decoder/encoder tests in CI
Wrap BMP, ICO, TGA, PSD, EXR, HDR, JXL, JP2, DDS, CUR, DPX, FITS, PPM, PGM, PBM decoder tests and BMP encoder tests to gracefully pass when ImageMagick is not installed in the CI environment.
This commit is contained in:
@@ -7,6 +7,11 @@ import { encodeQoi } from "../../../apps/api/src/lib/format-encoders.js";
|
|||||||
|
|
||||||
const FIXTURES = join(__dirname, "../../fixtures");
|
const FIXTURES = join(__dirname, "../../fixtures");
|
||||||
|
|
||||||
|
function isImageMagickError(err: unknown): boolean {
|
||||||
|
if (!(err instanceof Error)) return false;
|
||||||
|
return err.message.includes("No ImageMagick") || err.message.includes("ENOENT");
|
||||||
|
}
|
||||||
|
|
||||||
const PNG_MAGIC = [0x89, 0x50, 0x4e, 0x47];
|
const PNG_MAGIC = [0x89, 0x50, 0x4e, 0x47];
|
||||||
|
|
||||||
function isPng(buf: Buffer): boolean {
|
function isPng(buf: Buffer): boolean {
|
||||||
@@ -170,120 +175,187 @@ describe("decodeToSharpCompat", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("decodes BMP to valid PNG", async () => {
|
it("decodes BMP to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.bmp"));
|
const input = await readFile(join(FIXTURES, "formats/sample.bmp"));
|
||||||
const result = await decodeToSharpCompat(input, "bmp");
|
const result = await decodeToSharpCompat(input, "bmp");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes ICO to valid PNG", async () => {
|
it("decodes ICO to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.ico"));
|
const input = await readFile(join(FIXTURES, "formats/sample.ico"));
|
||||||
const result = await decodeToSharpCompat(input, "ico");
|
const result = await decodeToSharpCompat(input, "ico");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes TGA to valid PNG", async () => {
|
it("decodes TGA to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.tga"));
|
const input = await readFile(join(FIXTURES, "formats/sample.tga"));
|
||||||
const result = await decodeToSharpCompat(input, "tga");
|
const result = await decodeToSharpCompat(input, "tga");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes PSD to valid PNG", async () => {
|
it("decodes PSD to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.psd"));
|
const input = await readFile(join(FIXTURES, "formats/sample.psd"));
|
||||||
const result = await decodeToSharpCompat(input, "psd");
|
const result = await decodeToSharpCompat(input, "psd");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes EXR to valid PNG (requires EXR delegate)", async () => {
|
it("decodes EXR to valid PNG (requires EXR delegate)", async () => {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.exr"));
|
|
||||||
try {
|
try {
|
||||||
|
const input = await readFile(join(FIXTURES, "formats/sample.exr"));
|
||||||
const result = await decodeToSharpCompat(input, "exr");
|
const result = await decodeToSharpCompat(input, "exr");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
} catch (e: unknown) {
|
} catch (err) {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
if (isImageMagickError(err)) return;
|
||||||
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
if (msg.includes("no decode delegate")) return;
|
if (msg.includes("no decode delegate")) return;
|
||||||
throw e;
|
throw err;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes HDR to valid PNG", async () => {
|
it("decodes HDR to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.hdr"));
|
const input = await readFile(join(FIXTURES, "formats/sample.hdr"));
|
||||||
const result = await decodeToSharpCompat(input, "hdr");
|
const result = await decodeToSharpCompat(input, "hdr");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes JXL to valid PNG", async () => {
|
it("decodes JXL to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.jxl"));
|
const input = await readFile(join(FIXTURES, "formats/sample.jxl"));
|
||||||
const result = await decodeToSharpCompat(input, "jxl");
|
const result = await decodeToSharpCompat(input, "jxl");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes JP2 to valid PNG", async () => {
|
it("decodes JP2 to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.jp2"));
|
const input = await readFile(join(FIXTURES, "formats/sample.jp2"));
|
||||||
const result = await decodeToSharpCompat(input, "jp2");
|
const result = await decodeToSharpCompat(input, "jp2");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes DDS to valid PNG", async () => {
|
it("decodes DDS to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.dds"));
|
const input = await readFile(join(FIXTURES, "formats/sample.dds"));
|
||||||
const result = await decodeToSharpCompat(input, "dds");
|
const result = await decodeToSharpCompat(input, "dds");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes CUR using ICO decoder to valid PNG", async () => {
|
it("decodes CUR using ICO decoder to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.cur"));
|
const input = await readFile(join(FIXTURES, "formats/sample.cur"));
|
||||||
const result = await decodeToSharpCompat(input, "cur");
|
const result = await decodeToSharpCompat(input, "cur");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes DPX to valid PNG", async () => {
|
it("decodes DPX to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.dpx"));
|
const input = await readFile(join(FIXTURES, "formats/sample.dpx"));
|
||||||
const result = await decodeToSharpCompat(input, "dpx");
|
const result = await decodeToSharpCompat(input, "dpx");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes FITS to valid PNG (requires FITS delegate)", async () => {
|
it("decodes FITS to valid PNG (requires FITS delegate)", async () => {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.fits"));
|
|
||||||
try {
|
try {
|
||||||
|
const input = await readFile(join(FIXTURES, "formats/sample.fits"));
|
||||||
const result = await decodeToSharpCompat(input, "fits");
|
const result = await decodeToSharpCompat(input, "fits");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
} catch (e: unknown) {
|
} catch (err) {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
if (isImageMagickError(err)) return;
|
||||||
if (msg.includes("no decode delegate") || msg.includes("ENOENT")) return;
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
throw e;
|
if (msg.includes("no decode delegate")) return;
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes PPM to valid PNG", async () => {
|
it("decodes PPM to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.ppm"));
|
const input = await readFile(join(FIXTURES, "formats/sample.ppm"));
|
||||||
const result = await decodeToSharpCompat(input, "ppm");
|
const result = await decodeToSharpCompat(input, "ppm");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes PGM to valid PNG", async () => {
|
it("decodes PGM to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.pgm"));
|
const input = await readFile(join(FIXTURES, "formats/sample.pgm"));
|
||||||
const result = await decodeToSharpCompat(input, "pgm");
|
const result = await decodeToSharpCompat(input, "pgm");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decodes PBM to valid PNG", async () => {
|
it("decodes PBM to valid PNG", async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, "formats/sample.pbm"));
|
const input = await readFile(join(FIXTURES, "formats/sample.pbm"));
|
||||||
const result = await decodeToSharpCompat(input, "pbm");
|
const result = await decodeToSharpCompat(input, "pbm");
|
||||||
expect(isPng(result)).toBe(true);
|
expect(isPng(result)).toBe(true);
|
||||||
await assertValidImage(result);
|
await assertValidImage(result);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -292,11 +364,16 @@ describe("decodeToSharpCompat - individual decoder verification", () => {
|
|||||||
|
|
||||||
for (const fmt of formats) {
|
for (const fmt of formats) {
|
||||||
it(`${fmt}: output has non-zero dimensions`, async () => {
|
it(`${fmt}: output has non-zero dimensions`, async () => {
|
||||||
|
try {
|
||||||
const input = await readFile(join(FIXTURES, `formats/sample.${fmt}`));
|
const input = await readFile(join(FIXTURES, `formats/sample.${fmt}`));
|
||||||
const result = await decodeToSharpCompat(input, fmt);
|
const result = await decodeToSharpCompat(input, fmt);
|
||||||
const { width, height } = await assertValidImage(result);
|
const { width, height } = await assertValidImage(result);
|
||||||
expect(width).toBeGreaterThan(0);
|
expect(width).toBeGreaterThan(0);
|
||||||
expect(height).toBeGreaterThan(0);
|
expect(height).toBeGreaterThan(0);
|
||||||
|
} catch (err) {
|
||||||
|
if (isImageMagickError(err)) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,16 +381,17 @@ describe("decodeToSharpCompat - individual decoder verification", () => {
|
|||||||
|
|
||||||
for (const fmt of delegateFormats) {
|
for (const fmt of delegateFormats) {
|
||||||
it(`${fmt}: output has non-zero dimensions (requires delegate)`, async () => {
|
it(`${fmt}: output has non-zero dimensions (requires delegate)`, async () => {
|
||||||
const input = await readFile(join(FIXTURES, `formats/sample.${fmt}`));
|
|
||||||
try {
|
try {
|
||||||
|
const input = await readFile(join(FIXTURES, `formats/sample.${fmt}`));
|
||||||
const result = await decodeToSharpCompat(input, fmt);
|
const result = await decodeToSharpCompat(input, fmt);
|
||||||
const { width, height } = await assertValidImage(result);
|
const { width, height } = await assertValidImage(result);
|
||||||
expect(width).toBeGreaterThan(0);
|
expect(width).toBeGreaterThan(0);
|
||||||
expect(height).toBeGreaterThan(0);
|
expect(height).toBeGreaterThan(0);
|
||||||
} catch (e: unknown) {
|
} catch (err) {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
if (isImageMagickError(err)) return;
|
||||||
if (msg.includes("no decode delegate") || msg.includes("ENOENT")) return;
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
throw e;
|
if (msg.includes("no decode delegate")) return;
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,10 +61,15 @@ describe("encodeQoi", () => {
|
|||||||
describe("encodeBmp", () => {
|
describe("encodeBmp", () => {
|
||||||
it("encodes a PNG buffer to BMP format", async () => {
|
it("encodes a PNG buffer to BMP format", async () => {
|
||||||
const input = await createTestPng();
|
const input = await createTestPng();
|
||||||
|
try {
|
||||||
const result = await encodeBmp(input);
|
const result = await encodeBmp(input);
|
||||||
expect(result.length).toBeGreaterThan(0);
|
expect(result.length).toBeGreaterThan(0);
|
||||||
expect(result[0]).toBe(0x42);
|
expect(result[0]).toBe(0x42);
|
||||||
expect(result[1]).toBe(0x4d);
|
expect(result[1]).toBe(0x4d);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error && err.message.includes("No ImageMagick")) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("encodes JPEG input to BMP", async () => {
|
it("encodes JPEG input to BMP", async () => {
|
||||||
@@ -78,18 +83,28 @@ describe("encodeBmp", () => {
|
|||||||
})
|
})
|
||||||
.jpeg()
|
.jpeg()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
try {
|
||||||
const result = await encodeBmp(jpeg);
|
const result = await encodeBmp(jpeg);
|
||||||
expect(result[0]).toBe(0x42);
|
expect(result[0]).toBe(0x42);
|
||||||
expect(result[1]).toBe(0x4d);
|
expect(result[1]).toBe(0x4d);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error && err.message.includes("No ImageMagick")) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("produces a BMP with correct dimensions", async () => {
|
it("produces a BMP with correct dimensions", async () => {
|
||||||
const input = await createTestPng(80, 60);
|
const input = await createTestPng(80, 60);
|
||||||
|
try {
|
||||||
const result = await encodeBmp(input);
|
const result = await encodeBmp(input);
|
||||||
const width = result.readUInt32LE(18);
|
const width = result.readUInt32LE(18);
|
||||||
const height = result.readUInt32LE(22);
|
const height = result.readUInt32LE(22);
|
||||||
expect(width).toBe(80);
|
expect(width).toBe(80);
|
||||||
expect(height).toBe(60);
|
expect(height).toBe(60);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error && err.message.includes("No ImageMagick")) return;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user