mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 22:09:16 +01:00
Add new media handling package
This commit is contained in:
parent
a6c441f665
commit
177f573792
13 changed files with 631 additions and 0 deletions
243
packages/media-manager/tests/media-backends.test.ts
Normal file
243
packages/media-manager/tests/media-backends.test.ts
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
// FILEPATH: /home/jessew/Dev/lysand/packages/media-manager/backends/s3.test.ts
|
||||
import { MediaBackend, MediaBackendType, MediaHasher } from "..";
|
||||
import type { S3Client } from "@bradenmacdonald/s3-lite-client";
|
||||
import { beforeEach, describe, jest, it, expect, spyOn } from "bun:test";
|
||||
import { S3MediaBackend } from "../backends/s3";
|
||||
import type { ConfigType } from "config-manager";
|
||||
import { ConvertableMediaFormats, MediaConverter } from "../media-converter";
|
||||
import { LocalMediaBackend } from "../backends/local";
|
||||
|
||||
type DeepPartial<T> = {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
};
|
||||
|
||||
describe("MediaBackend", () => {
|
||||
let mediaBackend: MediaBackend;
|
||||
let mockConfig: ConfigType;
|
||||
|
||||
beforeEach(() => {
|
||||
mediaBackend = new MediaBackend(MediaBackendType.S3);
|
||||
mockConfig = {
|
||||
media: {
|
||||
conversion: {
|
||||
convert_images: true,
|
||||
},
|
||||
},
|
||||
} as ConfigType;
|
||||
});
|
||||
|
||||
it("should initialize with correct backend type", () => {
|
||||
expect(mediaBackend.getBackendType()).toEqual(MediaBackendType.S3);
|
||||
});
|
||||
|
||||
it("should check if images should be converted", () => {
|
||||
expect(mediaBackend.shouldConvertImages(mockConfig)).toBe(true);
|
||||
mockConfig.media.conversion.convert_images = false;
|
||||
expect(mediaBackend.shouldConvertImages(mockConfig)).toBe(false);
|
||||
});
|
||||
|
||||
it("should throw error when calling getFileByHash", () => {
|
||||
const mockHash = "test-hash";
|
||||
const databaseHashFetcher = jest.fn().mockResolvedValue("test.jpg");
|
||||
|
||||
expect(
|
||||
mediaBackend.getFileByHash(mockHash, databaseHashFetcher)
|
||||
).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
it("should throw error when calling getFile", () => {
|
||||
const mockFilename = "test.jpg";
|
||||
|
||||
expect(mediaBackend.getFile(mockFilename)).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
it("should throw error when calling addFile", () => {
|
||||
const mockFile = new File([""], "test.jpg");
|
||||
|
||||
expect(mediaBackend.addFile(mockFile)).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("S3MediaBackend", () => {
|
||||
let s3MediaBackend: S3MediaBackend;
|
||||
let mockS3Client: Partial<S3Client>;
|
||||
let mockConfig: DeepPartial<ConfigType>;
|
||||
let mockFile: File;
|
||||
let mockMediaHasher: MediaHasher;
|
||||
|
||||
beforeEach(() => {
|
||||
mockConfig = {
|
||||
s3: {
|
||||
endpoint: "http://localhost:4566",
|
||||
region: "us-east-1",
|
||||
bucket_name: "test-bucket",
|
||||
access_key: "test-access-key",
|
||||
secret_access_key: "test-secret-access-key",
|
||||
public_url: "test",
|
||||
},
|
||||
media: {
|
||||
conversion: {
|
||||
convert_to: ConvertableMediaFormats.PNG,
|
||||
},
|
||||
},
|
||||
};
|
||||
mockFile = new File([new TextEncoder().encode("test")], "test.jpg");
|
||||
mockMediaHasher = new MediaHasher();
|
||||
mockS3Client = {
|
||||
putObject: jest.fn().mockResolvedValue({}),
|
||||
statObject: jest.fn().mockResolvedValue({}),
|
||||
getObject: jest.fn().mockResolvedValue({
|
||||
blob: jest.fn().mockResolvedValue(new Blob()),
|
||||
headers: new Headers({ "Content-Type": "image/jpeg" }),
|
||||
}),
|
||||
} as Partial<S3Client>;
|
||||
s3MediaBackend = new S3MediaBackend(
|
||||
mockConfig as ConfigType,
|
||||
mockS3Client as S3Client
|
||||
);
|
||||
});
|
||||
|
||||
it("should initialize with correct type", () => {
|
||||
expect(s3MediaBackend.getBackendType()).toEqual(MediaBackendType.S3);
|
||||
});
|
||||
|
||||
it("should add file", async () => {
|
||||
const mockHash = "test-hash";
|
||||
spyOn(mockMediaHasher, "getMediaHash").mockResolvedValue(mockHash);
|
||||
|
||||
const result = await s3MediaBackend.addFile(mockFile);
|
||||
|
||||
expect(result.uploadedFile).toEqual(mockFile);
|
||||
expect(result.hash).toHaveLength(64);
|
||||
expect(mockS3Client.putObject).toHaveBeenCalledWith(
|
||||
mockFile.name,
|
||||
expect.any(ReadableStream),
|
||||
{ size: mockFile.size }
|
||||
);
|
||||
});
|
||||
|
||||
it("should get file by hash", async () => {
|
||||
const mockHash = "test-hash";
|
||||
const mockFilename = "test.jpg";
|
||||
const databaseHashFetcher = jest.fn().mockResolvedValue(mockFilename);
|
||||
mockS3Client.statObject = jest.fn().mockResolvedValue({});
|
||||
mockS3Client.getObject = jest.fn().mockResolvedValue({
|
||||
arrayBuffer: jest.fn().mockResolvedValue(new ArrayBuffer(10)),
|
||||
headers: new Headers({ "Content-Type": "image/jpeg" }),
|
||||
});
|
||||
|
||||
const file = await s3MediaBackend.getFileByHash(
|
||||
mockHash,
|
||||
databaseHashFetcher
|
||||
);
|
||||
|
||||
expect(file).not.toBeNull();
|
||||
expect(file?.name).toEqual(mockFilename);
|
||||
expect(file?.type).toEqual("image/jpeg");
|
||||
});
|
||||
|
||||
it("should get file", async () => {
|
||||
const mockFilename = "test.jpg";
|
||||
mockS3Client.statObject = jest.fn().mockResolvedValue({});
|
||||
mockS3Client.getObject = jest.fn().mockResolvedValue({
|
||||
arrayBuffer: jest.fn().mockResolvedValue(new ArrayBuffer(10)),
|
||||
headers: new Headers({ "Content-Type": "image/jpeg" }),
|
||||
});
|
||||
|
||||
const file = await s3MediaBackend.getFile(mockFilename);
|
||||
|
||||
expect(file).not.toBeNull();
|
||||
expect(file?.name).toEqual(mockFilename);
|
||||
expect(file?.type).toEqual("image/jpeg");
|
||||
});
|
||||
});
|
||||
|
||||
describe("LocalMediaBackend", () => {
|
||||
let localMediaBackend: LocalMediaBackend;
|
||||
let mockConfig: ConfigType;
|
||||
let mockFile: File;
|
||||
let mockMediaHasher: MediaHasher;
|
||||
|
||||
beforeEach(() => {
|
||||
mockConfig = {
|
||||
media: {
|
||||
conversion: {
|
||||
convert_images: true,
|
||||
convert_to: ConvertableMediaFormats.PNG,
|
||||
},
|
||||
local_uploads_folder: "./uploads",
|
||||
},
|
||||
} as ConfigType;
|
||||
mockFile = Bun.file(__dirname + "/megamind.jpg") as unknown as File;
|
||||
mockMediaHasher = new MediaHasher();
|
||||
localMediaBackend = new LocalMediaBackend(mockConfig);
|
||||
});
|
||||
|
||||
it("should initialize with correct type", () => {
|
||||
expect(localMediaBackend.getBackendType()).toEqual(
|
||||
MediaBackendType.LOCAL
|
||||
);
|
||||
});
|
||||
|
||||
it("should add file", async () => {
|
||||
const mockHash = "test-hash";
|
||||
spyOn(mockMediaHasher, "getMediaHash").mockResolvedValue(mockHash);
|
||||
const mockMediaConverter = new MediaConverter(
|
||||
ConvertableMediaFormats.JPG,
|
||||
ConvertableMediaFormats.PNG
|
||||
);
|
||||
spyOn(mockMediaConverter, "convert").mockResolvedValue(mockFile);
|
||||
// @ts-expect-error This is a mock
|
||||
spyOn(Bun, "file").mockImplementationOnce(() => ({
|
||||
exists: () => Promise.resolve(false),
|
||||
}));
|
||||
spyOn(Bun, "write").mockImplementationOnce(() =>
|
||||
Promise.resolve(mockFile.size)
|
||||
);
|
||||
|
||||
const result = await localMediaBackend.addFile(mockFile);
|
||||
|
||||
expect(result.uploadedFile).toEqual(mockFile);
|
||||
expect(result.path).toEqual(`./uploads/megamind.png`);
|
||||
expect(result.hash).toHaveLength(64);
|
||||
});
|
||||
|
||||
it("should get file by hash", async () => {
|
||||
const mockHash = "test-hash";
|
||||
const mockFilename = "test.jpg";
|
||||
const databaseHashFetcher = jest.fn().mockResolvedValue(mockFilename);
|
||||
// @ts-expect-error This is a mock
|
||||
spyOn(Bun, "file").mockImplementationOnce(() => ({
|
||||
exists: () => Promise.resolve(true),
|
||||
arrayBuffer: () => Promise.resolve(new ArrayBuffer(8)),
|
||||
type: "image/jpeg",
|
||||
lastModified: 123456789,
|
||||
}));
|
||||
|
||||
const file = await localMediaBackend.getFileByHash(
|
||||
mockHash,
|
||||
databaseHashFetcher
|
||||
);
|
||||
|
||||
expect(file).not.toBeNull();
|
||||
expect(file?.name).toEqual(mockFilename);
|
||||
expect(file?.type).toEqual("image/jpeg");
|
||||
});
|
||||
|
||||
it("should get file", async () => {
|
||||
const mockFilename = "test.jpg";
|
||||
// @ts-expect-error This is a mock
|
||||
spyOn(Bun, "file").mockImplementationOnce(() => ({
|
||||
exists: () => Promise.resolve(true),
|
||||
arrayBuffer: () => Promise.resolve(new ArrayBuffer(8)),
|
||||
type: "image/jpeg",
|
||||
lastModified: 123456789,
|
||||
}));
|
||||
|
||||
const file = await localMediaBackend.getFile(mockFilename);
|
||||
|
||||
expect(file).not.toBeNull();
|
||||
expect(file?.name).toEqual(mockFilename);
|
||||
expect(file?.type).toEqual("image/jpeg");
|
||||
});
|
||||
});
|
||||
65
packages/media-manager/tests/media-manager.test.ts
Normal file
65
packages/media-manager/tests/media-manager.test.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// FILEPATH: /home/jessew/Dev/lysand/packages/media-manager/media-converter.test.ts
|
||||
import { describe, it, expect, beforeEach } from "bun:test";
|
||||
import { MediaConverter, ConvertableMediaFormats } from "../media-converter";
|
||||
|
||||
describe("MediaConverter", () => {
|
||||
let mediaConverter: MediaConverter;
|
||||
|
||||
beforeEach(() => {
|
||||
mediaConverter = new MediaConverter(
|
||||
ConvertableMediaFormats.JPG,
|
||||
ConvertableMediaFormats.PNG
|
||||
);
|
||||
});
|
||||
|
||||
it("should initialize with correct formats", () => {
|
||||
expect(mediaConverter.fromFormat).toEqual(ConvertableMediaFormats.JPG);
|
||||
expect(mediaConverter.toFormat).toEqual(ConvertableMediaFormats.PNG);
|
||||
});
|
||||
|
||||
it("should check if media is convertable", () => {
|
||||
expect(mediaConverter.isConvertable()).toBe(true);
|
||||
mediaConverter.toFormat = ConvertableMediaFormats.JPG;
|
||||
expect(mediaConverter.isConvertable()).toBe(false);
|
||||
});
|
||||
|
||||
it("should replace file name extension", () => {
|
||||
const fileName = "test.jpg";
|
||||
const expectedFileName = "test.png";
|
||||
// Written like this because it's a private function
|
||||
expect(mediaConverter["getReplacedFileName"](fileName)).toEqual(
|
||||
expectedFileName
|
||||
);
|
||||
});
|
||||
|
||||
describe("Filename extractor", () => {
|
||||
it("should extract filename from path", () => {
|
||||
const path = "path/to/test.jpg";
|
||||
const expectedFileName = "test.jpg";
|
||||
expect(mediaConverter["extractFilenameFromPath"](path)).toEqual(
|
||||
expectedFileName
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle escaped slashes", () => {
|
||||
const path = "path/to/test\\/test.jpg";
|
||||
const expectedFileName = "test\\/test.jpg";
|
||||
expect(mediaConverter["extractFilenameFromPath"](path)).toEqual(
|
||||
expectedFileName
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should convert media", async () => {
|
||||
const file = Bun.file(__dirname + "/megamind.jpg");
|
||||
|
||||
const convertedFile = await mediaConverter.convert(
|
||||
file as unknown as File
|
||||
);
|
||||
|
||||
expect(convertedFile.name).toEqual("megamind.png");
|
||||
expect(convertedFile.type).toEqual(
|
||||
`image/${ConvertableMediaFormats.PNG}`
|
||||
);
|
||||
});
|
||||
});
|
||||
BIN
packages/media-manager/tests/megamind.jpg
Normal file
BIN
packages/media-manager/tests/megamind.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Loading…
Add table
Add a link
Reference in a new issue