mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-17 12:56:05 +00:00
public commit
This commit is contained in:
56
app/__test__/media/MediaController.spec.ts
Normal file
56
app/__test__/media/MediaController.spec.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { describe, test } from "bun:test";
|
||||
import { Hono } from "hono";
|
||||
import { Guard } from "../../src/auth";
|
||||
import { EventManager } from "../../src/core/events";
|
||||
import { EntityManager } from "../../src/data";
|
||||
import { AppMedia } from "../../src/media/AppMedia";
|
||||
import { MediaController } from "../../src/media/api/MediaController";
|
||||
import { getDummyConnection } from "../helper";
|
||||
|
||||
const { dummyConnection, afterAllCleanup } = getDummyConnection();
|
||||
|
||||
/**
|
||||
* R2
|
||||
* value: ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob | null,
|
||||
* Node writefile
|
||||
* data: string | NodeJS.ArrayBufferView | Iterable<string | NodeJS.ArrayBufferView> | AsyncIterable<string | NodeJS.ArrayBufferView> | Stream,
|
||||
*/
|
||||
|
||||
describe("MediaController", () => {
|
||||
test("..", async () => {
|
||||
const ctx: any = {
|
||||
em: new EntityManager([], dummyConnection, []),
|
||||
guard: new Guard(),
|
||||
emgr: new EventManager(),
|
||||
server: new Hono()
|
||||
};
|
||||
|
||||
const media = new AppMedia(
|
||||
// @ts-ignore
|
||||
{
|
||||
enabled: true,
|
||||
adapter: {
|
||||
type: "s3",
|
||||
config: {
|
||||
access_key: process.env.R2_ACCESS_KEY as string,
|
||||
secret_access_key: process.env.R2_SECRET_ACCESS_KEY as string,
|
||||
url: process.env.R2_URL as string
|
||||
}
|
||||
}
|
||||
},
|
||||
ctx
|
||||
);
|
||||
await media.build();
|
||||
const app = new MediaController(media).getController();
|
||||
|
||||
const file = Bun.file(`${import.meta.dir}/adapters/icon.png`);
|
||||
console.log("file", file);
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
|
||||
await app.request("/upload/test.png", {
|
||||
method: "POST",
|
||||
body: file
|
||||
});
|
||||
});
|
||||
});
|
||||
81
app/__test__/media/Storage.spec.ts
Normal file
81
app/__test__/media/Storage.spec.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { type FileBody, Storage, type StorageAdapter } from "../../src/media/storage/Storage";
|
||||
import * as StorageEvents from "../../src/media/storage/events";
|
||||
|
||||
class TestAdapter implements StorageAdapter {
|
||||
files: Record<string, FileBody> = {};
|
||||
|
||||
getName() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async listObjects(prefix?: string) {
|
||||
return [];
|
||||
}
|
||||
|
||||
async putObject(key: string, body: FileBody) {
|
||||
this.files[key] = body;
|
||||
return "etag-string";
|
||||
}
|
||||
|
||||
async deleteObject(key: string) {
|
||||
delete this.files[key];
|
||||
}
|
||||
|
||||
async objectExists(key: string) {
|
||||
return key in this.files;
|
||||
}
|
||||
|
||||
async getObject(key: string) {
|
||||
return new Response(this.files[key]);
|
||||
}
|
||||
|
||||
getObjectUrl(key: string) {
|
||||
return key;
|
||||
}
|
||||
|
||||
async getObjectMeta(key: string) {
|
||||
return { type: "text/plain", size: 0 };
|
||||
}
|
||||
|
||||
toJSON(secrets?: boolean): any {
|
||||
return { name: this.getName() };
|
||||
}
|
||||
}
|
||||
|
||||
describe("Storage", async () => {
|
||||
const adapter = new TestAdapter();
|
||||
const storage = new Storage(adapter);
|
||||
const events = new Map<string, any>();
|
||||
|
||||
storage.emgr.onAny((event) => {
|
||||
// @ts-ignore
|
||||
events.set(event.constructor.slug, event);
|
||||
//console.log("event", event.constructor.slug, event);
|
||||
});
|
||||
|
||||
test("uploads a file", async () => {
|
||||
const {
|
||||
meta: { type, size }
|
||||
} = await storage.uploadFile("hello", "world.txt");
|
||||
expect({ type, size }).toEqual({ type: "text/plain", size: 0 });
|
||||
});
|
||||
|
||||
test("deletes the file", async () => {
|
||||
expect(await storage.deleteFile("hello")).toBeUndefined();
|
||||
expect(await storage.fileExists("hello")).toBeFalse();
|
||||
});
|
||||
|
||||
test("events were fired", async () => {
|
||||
expect(events.has(StorageEvents.FileUploadedEvent.slug)).toBeTrue();
|
||||
expect(events.has(StorageEvents.FileDeletedEvent.slug)).toBeTrue();
|
||||
// @todo: file access must be tested in controllers
|
||||
//expect(events.has(StorageEvents.FileAccessEvent.slug)).toBeTrue();
|
||||
});
|
||||
|
||||
// @todo: test controllers
|
||||
});
|
||||
34
app/__test__/media/StorageR2Adapter.native-spec.ts
Normal file
34
app/__test__/media/StorageR2Adapter.native-spec.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as assert from "node:assert/strict";
|
||||
import { createWriteStream } from "node:fs";
|
||||
import { test } from "node:test";
|
||||
import { Miniflare } from "miniflare";
|
||||
|
||||
// https://github.com/nodejs/node/issues/44372#issuecomment-1736530480
|
||||
console.log = async (message: any) => {
|
||||
const tty = createWriteStream("/dev/tty");
|
||||
const msg = typeof message === "string" ? message : JSON.stringify(message, null, 2);
|
||||
return tty.write(`${msg}\n`);
|
||||
};
|
||||
|
||||
test("what", async () => {
|
||||
const mf = new Miniflare({
|
||||
modules: true,
|
||||
script: "export default { async fetch() { return new Response(null); } }",
|
||||
r2Buckets: ["BUCKET"]
|
||||
});
|
||||
|
||||
const bucket = await mf.getR2Bucket("BUCKET");
|
||||
console.log(await bucket.put("count", "1"));
|
||||
|
||||
const object = await bucket.get("count");
|
||||
if (object) {
|
||||
/*const headers = new Headers();
|
||||
object.writeHttpMetadata(headers);
|
||||
headers.set("etag", object.httpEtag);*/
|
||||
console.log("yo -->", await object.text());
|
||||
|
||||
assert.strictEqual(await object.text(), "1");
|
||||
}
|
||||
|
||||
await mf.dispose();
|
||||
});
|
||||
61
app/__test__/media/adapters/StorageCloudinaryAdapter.spec.ts
Normal file
61
app/__test__/media/adapters/StorageCloudinaryAdapter.spec.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { randomString } from "../../../src/core/utils";
|
||||
import { StorageCloudinaryAdapter } from "../../../src/media";
|
||||
|
||||
import { config } from "dotenv";
|
||||
const dotenvOutput = config({ path: `${import.meta.dir}/../../.env` });
|
||||
const {
|
||||
CLOUDINARY_CLOUD_NAME,
|
||||
CLOUDINARY_API_KEY,
|
||||
CLOUDINARY_API_SECRET,
|
||||
CLOUDINARY_UPLOAD_PRESET
|
||||
} = dotenvOutput.parsed!;
|
||||
|
||||
const ALL_TESTS = !!process.env.ALL_TESTS;
|
||||
|
||||
describe.skipIf(ALL_TESTS)("StorageCloudinaryAdapter", () => {
|
||||
const adapter = new StorageCloudinaryAdapter({
|
||||
cloud_name: CLOUDINARY_CLOUD_NAME as string,
|
||||
api_key: CLOUDINARY_API_KEY as string,
|
||||
api_secret: CLOUDINARY_API_SECRET as string,
|
||||
upload_preset: CLOUDINARY_UPLOAD_PRESET as string
|
||||
});
|
||||
|
||||
const file = Bun.file(`${import.meta.dir}/icon.png`);
|
||||
const _filename = randomString(10);
|
||||
const filename = `${_filename}.png`;
|
||||
|
||||
test("object exists", async () => {
|
||||
expect(await adapter.objectExists("7fCTBi6L8c.png")).toBeTrue();
|
||||
process.exit();
|
||||
});
|
||||
|
||||
test("puts object", async () => {
|
||||
expect(await adapter.objectExists(filename)).toBeFalse();
|
||||
|
||||
const result = await adapter.putObject(filename, file);
|
||||
console.log("result", result);
|
||||
expect(result).toBeDefined();
|
||||
expect(result?.name).toBe(filename);
|
||||
});
|
||||
|
||||
test("object exists", async () => {
|
||||
await Bun.sleep(10000);
|
||||
const one = await adapter.objectExists(_filename);
|
||||
const two = await adapter.objectExists(filename);
|
||||
expect(await adapter.objectExists(filename)).toBeTrue();
|
||||
});
|
||||
|
||||
test("object meta", async () => {
|
||||
const result = await adapter.getObjectMeta(filename);
|
||||
console.log("objectMeta:result", result);
|
||||
expect(result).toBeDefined();
|
||||
expect(result.type).toBe("image/png");
|
||||
expect(result.size).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("list objects", async () => {
|
||||
const result = await adapter.listObjects();
|
||||
console.log("listObjects:result", result);
|
||||
});
|
||||
});
|
||||
46
app/__test__/media/adapters/StorageLocalAdapter.spec.ts
Normal file
46
app/__test__/media/adapters/StorageLocalAdapter.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { randomString } from "../../../src/core/utils";
|
||||
import { StorageLocalAdapter } from "../../../src/media/storage/adapters/StorageLocalAdapter";
|
||||
|
||||
describe("StorageLocalAdapter", () => {
|
||||
const adapter = new StorageLocalAdapter({
|
||||
path: `${import.meta.dir}/local`
|
||||
});
|
||||
|
||||
const file = Bun.file(`${import.meta.dir}/icon.png`);
|
||||
const _filename = randomString(10);
|
||||
const filename = `${_filename}.png`;
|
||||
|
||||
let objects = 0;
|
||||
|
||||
test("puts an object", async () => {
|
||||
objects = (await adapter.listObjects()).length;
|
||||
expect(await adapter.putObject(filename, await file.arrayBuffer())).toBeString();
|
||||
});
|
||||
|
||||
test("lists objects", async () => {
|
||||
expect((await adapter.listObjects()).length).toBe(objects + 1);
|
||||
});
|
||||
|
||||
test("file exists", async () => {
|
||||
expect(await adapter.objectExists(filename)).toBeTrue();
|
||||
});
|
||||
|
||||
test("gets an object", async () => {
|
||||
const res = await adapter.getObject(filename, new Headers());
|
||||
expect(res.ok).toBeTrue();
|
||||
// @todo: check the content
|
||||
});
|
||||
|
||||
test("gets object meta", async () => {
|
||||
expect(await adapter.getObjectMeta(filename)).toEqual({
|
||||
type: file.type, // image/png
|
||||
size: file.size
|
||||
});
|
||||
});
|
||||
|
||||
test("deletes an object", async () => {
|
||||
expect(await adapter.deleteObject(filename)).toBeUndefined();
|
||||
expect(await adapter.objectExists(filename)).toBeFalse();
|
||||
});
|
||||
});
|
||||
96
app/__test__/media/adapters/StorageS3Adapter.spec.ts
Normal file
96
app/__test__/media/adapters/StorageS3Adapter.spec.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { randomString } from "../../../src/core/utils";
|
||||
import { StorageS3Adapter } from "../../../src/media";
|
||||
|
||||
import { config } from "dotenv";
|
||||
const dotenvOutput = config({ path: `${import.meta.dir}/../../.env` });
|
||||
const { R2_ACCESS_KEY, R2_SECRET_ACCESS_KEY, R2_URL, AWS_ACCESS_KEY, AWS_SECRET_KEY, AWS_S3_URL } =
|
||||
dotenvOutput.parsed!;
|
||||
|
||||
// @todo: mock r2/s3 responses for faster tests
|
||||
const ALL_TESTS = process.env.ALL_TESTS;
|
||||
|
||||
describe("Storage", async () => {
|
||||
console.log("ALL_TESTS", process.env.ALL_TESTS);
|
||||
const versions = [
|
||||
[
|
||||
"r2",
|
||||
new StorageS3Adapter({
|
||||
access_key: R2_ACCESS_KEY as string,
|
||||
secret_access_key: R2_SECRET_ACCESS_KEY as string,
|
||||
url: R2_URL as string
|
||||
})
|
||||
],
|
||||
[
|
||||
"s3",
|
||||
new StorageS3Adapter({
|
||||
access_key: AWS_ACCESS_KEY as string,
|
||||
secret_access_key: AWS_SECRET_KEY as string,
|
||||
url: AWS_S3_URL as string
|
||||
})
|
||||
]
|
||||
] as const;
|
||||
|
||||
const _conf = {
|
||||
adapters: ["r2", "s3"],
|
||||
tests: [
|
||||
"listObjects",
|
||||
"putObject",
|
||||
"objectExists",
|
||||
"getObject",
|
||||
"deleteObject",
|
||||
"getObjectMeta"
|
||||
]
|
||||
};
|
||||
|
||||
const file = Bun.file(`${import.meta.dir}/icon.png`);
|
||||
const filename = `${randomString(10)}.png`;
|
||||
|
||||
// single (dev)
|
||||
//_conf = { adapters: [/*"r2",*/ "s3"], tests: [/*"putObject",*/ "listObjects"] };
|
||||
|
||||
function disabled(test: (typeof _conf.tests)[number]) {
|
||||
return !_conf.tests.includes(test);
|
||||
}
|
||||
|
||||
// @todo: add mocked fetch for faster tests
|
||||
describe.each(versions)("StorageS3Adapter for %s", async (name, adapter) => {
|
||||
if (!_conf.adapters.includes(name)) {
|
||||
console.log("Skipping", name);
|
||||
return;
|
||||
}
|
||||
|
||||
let objects = 0;
|
||||
|
||||
test.skipIf(disabled("putObject"))("puts an object", async () => {
|
||||
objects = (await adapter.listObjects()).length;
|
||||
expect(await adapter.putObject(filename, file)).toBeString();
|
||||
});
|
||||
|
||||
test.skipIf(disabled("listObjects"))("lists objects", async () => {
|
||||
expect((await adapter.listObjects()).length).toBe(objects + 1);
|
||||
});
|
||||
|
||||
test.skipIf(disabled("objectExists"))("file exists", async () => {
|
||||
expect(await adapter.objectExists(filename)).toBeTrue();
|
||||
});
|
||||
|
||||
test.skipIf(disabled("getObject"))("gets an object", async () => {
|
||||
const res = await adapter.getObject(filename, new Headers());
|
||||
expect(res.ok).toBeTrue();
|
||||
// @todo: check the content
|
||||
});
|
||||
|
||||
test.skipIf(disabled("getObjectMeta"))("gets object meta", async () => {
|
||||
expect(await adapter.getObjectMeta(filename)).toEqual({
|
||||
type: file.type, // image/png
|
||||
size: file.size
|
||||
});
|
||||
});
|
||||
|
||||
test.skipIf(disabled("deleteObject"))("deletes an object", async () => {
|
||||
expect(await adapter.deleteObject(filename)).toBeUndefined();
|
||||
expect(await adapter.objectExists(filename)).toBeFalse();
|
||||
});
|
||||
});
|
||||
});
|
||||
BIN
app/__test__/media/adapters/icon.png
Normal file
BIN
app/__test__/media/adapters/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
Reference in New Issue
Block a user