s3-orm
Version:
Object-Relational Mapping (ORM) interface for Amazon S3, enabling model-based data operations with indexing and querying capabilities
139 lines (116 loc) • 4.98 kB
text/typescript
import { S3Client, ListObjectsV2Command, HeadObjectCommand, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import { mockClient } from 'aws-sdk-client-mock';
import { Readable } from 'stream';
import { Readable as NodeReadable } from 'stream';
import { sdkStreamMixin } from '@aws-sdk/util-stream-node';
import {S3Helper} from './S3Helper';
import AuthError from '../../lib/errors/AuthError';
const s3Mock = mockClient(S3Client);
describe('S3Helper', () => {
let s3Helper: S3Helper;
beforeEach(() => {
s3Mock.reset();
s3Helper = new S3Helper({
bucket: 'test-bucket',
prefix: 'test-prefix',
region: 'us-east-1',
accessKeyId: 'test-key',
secretAccessKey: 'test-secret'
});
});
describe('constructor', () => {
it('should throw error if no options provided', () => {
expect(() => new S3Helper(null as any)).toThrow('You must pass configuration settings!');
});
it('should throw error if no bucket specified', () => {
expect(() => new S3Helper({} as any)).toThrow('No AWS Bucket specified!');
});
it('should set default values', () => {
const helper = new S3Helper({
bucket: 'test-bucket',
prefix: 'test-prefix',
accessKeyId: 'test-key',
secretAccessKey: 'test-secret'
});
expect(helper.getRegion()).toBe('us-east-1');
expect(helper.getBucket()).toBe('test-bucket');
});
it('should mark as authenticated when credentials provided', () => {
expect(s3Helper['authenticated']).toBe(true);
});
});
describe('list', () => {
it('should list objects in directory', async () => {
const mockObjects = [
{ Key: 'test/file1.txt' },
{ Key: 'test/file2.txt' }
];
s3Mock.on(ListObjectsV2Command).resolves({
Contents: mockObjects
});
const result = await s3Helper.list('test/');
expect(result).toEqual(mockObjects);
});
it('should return empty array when no objects found', async () => {
s3Mock.on(ListObjectsV2Command).resolves({});
const result = await s3Helper.list('test/');
expect(result).toEqual([]);
});
});
describe('exists', () => {
it('should return true when object exists', async () => {
s3Mock.on(HeadObjectCommand).resolves({});
const result = await s3Helper.exists('test/file.txt');
expect(result).toBe(true);
});
it('should return false when object does not exist', async () => {
s3Mock.on(HeadObjectCommand).rejects(new Error('Not found'));
const result = await s3Helper.exists('test/file.txt');
expect(result).toBe(false);
});
});
describe('get', () => {
it('should get file content', async () => {
const mockContent = Buffer.from('test content');
const mockStream = sdkStreamMixin(NodeReadable.from([mockContent]));
s3Mock.on(GetObjectCommand).resolves({
Body: mockStream
});
const result = await s3Helper.get('test/file.txt');
expect(result).toBe('test content');
}, 10000); // Increase timeout to 10 seconds
it('should throw error for invalid body type', async () => {
s3Mock.on(GetObjectCommand).resolves({
Body: undefined
});
await expect(s3Helper.get('test/file.txt')).rejects.toThrow('Invalid response body type');
});
});
describe('uploadString', () => {
it('should throw AuthError when not authenticated', async () => {
const unauthenticatedHelper = new S3Helper({
bucket: 'test-bucket',
prefix: 'test-prefix',
accessKeyId: '',
secretAccessKey: ''
});
await expect(unauthenticatedHelper.uploadString('content', 'test.txt'))
.rejects.toThrow(AuthError);
});
it('should upload string content', async () => {
s3Mock.on(PutObjectCommand).resolves({});
const result = await s3Helper.uploadString('test content', 'test/file.txt');
expect(result).toBe('https://test-bucket.s3.amazonaws.com/test/file.txt');
});
});
describe('getUrl', () => {
it('should return correct URL', () => {
expect(s3Helper.getUrl('test/file.txt'))
.toBe('https://test-bucket.s3.amazonaws.com/test/file.txt');
});
it('should handle leading slash', () => {
expect(s3Helper.getUrl('/test/file.txt'))
.toBe('https://test-bucket.s3.amazonaws.com/test/file.txt');
});
});
});