From 784268ab605a3cd80fd55eaa492a25504e78ee17 Mon Sep 17 00:00:00 2001 From: nityam Date: Fri, 29 May 2026 12:40:20 +0530 Subject: [PATCH 1/2] count only referenced assets toward upload limit --- server/controllers/aws.controller.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/controllers/aws.controller.js b/server/controllers/aws.controller.js index b6e03db13c..7bdd8ca281 100644 --- a/server/controllers/aws.controller.js +++ b/server/controllers/aws.controller.js @@ -105,7 +105,6 @@ export async function listObjectsInS3ForUser(userId) { size: asset.size, url: `${process.env.S3_BUCKET_URL_BASE}${asset.key}` }; - totalSize += asset.size; const wasMatched = projects.some((project) => project.files.some((file) => { @@ -121,8 +120,11 @@ export async function listObjectsInS3ForUser(userId) { }) ); + // Only count referenced assets toward the limit; unreferenced S3 + // leftovers aren't shown or deletable in "My Assets". if (wasMatched) { projectAssets.push(foundAsset); + totalSize += asset.size; } }); From ac4f6e1d47ae6d7ec5aeaf05df3692d406089cfd Mon Sep 17 00:00:00 2001 From: nityam Date: Fri, 29 May 2026 14:00:01 +0530 Subject: [PATCH 2/2] test referenced-only asset total --- .../__tests__/aws.controller.test.js | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 server/controllers/__tests__/aws.controller.test.js diff --git a/server/controllers/__tests__/aws.controller.test.js b/server/controllers/__tests__/aws.controller.test.js new file mode 100644 index 0000000000..a3e062be6f --- /dev/null +++ b/server/controllers/__tests__/aws.controller.test.js @@ -0,0 +1,75 @@ +/** + * @jest-environment node + */ +import Project from '../../models/project'; +import { listObjectsInS3ForUser } from '../aws.controller'; + +const mockSend = jest.fn(); + +jest.mock('../../models/project'); +jest.mock('@aws-sdk/client-s3', () => { + const actual = jest.requireActual('@aws-sdk/client-s3'); + return { + ...actual, + S3Client: jest.fn().mockImplementation(() => ({ + send: (...args) => mockSend(...args) + })) + }; +}); + +describe('listObjectsInS3ForUser()', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('excludes unreferenced (orphaned) S3 objects from totalSize', async () => { + mockSend.mockResolvedValue({ + Contents: [ + { Key: 'user1/referenced.png', Size: 100 }, + { Key: 'user1/orphan.png', Size: 999 } + ] + }); + Project.getProjectsForUserId = jest.fn().mockResolvedValue([ + { + id: 'project-1', + name: 'sketch one', + files: [ + { + name: 'referenced.png', + url: 'https://s3.example.com/user1/referenced.png' + } + ] + } + ]); + + const result = await listObjectsInS3ForUser('user1'); + + expect(result.totalSize).toBe(100); + expect(result.assets).toHaveLength(1); + expect(result.assets[0].key).toBe('user1/referenced.png'); + }); + + it('counts every object when they are all referenced', async () => { + mockSend.mockResolvedValue({ + Contents: [ + { Key: 'user1/a.png', Size: 100 }, + { Key: 'user1/b.png', Size: 50 } + ] + }); + Project.getProjectsForUserId = jest.fn().mockResolvedValue([ + { + id: 'project-1', + name: 'sketch one', + files: [ + { name: 'a.png', url: 'https://s3.example.com/user1/a.png' }, + { name: 'b.png', url: 'https://s3.example.com/user1/b.png' } + ] + } + ]); + + const result = await listObjectsInS3ForUser('user1'); + + expect(result.totalSize).toBe(150); + expect(result.assets).toHaveLength(2); + }); +});