255 lines
7.9 KiB
JavaScript
255 lines
7.9 KiB
JavaScript
const S3OwnershipService = require('../../../services/s3OwnershipService');
|
|
const { Message, ConditionCheck, Rental } = require('../../../models');
|
|
|
|
jest.mock('../../../models');
|
|
|
|
describe('S3OwnershipService', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('getFileTypeFromKey', () => {
|
|
it('should return "profile" for profiles folder', () => {
|
|
expect(S3OwnershipService.getFileTypeFromKey('profiles/uuid.jpg')).toBe('profile');
|
|
});
|
|
|
|
it('should return "item" for items folder', () => {
|
|
expect(S3OwnershipService.getFileTypeFromKey('items/uuid.jpg')).toBe('item');
|
|
});
|
|
|
|
it('should return "message" for messages folder', () => {
|
|
expect(S3OwnershipService.getFileTypeFromKey('messages/uuid.jpg')).toBe('message');
|
|
});
|
|
|
|
it('should return "forum" for forum folder', () => {
|
|
expect(S3OwnershipService.getFileTypeFromKey('forum/uuid.jpg')).toBe('forum');
|
|
});
|
|
|
|
it('should return "condition-check" for condition-checks folder', () => {
|
|
expect(S3OwnershipService.getFileTypeFromKey('condition-checks/uuid.jpg')).toBe('condition-check');
|
|
});
|
|
|
|
it('should return null for unknown folder', () => {
|
|
expect(S3OwnershipService.getFileTypeFromKey('unknown/uuid.jpg')).toBeNull();
|
|
});
|
|
|
|
it('should return null for null key', () => {
|
|
expect(S3OwnershipService.getFileTypeFromKey(null)).toBeNull();
|
|
});
|
|
|
|
it('should return null for undefined key', () => {
|
|
expect(S3OwnershipService.getFileTypeFromKey(undefined)).toBeNull();
|
|
});
|
|
|
|
it('should return null for empty string', () => {
|
|
expect(S3OwnershipService.getFileTypeFromKey('')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('canAccessFile', () => {
|
|
describe('public folders', () => {
|
|
it('should authorize access to profile images for any user', async () => {
|
|
const result = await S3OwnershipService.canAccessFile('profiles/uuid.jpg', 'user-123');
|
|
|
|
expect(result).toEqual({ authorized: true });
|
|
});
|
|
|
|
it('should authorize access to item images for any user', async () => {
|
|
const result = await S3OwnershipService.canAccessFile('items/uuid.jpg', 'user-123');
|
|
|
|
expect(result).toEqual({ authorized: true });
|
|
});
|
|
|
|
it('should authorize access to forum images for any user', async () => {
|
|
const result = await S3OwnershipService.canAccessFile('forum/uuid.jpg', 'user-123');
|
|
|
|
expect(result).toEqual({ authorized: true });
|
|
});
|
|
});
|
|
|
|
describe('private folders', () => {
|
|
it('should call verifyMessageAccess for message images', async () => {
|
|
Message.findOne.mockResolvedValue({ id: 'msg-123' });
|
|
|
|
const result = await S3OwnershipService.canAccessFile('messages/uuid.jpg', 'user-123');
|
|
|
|
expect(Message.findOne).toHaveBeenCalled();
|
|
expect(result.authorized).toBe(true);
|
|
});
|
|
|
|
it('should call verifyConditionCheckAccess for condition-check images', async () => {
|
|
ConditionCheck.findOne.mockResolvedValue({ id: 'check-123' });
|
|
|
|
const result = await S3OwnershipService.canAccessFile('condition-checks/uuid.jpg', 'user-123');
|
|
|
|
expect(ConditionCheck.findOne).toHaveBeenCalled();
|
|
expect(result.authorized).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('unknown file types', () => {
|
|
it('should deny access for unknown folder', async () => {
|
|
const result = await S3OwnershipService.canAccessFile('unknown/uuid.jpg', 'user-123');
|
|
|
|
expect(result).toEqual({
|
|
authorized: false,
|
|
reason: 'Unknown file type'
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('verifyMessageAccess', () => {
|
|
const testKey = 'messages/550e8400-e29b-41d4-a716-446655440000.jpg';
|
|
const senderId = 'sender-123';
|
|
const receiverId = 'receiver-456';
|
|
|
|
it('should authorize sender to access message image', async () => {
|
|
Message.findOne.mockResolvedValue({
|
|
id: 'msg-123',
|
|
senderId,
|
|
receiverId,
|
|
imageFilename: testKey
|
|
});
|
|
|
|
const result = await S3OwnershipService.verifyMessageAccess(testKey, senderId);
|
|
|
|
expect(result).toEqual({
|
|
authorized: true,
|
|
reason: null
|
|
});
|
|
|
|
expect(Message.findOne).toHaveBeenCalledWith({
|
|
where: expect.objectContaining({
|
|
imageFilename: testKey
|
|
})
|
|
});
|
|
});
|
|
|
|
it('should authorize receiver to access message image', async () => {
|
|
Message.findOne.mockResolvedValue({
|
|
id: 'msg-123',
|
|
senderId,
|
|
receiverId,
|
|
imageFilename: testKey
|
|
});
|
|
|
|
const result = await S3OwnershipService.verifyMessageAccess(testKey, receiverId);
|
|
|
|
expect(result.authorized).toBe(true);
|
|
});
|
|
|
|
it('should deny access to unauthorized user', async () => {
|
|
Message.findOne.mockResolvedValue(null);
|
|
|
|
const result = await S3OwnershipService.verifyMessageAccess(testKey, 'other-user');
|
|
|
|
expect(result).toEqual({
|
|
authorized: false,
|
|
reason: 'Not a participant in this message'
|
|
});
|
|
});
|
|
|
|
it('should deny access when message does not exist', async () => {
|
|
Message.findOne.mockResolvedValue(null);
|
|
|
|
const result = await S3OwnershipService.verifyMessageAccess('messages/nonexistent.jpg', senderId);
|
|
|
|
expect(result.authorized).toBe(false);
|
|
expect(result.reason).toBe('Not a participant in this message');
|
|
});
|
|
});
|
|
|
|
describe('verifyConditionCheckAccess', () => {
|
|
const testKey = 'condition-checks/550e8400-e29b-41d4-a716-446655440000.jpg';
|
|
const ownerId = 'owner-123';
|
|
const renterId = 'renter-456';
|
|
|
|
it('should authorize owner to access condition check image', async () => {
|
|
ConditionCheck.findOne.mockResolvedValue({
|
|
id: 'check-123',
|
|
imageFilenames: [testKey],
|
|
rental: {
|
|
id: 'rental-123',
|
|
ownerId,
|
|
renterId
|
|
}
|
|
});
|
|
|
|
const result = await S3OwnershipService.verifyConditionCheckAccess(testKey, ownerId);
|
|
|
|
expect(result).toEqual({
|
|
authorized: true,
|
|
reason: null
|
|
});
|
|
|
|
expect(ConditionCheck.findOne).toHaveBeenCalledWith({
|
|
where: expect.objectContaining({
|
|
imageFilenames: expect.anything()
|
|
}),
|
|
include: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
model: Rental,
|
|
as: 'rental'
|
|
})
|
|
])
|
|
});
|
|
});
|
|
|
|
it('should authorize renter to access condition check image', async () => {
|
|
ConditionCheck.findOne.mockResolvedValue({
|
|
id: 'check-123',
|
|
imageFilenames: [testKey],
|
|
rental: {
|
|
id: 'rental-123',
|
|
ownerId,
|
|
renterId
|
|
}
|
|
});
|
|
|
|
const result = await S3OwnershipService.verifyConditionCheckAccess(testKey, renterId);
|
|
|
|
expect(result.authorized).toBe(true);
|
|
});
|
|
|
|
it('should deny access to unauthorized user', async () => {
|
|
ConditionCheck.findOne.mockResolvedValue(null);
|
|
|
|
const result = await S3OwnershipService.verifyConditionCheckAccess(testKey, 'other-user');
|
|
|
|
expect(result).toEqual({
|
|
authorized: false,
|
|
reason: 'Not a participant in this rental'
|
|
});
|
|
});
|
|
|
|
it('should deny access when condition check does not exist', async () => {
|
|
ConditionCheck.findOne.mockResolvedValue(null);
|
|
|
|
const result = await S3OwnershipService.verifyConditionCheckAccess(
|
|
'condition-checks/nonexistent.jpg',
|
|
ownerId
|
|
);
|
|
|
|
expect(result.authorized).toBe(false);
|
|
expect(result.reason).toBe('Not a participant in this rental');
|
|
});
|
|
|
|
it('should use Op.contains for imageFilenames array search', async () => {
|
|
const { Op } = require('sequelize');
|
|
|
|
ConditionCheck.findOne.mockResolvedValue(null);
|
|
|
|
await S3OwnershipService.verifyConditionCheckAccess(testKey, ownerId);
|
|
|
|
expect(ConditionCheck.findOne).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
where: {
|
|
imageFilenames: expect.anything()
|
|
}
|
|
})
|
|
);
|
|
});
|
|
});
|
|
});
|