f9b58a0e3d
guardedFetch blocks loopback/private/link-local/metadata IPs and never calls fetch; decryptHeaders fails open (returns undefined, warns once, no blob leak). yjs.util setYjsMark/removeYjsMarkByAttribute/updateYjsMarkAttribute on real Y.Docs. SHARED_TOOL_SPECS<->in-app parity (name/desc/input-schema; a dropped or renamed wiring fails). Replace the tautological storage.service spec with driver-delegation checks across every public method. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
111 lines
4.1 KiB
TypeScript
111 lines
4.1 KiB
TypeScript
import { Readable } from 'stream';
|
|
import { StorageService } from './storage.service';
|
|
import type { StorageDriver } from './interfaces';
|
|
|
|
/**
|
|
* StorageService is a thin facade over the injected StorageDriver: each public
|
|
* method must forward to the driver with the SAME arguments and return/await the
|
|
* driver's result unchanged (the read paths return it; the write paths await it).
|
|
* A mock driver lets us assert that delegation exactly, with no real S3/disk IO.
|
|
*/
|
|
describe('StorageService delegation', () => {
|
|
// Every driver method is a jest mock so we can assert call args + return passing.
|
|
function buildDriver(): jest.Mocked<StorageDriver> {
|
|
return {
|
|
upload: jest.fn().mockResolvedValue(undefined),
|
|
uploadStream: jest.fn().mockResolvedValue(undefined),
|
|
copy: jest.fn().mockResolvedValue(undefined),
|
|
read: jest.fn(),
|
|
readStream: jest.fn(),
|
|
readRangeStream: jest.fn(),
|
|
exists: jest.fn(),
|
|
getUrl: jest.fn(),
|
|
getSignedUrl: jest.fn(),
|
|
delete: jest.fn().mockResolvedValue(undefined),
|
|
getDriver: jest.fn(),
|
|
getDriverName: jest.fn(),
|
|
getConfig: jest.fn(),
|
|
} as unknown as jest.Mocked<StorageDriver>;
|
|
}
|
|
|
|
let driver: jest.Mocked<StorageDriver>;
|
|
let service: StorageService;
|
|
|
|
beforeEach(() => {
|
|
driver = buildDriver();
|
|
service = new StorageService(driver as unknown as StorageDriver);
|
|
});
|
|
|
|
it('upload forwards path + content to the driver', async () => {
|
|
const buf = Buffer.from('data');
|
|
await service.upload('a/b.png', buf);
|
|
expect(driver.upload).toHaveBeenCalledWith('a/b.png', buf);
|
|
});
|
|
|
|
it('uploadStream forwards path, stream and options', async () => {
|
|
const stream = Readable.from(['x']);
|
|
await service.uploadStream('a/b.bin', stream, { recreateClient: true });
|
|
expect(driver.uploadStream).toHaveBeenCalledWith('a/b.bin', stream, {
|
|
recreateClient: true,
|
|
});
|
|
});
|
|
|
|
it('copy forwards both paths', async () => {
|
|
await service.copy('from.txt', 'to.txt');
|
|
expect(driver.copy).toHaveBeenCalledWith('from.txt', 'to.txt');
|
|
});
|
|
|
|
it('read returns the driver buffer unchanged', async () => {
|
|
const buf = Buffer.from('content');
|
|
driver.read.mockResolvedValue(buf);
|
|
await expect(service.read('f.txt')).resolves.toBe(buf);
|
|
expect(driver.read).toHaveBeenCalledWith('f.txt');
|
|
});
|
|
|
|
it('readStream returns the driver stream unchanged', async () => {
|
|
const stream = Readable.from(['y']);
|
|
driver.readStream.mockResolvedValue(stream);
|
|
await expect(service.readStream('f.bin')).resolves.toBe(stream);
|
|
expect(driver.readStream).toHaveBeenCalledWith('f.bin');
|
|
});
|
|
|
|
it('readRangeStream forwards the range object and returns the stream', async () => {
|
|
const stream = Readable.from(['z']);
|
|
driver.readRangeStream.mockResolvedValue(stream);
|
|
const range = { start: 0, end: 99 };
|
|
await expect(service.readRangeStream('f.bin', range)).resolves.toBe(stream);
|
|
expect(driver.readRangeStream).toHaveBeenCalledWith('f.bin', range);
|
|
});
|
|
|
|
it('exists returns the driver boolean', async () => {
|
|
driver.exists.mockResolvedValue(false);
|
|
await expect(service.exists('missing')).resolves.toBe(false);
|
|
expect(driver.exists).toHaveBeenCalledWith('missing');
|
|
});
|
|
|
|
it('getSignedUrl forwards path + expiry and returns the signed url', async () => {
|
|
driver.getSignedUrl.mockResolvedValue('https://signed/url');
|
|
await expect(service.getSignedUrl('f.png', 600)).resolves.toBe(
|
|
'https://signed/url',
|
|
);
|
|
expect(driver.getSignedUrl).toHaveBeenCalledWith('f.png', 600);
|
|
});
|
|
|
|
it('getUrl returns the driver url synchronously', () => {
|
|
driver.getUrl.mockReturnValue('https://cdn/f.png');
|
|
expect(service.getUrl('f.png')).toBe('https://cdn/f.png');
|
|
expect(driver.getUrl).toHaveBeenCalledWith('f.png');
|
|
});
|
|
|
|
it('delete forwards the path', async () => {
|
|
await service.delete('old.txt');
|
|
expect(driver.delete).toHaveBeenCalledWith('old.txt');
|
|
});
|
|
|
|
it('getDriverName returns the driver name', () => {
|
|
driver.getDriverName.mockReturnValue('s3');
|
|
expect(service.getDriverName()).toBe('s3');
|
|
expect(driver.getDriverName).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|