mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: make OCR portable and reliable across AMD64 and ARM64 (#519)
* fix: make OCR portable and reliable * fix: harden OCR installation portability * fix: pin OCR partials across downloads * fix: make OCR execution reliably asynchronous * fix: harden OCR portability and docs routes * fix: preserve decoder and docs safeguards
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { Readable } from "node:stream";
|
||||
import { afterAll, describe, expect, it } from "vitest";
|
||||
import {
|
||||
copyObjectToFile,
|
||||
deleteObject,
|
||||
getObjectSize,
|
||||
getObjectStream,
|
||||
@@ -12,9 +16,15 @@ import {
|
||||
|
||||
describe("object-storage (local backend)", () => {
|
||||
const key = `outputs/test-${process.pid}/hello.txt`;
|
||||
const copyKey = `outputs/test-${process.pid}/copy-source.bin`;
|
||||
const unavailableKey = `outputs/test-${process.pid}/unavailable.bin`;
|
||||
const copyDir = mkdtempSync(join(tmpdir(), "snapotter-object-copy-"));
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteObject(key).catch(() => {});
|
||||
await deleteObject(copyKey).catch(() => {});
|
||||
await deleteObject(unavailableKey).catch(() => {});
|
||||
rmSync(copyDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("round-trips buffers and streams with size and listing", async () => {
|
||||
@@ -52,4 +62,66 @@ describe("object-storage (local backend)", () => {
|
||||
/invalid/i,
|
||||
);
|
||||
});
|
||||
|
||||
it("classifies operational streaming-write failures as temporary storage outages", async () => {
|
||||
const source = Readable.from(
|
||||
(async function* () {
|
||||
yield Buffer.from("partial");
|
||||
throw Object.assign(new Error("disk quota exhausted"), { code: "EDQUOT" });
|
||||
})(),
|
||||
);
|
||||
|
||||
await expect(putObjectStream(unavailableKey, source)).rejects.toMatchObject({
|
||||
code: "EDQUOT",
|
||||
statusCode: 503,
|
||||
});
|
||||
await expect(objectExists(unavailableKey)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it("streams an object to a file without exceeding the hard byte cap", async () => {
|
||||
const source = Buffer.alloc(4096, 0x5a);
|
||||
const destination = join(copyDir, "bounded.bin");
|
||||
await putObject(copyKey, source);
|
||||
|
||||
await expect(copyObjectToFile(copyKey, destination, { maxBytes: source.length })).resolves.toBe(
|
||||
source.length,
|
||||
);
|
||||
expect(readFileSync(destination)).toEqual(source);
|
||||
});
|
||||
|
||||
it("removes a partial destination when the streamed object exceeds its cap", async () => {
|
||||
const destination = join(copyDir, "oversized.bin");
|
||||
await putObject(copyKey, Buffer.alloc(4096, 0x41));
|
||||
|
||||
await expect(copyObjectToFile(copyKey, destination, { maxBytes: 2048 })).rejects.toMatchObject({
|
||||
statusCode: 413,
|
||||
});
|
||||
expect(existsSync(destination)).toBe(false);
|
||||
});
|
||||
|
||||
it("does not leave a destination behind when copying is canceled", async () => {
|
||||
const destination = join(copyDir, "canceled.bin");
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
|
||||
await expect(
|
||||
copyObjectToFile(copyKey, destination, {
|
||||
maxBytes: 4096,
|
||||
signal: controller.signal,
|
||||
}),
|
||||
).rejects.toMatchObject({ name: "AbortError" });
|
||||
expect(existsSync(destination)).toBe(false);
|
||||
});
|
||||
|
||||
it("atomically replaces a stale destination left by a crashed attempt", async () => {
|
||||
const destination = join(copyDir, "stale-retry.bin");
|
||||
const source = Buffer.from("fresh object bytes");
|
||||
writeFileSync(destination, "stale partial bytes");
|
||||
await putObject(copyKey, source);
|
||||
|
||||
await expect(copyObjectToFile(copyKey, destination, { maxBytes: source.length })).resolves.toBe(
|
||||
source.length,
|
||||
);
|
||||
expect(readFileSync(destination)).toEqual(source);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user