snapshot of https://github.com/ueberdosis/hocuspocus.git @ cf3cfa355b4d3bb61c6b6c80071f71783e32470e (2026-09-15)
Some checks failed
build / lint (22) (push) Failing after 17s
build / test (22) (push) Failing after 8s
build / lint (23) (push) Failing after 2s
build / build (22) (push) Has been skipped
build / build (23) (push) Has been skipped
build / test (23) (push) Failing after 4s
docsearch / docsearch (push) Failing after 1s
Some checks failed
build / lint (22) (push) Failing after 17s
build / test (22) (push) Failing after 8s
build / lint (23) (push) Failing after 2s
build / build (22) (push) Has been skipped
build / build (23) (push) Has been skipped
build / test (23) (push) Failing after 4s
docsearch / docsearch (push) Failing after 1s
This commit is contained in:
153
tests/extension-s3/fetch.ts
Normal file
153
tests/extension-s3/fetch.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
// @ts-nocheck - AWS SDK types resolved through extension package
|
||||
import test from "ava";
|
||||
import sinon from "sinon";
|
||||
import { S3 } from "@hocuspocus/extension-s3";
|
||||
import {
|
||||
S3Client,
|
||||
CreateBucketCommand,
|
||||
HeadBucketCommand,
|
||||
GetObjectCommand,
|
||||
PutObjectCommand,
|
||||
HeadObjectCommand,
|
||||
} from "@aws-sdk/client-s3";
|
||||
import { ReadableStream } from "stream/web";
|
||||
import * as Y from 'yjs'
|
||||
|
||||
const testConfig = {
|
||||
bucket: "hocuspocus-test",
|
||||
prefix: "test-documents/",
|
||||
};
|
||||
|
||||
const storage = new Map<string, Buffer>();
|
||||
const sandbox = sinon.createSandbox();
|
||||
// Create a single dummy client to be used in all tests
|
||||
const dummyS3Client = new S3Client({});
|
||||
|
||||
test.beforeEach(() => {
|
||||
sandbox.restore();
|
||||
storage.clear();
|
||||
|
||||
sandbox.stub(S3Client.prototype, "send").callsFake(async (command: any) => {
|
||||
if (command instanceof HeadBucketCommand || command instanceof HeadObjectCommand) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (command instanceof CreateBucketCommand) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (command instanceof GetObjectCommand) {
|
||||
const key = command.input.Key as string; // 确保 key 是 string 类型
|
||||
if (storage.has(key)) {
|
||||
const buffer = storage.get(key)!;
|
||||
return {
|
||||
Body: {
|
||||
// Mimic AWS SDK v3 GetObjectCommand Body interface used in the extension
|
||||
transformToWebStream: () =>
|
||||
new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(new Uint8Array(buffer));
|
||||
controller.close();
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
const err = new Error("NoSuchKey");
|
||||
err.name = "NoSuchKey";
|
||||
// @ts-expect-error
|
||||
err.$metadata = { httpStatusCode: 404 };
|
||||
throw err;
|
||||
}
|
||||
if (command instanceof PutObjectCommand) {
|
||||
const key = command.input.Key as string; // 确保 key 是 string 类型
|
||||
const body = command.input.Body;
|
||||
if (body instanceof Buffer) {
|
||||
storage.set(key, body);
|
||||
} else {
|
||||
throw new Error("Body must be a Buffer");
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach.always(() => {
|
||||
sandbox.restore();
|
||||
storage.clear();
|
||||
});
|
||||
|
||||
test("should throw an error without bucket name", async (t) => {
|
||||
t.throws(
|
||||
() => {
|
||||
new S3({});
|
||||
},
|
||||
{ message: "S3 bucket name is required" }
|
||||
);
|
||||
});
|
||||
|
||||
test("should create extension with valid configuration", async (t) => {
|
||||
const extension = new S3({ ...testConfig, s3Client: dummyS3Client });
|
||||
t.truthy(extension);
|
||||
t.is(extension.configuration.bucket, testConfig.bucket);
|
||||
});
|
||||
|
||||
test("should store and retrieve documents", async (t) => {
|
||||
const extension = new S3({ ...testConfig, s3Client: dummyS3Client });
|
||||
await (extension as any).onConfigure();
|
||||
|
||||
const documentName = "test-document";
|
||||
|
||||
const doc = new Y.Doc();
|
||||
doc.getMap('test').set('key', 'value');
|
||||
|
||||
await extension.configuration.store({
|
||||
documentName,
|
||||
state: Buffer.from(Y.encodeStateAsUpdate(doc)),
|
||||
} as any);
|
||||
|
||||
const fetched = await extension.configuration.fetch({ documentName } as any);
|
||||
t.truthy(fetched);
|
||||
t.true(fetched instanceof Uint8Array);
|
||||
t.true((fetched as Uint8Array).length > 0);
|
||||
|
||||
const doc2 = new Y.Doc();
|
||||
Y.applyUpdate(doc2, fetched!);
|
||||
t.is(doc2.getMap('test').get('key'), 'value');
|
||||
});
|
||||
|
||||
test("should handle non-existent documents", async (t) => {
|
||||
const extension = new S3({ ...testConfig, s3Client: dummyS3Client });
|
||||
await (extension as any).onConfigure();
|
||||
|
||||
const result = await extension.configuration.fetch({
|
||||
documentName: "non-existent-document",
|
||||
} as any);
|
||||
|
||||
t.is(result, null);
|
||||
});
|
||||
|
||||
test("should work with custom S3 client", async (t) => {
|
||||
const extension = new S3({
|
||||
bucket: testConfig.bucket,
|
||||
s3Client: dummyS3Client,
|
||||
});
|
||||
|
||||
t.truthy(extension);
|
||||
t.is(extension.configuration.bucket, testConfig.bucket);
|
||||
});
|
||||
|
||||
test("should use correct object key format", async (t) => {
|
||||
const documentName = "test-document-key-format";
|
||||
const extension = new S3({ ...testConfig, s3Client: dummyS3Client });
|
||||
await (extension as any).onConfigure();
|
||||
|
||||
const doc = new Y.Doc();
|
||||
doc.getMap('test').set('key', 'value');
|
||||
|
||||
await extension.configuration.store({
|
||||
documentName,
|
||||
state: Buffer.from(Y.encodeStateAsUpdate(doc)),
|
||||
} as any);
|
||||
|
||||
const expectedKey = `${testConfig.prefix}${documentName}.bin`;
|
||||
t.true(storage.has(expectedKey), `Storage should have key: ${expectedKey}`);
|
||||
});
|
||||
Reference in New Issue
Block a user