diff --git a/server/src/services/activity.service.spec.ts b/server/src/services/activity.service.spec.ts index 1e5bb3b505..82bd8bba89 100644 --- a/server/src/services/activity.service.spec.ts +++ b/server/src/services/activity.service.spec.ts @@ -23,7 +23,7 @@ describe(ActivityService.name, () => { mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set([albumId])); mocks.activity.search.mockResolvedValue([]); - await expect(sut.getAll(factory.auth({ id: userId }), { assetId, albumId })).resolves.toEqual([]); + await expect(sut.getAll(factory.auth({ user: { id: userId } }), { assetId, albumId })).resolves.toEqual([]); expect(mocks.activity.search).toHaveBeenCalledWith({ assetId, albumId, isLiked: undefined }); }); @@ -35,7 +35,7 @@ describe(ActivityService.name, () => { mocks.activity.search.mockResolvedValue([]); await expect( - sut.getAll(factory.auth({ id: userId }), { assetId, albumId, type: ReactionType.LIKE }), + sut.getAll(factory.auth({ user: { id: userId } }), { assetId, albumId, type: ReactionType.LIKE }), ).resolves.toEqual([]); expect(mocks.activity.search).toHaveBeenCalledWith({ assetId, albumId, isLiked: true }); @@ -80,7 +80,7 @@ describe(ActivityService.name, () => { mocks.access.activity.checkCreateAccess.mockResolvedValue(new Set([albumId])); mocks.activity.create.mockResolvedValue(activity); - await sut.create(factory.auth({ id: userId }), { + await sut.create(factory.auth({ user: { id: userId } }), { albumId, assetId, type: ReactionType.COMMENT, @@ -116,7 +116,7 @@ describe(ActivityService.name, () => { mocks.activity.create.mockResolvedValue(activity); mocks.activity.search.mockResolvedValue([]); - await sut.create(factory.auth({ id: userId }), { albumId, assetId, type: ReactionType.LIKE }); + await sut.create(factory.auth({ user: { id: userId } }), { albumId, assetId, type: ReactionType.LIKE }); expect(mocks.activity.create).toHaveBeenCalledWith({ userId: activity.userId, albumId, assetId, isLiked: true }); }); diff --git a/server/src/services/api-key.service.spec.ts b/server/src/services/api-key.service.spec.ts index 0a89c04b0d..680cd38f1e 100644 --- a/server/src/services/api-key.service.spec.ts +++ b/server/src/services/api-key.service.spec.ts @@ -54,7 +54,7 @@ describe(ApiKeyService.name, () => { }); it('should throw an error if the api key does not have sufficient permissions', async () => { - const auth = factory.auth({ apiKey: factory.authApiKey({ permissions: [Permission.ASSET_READ] }) }); + const auth = factory.auth({ apiKey: { permissions: [Permission.ASSET_READ] } }); await expect(sut.create(auth, { permissions: [Permission.ASSET_UPDATE] })).rejects.toBeInstanceOf( BadRequestException, diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index 470f29fb3d..4f4b88c320 100755 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -88,7 +88,7 @@ describe(AssetService.name, () => { it('should get memories with partners with inTimeline enabled', async () => { const partner = factory.partner(); - const auth = factory.auth({ id: partner.sharedWithId }); + const auth = factory.auth({ user: { id: partner.sharedWithId } }); mocks.partner.getAll.mockResolvedValue([partner]); mocks.asset.getByDayOfYear.mockResolvedValue([]); @@ -139,7 +139,7 @@ describe(AssetService.name, () => { it('should not include partner assets if not in timeline', async () => { const partner = factory.partner({ inTimeline: false }); - const auth = factory.auth({ id: partner.sharedWithId }); + const auth = factory.auth({ user: { id: partner.sharedWithId } }); mocks.asset.getRandom.mockResolvedValue([assetStub.image]); mocks.partner.getAll.mockResolvedValue([partner]); @@ -151,7 +151,7 @@ describe(AssetService.name, () => { it('should include partner assets if in timeline', async () => { const partner = factory.partner({ inTimeline: true }); - const auth = factory.auth({ id: partner.sharedWithId }); + const auth = factory.auth({ user: { id: partner.sharedWithId } }); mocks.asset.getRandom.mockResolvedValue([assetStub.image]); mocks.partner.getAll.mockResolvedValue([partner]); diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 32274dacd3..fe379ef489 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -7,19 +7,18 @@ import { AuthService } from 'src/services/auth.service'; import { UserMetadataItem } from 'src/types'; import { sharedLinkStub } from 'test/fixtures/shared-link.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; -import { userStub } from 'test/fixtures/user.stub'; import { factory } from 'test/small.factory'; import { newTestService, ServiceMocks } from 'test/utils'; -const oauthResponse = { +const oauthResponse = ({ id, email, name }: { id: string; email: string; name: string }) => ({ accessToken: 'cmFuZG9tLWJ5dGVz', - userId: 'user-id', - userEmail: 'immich@test.com', - name: 'immich_name', + userId: id, + userEmail: email, + name, profileImagePath: '', isAdmin: false, shouldChangePassword: false, -}; +}); // const token = Buffer.from('my-api-key', 'utf8').toString('base64'); @@ -39,15 +38,7 @@ const fixtures = { }, }; -const oauthUserWithDefaultQuota = { - email, - name: ' ', - oauthId: sub, - quotaSizeInBytes: '1073741824', - storageLabel: null, -}; - -describe('AuthService', () => { +describe(AuthService.name, () => { let sut: AuthService; let mocks: ServiceMocks; @@ -118,14 +109,12 @@ describe('AuthService', () => { describe('changePassword', () => { it('should change the password', async () => { - const auth = { user: { email: 'test@imimch.com' } } as AuthDto; + const user = factory.userAdmin(); + const auth = factory.auth({ user }); const dto = { password: 'old-password', newPassword: 'new-password' }; - mocks.user.getByEmail.mockResolvedValue({ - email: 'test@immich.com', - password: 'hash-password', - } as UserAdmin & { password: string }); - mocks.user.update.mockResolvedValue(userStub.user1); + mocks.user.getByEmail.mockResolvedValue({ ...user, password: 'hash-password' }); + mocks.user.update.mockResolvedValue(user); await sut.changePassword(auth, dto); @@ -331,37 +320,39 @@ describe('AuthService', () => { }); it('should accept a base64url key', async () => { - mocks.sharedLink.getByKey.mockResolvedValue(sharedLinkStub.valid as any); - mocks.user.get.mockResolvedValue(userStub.admin); + const user = factory.userAdmin(); + const sharedLink = { ...sharedLinkStub.valid, user } as any; + + mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); + mocks.user.get.mockResolvedValue(user); await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLinkStub.valid.key.toString('base64url') }, + headers: { 'x-immich-share-key': sharedLink.key.toString('base64url') }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), - ).resolves.toEqual({ - user: userStub.admin, - sharedLink: sharedLinkStub.valid, - }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key); + ).resolves.toEqual({ user, sharedLink }); + + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); }); it('should accept a hex key', async () => { - mocks.sharedLink.getByKey.mockResolvedValue(sharedLinkStub.valid as any); - mocks.user.get.mockResolvedValue(userStub.admin); + const user = factory.userAdmin(); + const sharedLink = { ...sharedLinkStub.valid, user } as any; + + mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); + mocks.user.get.mockResolvedValue(user); await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLinkStub.valid.key.toString('hex') }, + headers: { 'x-immich-share-key': sharedLink.key.toString('hex') }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), - ).resolves.toEqual({ - user: userStub.admin, - sharedLink: sharedLinkStub.valid, - }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key); + ).resolves.toEqual({ user, sharedLink }); + + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); }); }); @@ -533,24 +524,28 @@ describe('AuthService', () => { }); it('should link an existing user', async () => { + const user = factory.userAdmin(); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthEnabled); - mocks.user.getByEmail.mockResolvedValue(userStub.user1); - mocks.user.update.mockResolvedValue(userStub.user1); + mocks.user.getByEmail.mockResolvedValue(user); + mocks.user.update.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse, + oauthResponse(user), ); expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1); - expect(mocks.user.update).toHaveBeenCalledWith(userStub.user1.id, { oauthId: sub }); + expect(mocks.user.update).toHaveBeenCalledWith(user.id, { oauthId: sub }); }); it('should not link to a user with a different oauth sub', async () => { + const user = factory.userAdmin({ isAdmin: true, oauthId: 'existing-sub' }); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithAutoRegister); - mocks.user.getByEmail.mockResolvedValueOnce({ ...userStub.user1, oauthId: 'existing-sub' }); - mocks.user.getAdmin.mockResolvedValue(userStub.user1); - mocks.user.create.mockResolvedValue(userStub.user1); + mocks.user.getByEmail.mockResolvedValueOnce(user); + mocks.user.getAdmin.mockResolvedValue(user); + mocks.user.create.mockResolvedValue(user); await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toThrow( BadRequestException, @@ -561,14 +556,16 @@ describe('AuthService', () => { }); it('should allow auto registering by default', async () => { + const user = factory.userAdmin({ oauthId: 'oauth-id' }); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled); mocks.user.getByEmail.mockResolvedValue(void 0); - mocks.user.getAdmin.mockResolvedValue(userStub.user1); - mocks.user.create.mockResolvedValue(userStub.user1); + mocks.user.getAdmin.mockResolvedValue(factory.userAdmin({ isAdmin: true })); + mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse, + oauthResponse(user), ); expect(mocks.user.getByEmail).toHaveBeenCalledTimes(2); // second call is for domain check before create @@ -576,10 +573,12 @@ describe('AuthService', () => { }); it('should throw an error if user should be auto registered but the email claim does not exist', async () => { + const user = factory.userAdmin({ isAdmin: true }); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled); mocks.user.getByEmail.mockResolvedValue(void 0); - mocks.user.getAdmin.mockResolvedValue(userStub.user1); - mocks.user.create.mockResolvedValue(userStub.user1); + mocks.user.getAdmin.mockResolvedValue(user); + mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); mocks.oauth.getProfile.mockResolvedValue({ sub, email: undefined }); @@ -600,8 +599,10 @@ describe('AuthService', () => { 'app.immich:///oauth-callback?code=abc123', ]) { it(`should use the mobile redirect override for a url of ${url}`, async () => { + const user = factory.userAdmin(); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithMobileOverride); - mocks.user.getByOAuthId.mockResolvedValue(userStub.user1); + mocks.user.getByOAuthId.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); await sut.callback({ url }, loginDetails); @@ -611,86 +612,97 @@ describe('AuthService', () => { } it('should use the default quota', async () => { + const user = factory.userAdmin({ oauthId: 'oauth-id' }); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithStorageQuota); mocks.user.getByEmail.mockResolvedValue(void 0); - mocks.user.getAdmin.mockResolvedValue(userStub.user1); - mocks.user.create.mockResolvedValue(userStub.user1); + mocks.user.getAdmin.mockResolvedValue(factory.userAdmin({ isAdmin: true })); + mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse, + oauthResponse(user), ); - expect(mocks.user.create).toHaveBeenCalledWith({ ...oauthUserWithDefaultQuota, quotaSizeInBytes: 1_073_741_824 }); + expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 })); }); it('should ignore an invalid storage quota', async () => { + const user = factory.userAdmin({ oauthId: 'oauth-id' }); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithStorageQuota); + mocks.oauth.getProfile.mockResolvedValue({ sub: user.oauthId, email: user.email, immich_quota: 'abc' }); + mocks.user.getAdmin.mockResolvedValue(factory.userAdmin({ isAdmin: true })); mocks.user.getByEmail.mockResolvedValue(void 0); - mocks.user.getAdmin.mockResolvedValue(userStub.user1); - mocks.user.create.mockResolvedValue(userStub.user1); - mocks.oauth.getProfile.mockResolvedValue({ sub, email, immich_quota: 'abc' }); + mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse, + oauthResponse(user), ); - expect(mocks.user.create).toHaveBeenCalledWith({ ...oauthUserWithDefaultQuota, quotaSizeInBytes: 1_073_741_824 }); + expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 })); }); it('should ignore a negative quota', async () => { + const user = factory.userAdmin({ oauthId: 'oauth-id' }); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithStorageQuota); + mocks.oauth.getProfile.mockResolvedValue({ sub: user.oauthId, email: user.email, immich_quota: -5 }); + mocks.user.getAdmin.mockResolvedValue(user); mocks.user.getByEmail.mockResolvedValue(void 0); - mocks.user.getAdmin.mockResolvedValue(userStub.user1); - mocks.user.create.mockResolvedValue(userStub.user1); - mocks.oauth.getProfile.mockResolvedValue({ sub, email, immich_quota: -5 }); + mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse, + oauthResponse(user), ); - expect(mocks.user.create).toHaveBeenCalledWith({ ...oauthUserWithDefaultQuota, quotaSizeInBytes: 1_073_741_824 }); + expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 })); }); it('should not set quota for 0 quota', async () => { + const user = factory.userAdmin({ oauthId: 'oauth-id' }); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithStorageQuota); + mocks.oauth.getProfile.mockResolvedValue({ sub: user.oauthId, email: user.email, immich_quota: 0 }); + mocks.user.getAdmin.mockResolvedValue(factory.userAdmin({ isAdmin: true })); mocks.user.getByEmail.mockResolvedValue(void 0); - mocks.user.getAdmin.mockResolvedValue(userStub.user1); - mocks.user.create.mockResolvedValue(userStub.user1); - mocks.oauth.getProfile.mockResolvedValue({ sub, email, immich_quota: 0 }); + mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse, + oauthResponse(user), ); expect(mocks.user.create).toHaveBeenCalledWith({ - email, + email: user.email, name: ' ', - oauthId: sub, + oauthId: user.oauthId, quotaSizeInBytes: null, storageLabel: null, }); }); it('should use a valid storage quota', async () => { + const user = factory.userAdmin({ oauthId: 'oauth-id' }); + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithStorageQuota); + mocks.oauth.getProfile.mockResolvedValue({ sub: user.oauthId, email: user.email, immich_quota: 5 }); mocks.user.getByEmail.mockResolvedValue(void 0); - mocks.user.getAdmin.mockResolvedValue(userStub.user1); - mocks.user.create.mockResolvedValue(userStub.user1); - mocks.oauth.getProfile.mockResolvedValue({ sub, email, immich_quota: 5 }); + mocks.user.getAdmin.mockResolvedValue(factory.userAdmin({ isAdmin: true })); + mocks.user.getByOAuthId.mockResolvedValue(void 0); + mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse, + oauthResponse(user), ); expect(mocks.user.create).toHaveBeenCalledWith({ - email, + email: user.email, name: ' ', - oauthId: sub, + oauthId: user.oauthId, quotaSizeInBytes: 5_368_709_120, storageLabel: null, }); @@ -699,12 +711,11 @@ describe('AuthService', () => { describe('link', () => { it('should link an account', async () => { - const authUser = factory.authUser(); - const authApiKey = factory.authApiKey({ permissions: [] }); - const auth = { user: authUser, apiKey: authApiKey }; + const user = factory.userAdmin(); + const auth = factory.auth({ apiKey: { permissions: [] }, user }); mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled); - mocks.user.update.mockResolvedValue(userStub.user1); + mocks.user.update.mockResolvedValue(user); await sut.link(auth, { url: 'http://immich/user-settings?code=abc123' }); @@ -729,12 +740,11 @@ describe('AuthService', () => { describe('unlink', () => { it('should unlink an account', async () => { - const authUser = factory.authUser(); - const authApiKey = factory.authApiKey({ permissions: [] }); - const auth = { user: authUser, apiKey: authApiKey }; + const user = factory.userAdmin(); + const auth = factory.auth({ user, apiKey: { permissions: [] } }); mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled); - mocks.user.update.mockResolvedValue(userStub.user1); + mocks.user.update.mockResolvedValue(user); await sut.unlink(auth); diff --git a/server/src/services/cli.service.spec.ts b/server/src/services/cli.service.spec.ts index ce591a7e62..1140d44601 100644 --- a/server/src/services/cli.service.spec.ts +++ b/server/src/services/cli.service.spec.ts @@ -1,5 +1,5 @@ import { CliService } from 'src/services/cli.service'; -import { userStub } from 'test/fixtures/user.stub'; +import { factory } from 'test/small.factory'; import { newTestService, ServiceMocks } from 'test/utils'; import { describe, it } from 'vitest'; @@ -13,7 +13,7 @@ describe(CliService.name, () => { describe('listUsers', () => { it('should list users', async () => { - mocks.user.getList.mockResolvedValue([userStub.admin]); + mocks.user.getList.mockResolvedValue([factory.userAdmin({ isAdmin: true })]); await expect(sut.listUsers()).resolves.toEqual([expect.objectContaining({ isAdmin: true })]); expect(mocks.user.getList).toHaveBeenCalledWith({ withDeleted: true }); }); @@ -30,8 +30,10 @@ describe(CliService.name, () => { }); it('should default to a random password', async () => { - mocks.user.getAdmin.mockResolvedValue(userStub.admin); - mocks.user.update.mockResolvedValue(userStub.admin); + const admin = factory.userAdmin({ isAdmin: true }); + + mocks.user.getAdmin.mockResolvedValue(admin); + mocks.user.update.mockResolvedValue(factory.userAdmin({ isAdmin: true })); const ask = vitest.fn().mockImplementation(() => {}); @@ -41,13 +43,15 @@ describe(CliService.name, () => { expect(response.provided).toBe(false); expect(ask).toHaveBeenCalled(); - expect(id).toEqual(userStub.admin.id); + expect(id).toEqual(admin.id); expect(update.password).toBeDefined(); }); it('should use the supplied password', async () => { - mocks.user.getAdmin.mockResolvedValue(userStub.admin); - mocks.user.update.mockResolvedValue(userStub.admin); + const admin = factory.userAdmin({ isAdmin: true }); + + mocks.user.getAdmin.mockResolvedValue(admin); + mocks.user.update.mockResolvedValue(admin); const ask = vitest.fn().mockResolvedValue('new-password'); @@ -57,7 +61,7 @@ describe(CliService.name, () => { expect(response.provided).toBe(true); expect(ask).toHaveBeenCalled(); - expect(id).toEqual(userStub.admin.id); + expect(id).toEqual(admin.id); expect(update.password).toBeDefined(); }); }); diff --git a/server/src/services/map.service.spec.ts b/server/src/services/map.service.spec.ts index 95750f5590..6dc56abf44 100644 --- a/server/src/services/map.service.spec.ts +++ b/server/src/services/map.service.spec.ts @@ -35,7 +35,7 @@ describe(MapService.name, () => { it('should include partner assets', async () => { const partner = factory.partner(); - const auth = factory.auth({ id: partner.sharedWithId }); + const auth = factory.auth({ user: { id: partner.sharedWithId } }); const asset = assetStub.withLocation; const marker = { diff --git a/server/src/services/memory.service.spec.ts b/server/src/services/memory.service.spec.ts index 22a4199572..7ce8b1ab46 100644 --- a/server/src/services/memory.service.spec.ts +++ b/server/src/services/memory.service.spec.ts @@ -24,7 +24,7 @@ describe(MemoryService.name, () => { mocks.memory.search.mockResolvedValue([memory1, memory2]); - await expect(sut.search(factory.auth({ id: userId }), {})).resolves.toEqual( + await expect(sut.search(factory.auth({ user: { id: userId } }), {})).resolves.toEqual( expect.arrayContaining([ expect.objectContaining({ id: memory1.id, assets: [expect.objectContaining({ id: asset.id })] }), expect.objectContaining({ id: memory2.id, assets: [] }), @@ -60,7 +60,9 @@ describe(MemoryService.name, () => { mocks.memory.get.mockResolvedValue(memory); mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id])); - await expect(sut.get(factory.auth({ id: userId }), memory.id)).resolves.toMatchObject({ id: memory.id }); + await expect(sut.get(factory.auth({ user: { id: userId } }), memory.id)).resolves.toMatchObject({ + id: memory.id, + }); expect(mocks.memory.get).toHaveBeenCalledWith(memory.id); expect(mocks.access.memory.checkOwnerAccess).toHaveBeenCalledWith(memory.ownerId, new Set([memory.id])); @@ -75,7 +77,7 @@ describe(MemoryService.name, () => { mocks.memory.create.mockResolvedValue(memory); await expect( - sut.create(factory.auth({ id: userId }), { + sut.create(factory.auth({ user: { id: userId } }), { type: memory.type, data: memory.data, memoryAt: memory.memoryAt, @@ -105,7 +107,7 @@ describe(MemoryService.name, () => { mocks.memory.create.mockResolvedValue(memory); await expect( - sut.create(factory.auth({ id: userId }), { + sut.create(factory.auth({ user: { id: userId } }), { type: memory.type, data: memory.data, assetIds: memory.assets.map((asset) => asset.id), diff --git a/server/src/services/partner.service.spec.ts b/server/src/services/partner.service.spec.ts index 6c3460666e..c6d5762c2c 100644 --- a/server/src/services/partner.service.spec.ts +++ b/server/src/services/partner.service.spec.ts @@ -22,7 +22,7 @@ describe(PartnerService.name, () => { const user2 = factory.user(); const sharedWithUser2 = factory.partner({ sharedBy: user1, sharedWith: user2 }); const sharedWithUser1 = factory.partner({ sharedBy: user2, sharedWith: user1 }); - const auth = factory.auth({ id: user1.id }); + const auth = factory.auth({ user: { id: user1.id } }); mocks.partner.getAll.mockResolvedValue([sharedWithUser1, sharedWithUser2]); @@ -35,7 +35,7 @@ describe(PartnerService.name, () => { const user2 = factory.user(); const sharedWithUser2 = factory.partner({ sharedBy: user1, sharedWith: user2 }); const sharedWithUser1 = factory.partner({ sharedBy: user2, sharedWith: user1 }); - const auth = factory.auth({ id: user1.id }); + const auth = factory.auth({ user: { id: user1.id } }); mocks.partner.getAll.mockResolvedValue([sharedWithUser1, sharedWithUser2]); await expect(sut.search(auth, { direction: PartnerDirection.SharedWith })).resolves.toBeDefined(); @@ -48,7 +48,7 @@ describe(PartnerService.name, () => { const user1 = factory.user(); const user2 = factory.user(); const partner = factory.partner({ sharedBy: user1, sharedWith: user2 }); - const auth = factory.auth({ id: user1.id }); + const auth = factory.auth({ user: { id: user1.id } }); mocks.partner.get.mockResolvedValue(void 0); mocks.partner.create.mockResolvedValue(partner); @@ -65,7 +65,7 @@ describe(PartnerService.name, () => { const user1 = factory.user(); const user2 = factory.user(); const partner = factory.partner({ sharedBy: user1, sharedWith: user2 }); - const auth = factory.auth({ id: user1.id }); + const auth = factory.auth({ user: { id: user1.id } }); mocks.partner.get.mockResolvedValue(partner); @@ -80,7 +80,7 @@ describe(PartnerService.name, () => { const user1 = factory.user(); const user2 = factory.user(); const partner = factory.partner({ sharedBy: user1, sharedWith: user2 }); - const auth = factory.auth({ id: user1.id }); + const auth = factory.auth({ user: { id: user1.id } }); mocks.partner.get.mockResolvedValue(partner); @@ -113,7 +113,7 @@ describe(PartnerService.name, () => { const user1 = factory.user(); const user2 = factory.user(); const partner = factory.partner({ sharedBy: user1, sharedWith: user2 }); - const auth = factory.auth({ id: user1.id }); + const auth = factory.auth({ user: { id: user1.id } }); mocks.access.partner.checkUpdateAccess.mockResolvedValue(new Set([user2.id])); mocks.partner.update.mockResolvedValue(partner); diff --git a/server/src/services/person.service.spec.ts b/server/src/services/person.service.spec.ts index 7012bc538b..01093d78cf 100644 --- a/server/src/services/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -1280,7 +1280,7 @@ describe(PersonService.name, () => { describe('mapFace', () => { it('should map a face', () => { - const authDto = factory.auth({ id: faceStub.face1.person.ownerId }); + const authDto = factory.auth({ user: { id: faceStub.face1.person.ownerId } }); expect(mapFaces(faceStub.face1, authDto)).toEqual({ boundingBoxX1: 0, boundingBoxX2: 1, diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index 8c79f752b7..4d084d6e67 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -7,6 +7,7 @@ import { albumStub } from 'test/fixtures/album.stub'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { sharedLinkResponseStub, sharedLinkStub } from 'test/fixtures/shared-link.stub'; +import { factory } from 'test/small.factory'; import { newTestService, ServiceMocks } from 'test/utils'; describe(SharedLinkService.name, () => { @@ -46,7 +47,13 @@ describe(SharedLinkService.name, () => { }); it('should not return metadata', async () => { - const authDto = authStub.adminSharedLinkNoExif; + const authDto = factory.auth({ + sharedLink: { + showExif: false, + allowDownload: true, + allowUpload: true, + }, + }); mocks.sharedLink.get.mockResolvedValue(sharedLinkStub.readonlyNoExif); await expect(sut.getMine(authDto, {})).resolves.toEqual(sharedLinkResponseStub.readonlyNoMetadata); expect(mocks.sharedLink.get).toHaveBeenCalledWith(authDto.user.id, authDto.sharedLink?.id); @@ -208,7 +215,9 @@ describe(SharedLinkService.name, () => { it('should update a shared link', async () => { mocks.sharedLink.get.mockResolvedValue(sharedLinkStub.valid); mocks.sharedLink.update.mockResolvedValue(sharedLinkStub.valid); + await sut.update(authStub.user1, sharedLinkStub.valid.id, { allowDownload: false }); + expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, sharedLinkStub.valid.id); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ id: sharedLinkStub.valid.id, @@ -242,6 +251,7 @@ describe(SharedLinkService.name, () => { describe('addAssets', () => { it('should not work on album shared links', async () => { mocks.sharedLink.get.mockResolvedValue(sharedLinkStub.valid); + await expect(sut.addAssets(authStub.admin, 'link-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf( BadRequestException, ); @@ -273,6 +283,7 @@ describe(SharedLinkService.name, () => { describe('removeAssets', () => { it('should not work on album shared links', async () => { mocks.sharedLink.get.mockResolvedValue(sharedLinkStub.valid); + await expect(sut.removeAssets(authStub.admin, 'link-1', { assetIds: ['asset-1'] })).rejects.toBeInstanceOf( BadRequestException, ); @@ -297,31 +308,39 @@ describe(SharedLinkService.name, () => { describe('getMetadataTags', () => { it('should return null when auth is not a shared link', async () => { await expect(sut.getMetadataTags(authStub.admin)).resolves.toBe(null); + expect(mocks.sharedLink.get).not.toHaveBeenCalled(); }); it('should return null when shared link has a password', async () => { - await expect(sut.getMetadataTags(authStub.passwordSharedLink)).resolves.toBe(null); + const auth = factory.auth({ user: {}, sharedLink: { password: 'password' } }); + + await expect(sut.getMetadataTags(auth)).resolves.toBe(null); + expect(mocks.sharedLink.get).not.toHaveBeenCalled(); }); it('should return metadata tags', async () => { mocks.sharedLink.get.mockResolvedValue(sharedLinkStub.individual); + await expect(sut.getMetadataTags(authStub.adminSharedLink)).resolves.toEqual({ description: '1 shared photos & videos', imageUrl: `https://my.immich.app/api/assets/asset-id/thumbnail?key=LCtkaJX4R1O_9D-2lq0STzsPryoL1UdAbyb6Sna1xxmQCSuqU2J1ZUsqt6GR-yGm1s0`, title: 'Public Share', }); + expect(mocks.sharedLink.get).toHaveBeenCalled(); }); it('should return metadata tags with a default image path if the asset id is not set', async () => { mocks.sharedLink.get.mockResolvedValue({ ...sharedLinkStub.individual, album: undefined, assets: [] }); + await expect(sut.getMetadataTags(authStub.adminSharedLink)).resolves.toEqual({ description: '0 shared photos & videos', imageUrl: `https://my.immich.app/feature-panel.png`, title: 'Public Share', }); + expect(mocks.sharedLink.get).toHaveBeenCalled(); }); }); diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index edff406b48..46603cdbce 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -5,6 +5,7 @@ import { StorageTemplateService } from 'src/services/storage-template.service'; import { albumStub } from 'test/fixtures/album.stub'; import { assetStub } from 'test/fixtures/asset.stub'; import { userStub } from 'test/fixtures/user.stub'; +import { factory } from 'test/small.factory'; import { makeStream, newTestService, ServiceMocks } from 'test/utils'; const motionAsset = assetStub.storageAsset({}); @@ -426,15 +427,16 @@ describe(StorageTemplateService.name, () => { }); it('should use the user storage label', async () => { - const asset = assetStub.storageAsset(); + const user = factory.userAdmin({ storageLabel: 'label-1' }); + const asset = assetStub.storageAsset({ ownerId: user.id }); mocks.asset.streamStorageTemplateAssets.mockReturnValue(makeStream([asset])); - mocks.user.getList.mockResolvedValue([userStub.storageLabel]); + mocks.user.getList.mockResolvedValue([user]); mocks.move.create.mockResolvedValue({ id: '123', entityId: asset.id, pathType: AssetPathType.ORIGINAL, oldPath: asset.originalPath, - newPath: `upload/library/user-id/2023/2023-02-23/${asset.originalFileName}`, + newPath: `upload/library/${user.storageLabel}/2023/2023-02-23/${asset.originalFileName}`, }); await sut.handleMigration(); @@ -442,11 +444,11 @@ describe(StorageTemplateService.name, () => { expect(mocks.asset.streamStorageTemplateAssets).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - `upload/library/label-1/2022/2022-06-19/${asset.originalFileName}`, + `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, ); expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, - originalPath: `upload/library/label-1/2022/2022-06-19/${asset.originalFileName}`, + originalPath: `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, }); }); @@ -551,98 +553,106 @@ describe(StorageTemplateService.name, () => { describe('file rename correctness', () => { it('should not create double extensions when filename has lower extension', async () => { + const user = factory.userAdmin({ storageLabel: 'label-1' }); const asset = assetStub.storageAsset({ - originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.heic', + ownerId: user.id, + originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, originalFileName: 'IMG_7065.HEIC', }); mocks.asset.streamStorageTemplateAssets.mockReturnValue(makeStream([asset])); - mocks.user.getList.mockResolvedValue([userStub.storageLabel]); + mocks.user.getList.mockResolvedValue([user]); mocks.move.create.mockResolvedValue({ id: '123', entityId: asset.id, pathType: AssetPathType.ORIGINAL, - oldPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.heic', - newPath: 'upload/library/user-id/2023/2023-02-23/IMG_7065.heic', + oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.asset.streamStorageTemplateAssets).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - 'upload/library/user-id/2022/2022-06-19/IMG_7065.heic', - 'upload/library/label-1/2022/2022-06-19/IMG_7065.heic', + `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + `upload/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`, ); }); it('should not create double extensions when filename has uppercase extension', async () => { + const user = factory.userAdmin(); const asset = assetStub.storageAsset({ - originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.HEIC', + ownerId: user.id, + originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, originalFileName: 'IMG_7065.HEIC', }); mocks.asset.streamStorageTemplateAssets.mockReturnValue(makeStream([asset])); - mocks.user.getList.mockResolvedValue([userStub.storageLabel]); + mocks.user.getList.mockResolvedValue([user]); mocks.move.create.mockResolvedValue({ id: '123', entityId: asset.id, pathType: AssetPathType.ORIGINAL, - oldPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.HEIC', - newPath: 'upload/library/user-id/2023/2023-02-23/IMG_7065.heic', + oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.asset.streamStorageTemplateAssets).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - 'upload/library/user-id/2022/2022-06-19/IMG_7065.HEIC', - 'upload/library/label-1/2022/2022-06-19/IMG_7065.heic', + `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, ); }); it('should normalize the filename to lowercase (JPEG > jpg)', async () => { + const user = factory.userAdmin(); const asset = assetStub.storageAsset({ - originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPEG', + ownerId: user.id, + originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, originalFileName: 'IMG_7065.JPEG', }); mocks.asset.streamStorageTemplateAssets.mockReturnValue(makeStream([asset])); - mocks.user.getList.mockResolvedValue([userStub.storageLabel]); + mocks.user.getList.mockResolvedValue([user]); mocks.move.create.mockResolvedValue({ id: '123', entityId: asset.id, pathType: AssetPathType.ORIGINAL, - oldPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPEG', - newPath: 'upload/library/user-id/2023/2023-02-23/IMG_7065.jpg', + oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.asset.streamStorageTemplateAssets).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPEG', - 'upload/library/label-1/2022/2022-06-19/IMG_7065.jpg', + `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + `upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`, ); }); it('should normalize the filename to lowercase (JPG > jpg)', async () => { + const user = factory.userAdmin(); const asset = assetStub.storageAsset({ + ownerId: user.id, originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG', originalFileName: 'IMG_7065.JPG', }); mocks.asset.streamStorageTemplateAssets.mockReturnValue(makeStream([asset])); - mocks.user.getList.mockResolvedValue([userStub.storageLabel]); + mocks.user.getList.mockResolvedValue([user]); mocks.move.create.mockResolvedValue({ id: '123', entityId: asset.id, pathType: AssetPathType.ORIGINAL, - oldPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG', - newPath: 'upload/library/user-id/2023/2023-02-23/IMG_7065.jpg', + oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, + newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.asset.streamStorageTemplateAssets).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG', - 'upload/library/label-1/2022/2022-06-19/IMG_7065.jpg', + `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, + `upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`, ); }); }); diff --git a/server/src/services/sync.service.spec.ts b/server/src/services/sync.service.spec.ts index 27a54b2b58..5f7357c64d 100644 --- a/server/src/services/sync.service.spec.ts +++ b/server/src/services/sync.service.spec.ts @@ -39,7 +39,7 @@ describe(SyncService.name, () => { describe('getChangesForDeltaSync', () => { it('should return a response requiring a full sync when partners are out of sync', async () => { const partner = factory.partner(); - const auth = factory.auth({ id: partner.sharedWithId }); + const auth = factory.auth({ user: { id: partner.sharedWithId } }); mocks.partner.getAll.mockResolvedValue([partner]); diff --git a/server/src/services/timeline.service.spec.ts b/server/src/services/timeline.service.spec.ts index 1c2c422433..c6a09d2fdf 100644 --- a/server/src/services/timeline.service.spec.ts +++ b/server/src/services/timeline.service.spec.ts @@ -3,6 +3,7 @@ import { TimeBucketSize } from 'src/repositories/asset.repository'; import { TimelineService } from 'src/services/timeline.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; +import { factory } from 'test/small.factory'; import { newTestService, ServiceMocks } from 'test/utils'; describe(TimelineService.name, () => { @@ -114,15 +115,15 @@ describe(TimelineService.name, () => { mocks.access.album.checkSharedLinkAccess.mockResolvedValue(new Set(['album-id'])); mocks.asset.getTimeBucket.mockResolvedValue([assetStub.image]); - const buckets = await sut.getTimeBucket( - { ...authStub.admin, sharedLink: { ...authStub.adminSharedLink.sharedLink!, showExif: false } }, - { - size: TimeBucketSize.DAY, - timeBucket: 'bucket', - isArchived: true, - albumId: 'album-id', - }, - ); + const auth = factory.auth({ sharedLink: { showExif: false } }); + + const buckets = await sut.getTimeBucket(auth, { + size: TimeBucketSize.DAY, + timeBucket: 'bucket', + isArchived: true, + albumId: 'album-id', + }); + expect(buckets).toEqual([expect.objectContaining({ id: 'asset-id' })]); expect(buckets[0]).not.toHaveProperty('exif'); expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith('bucket', { diff --git a/server/src/services/user.service.spec.ts b/server/src/services/user.service.spec.ts index df00fdfaa4..4ee92fc3d3 100644 --- a/server/src/services/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -29,7 +29,7 @@ describe(UserService.name, () => { describe('getAll', () => { it('admin should get all users', async () => { const user = factory.userAdmin(); - const auth = factory.auth(user); + const auth = factory.auth({ user }); mocks.user.getList.mockResolvedValue([user]); @@ -39,14 +39,12 @@ describe(UserService.name, () => { }); it('non-admin should get all users when publicUsers enabled', async () => { - mocks.user.getList.mockResolvedValue([userStub.user1]); + const user = factory.userAdmin(); + const auth = factory.auth({ user }); - await expect(sut.search(authStub.user1)).resolves.toEqual([ - expect.objectContaining({ - id: authStub.user1.user.id, - email: authStub.user1.user.email, - }), - ]); + mocks.user.getList.mockResolvedValue([user]); + + await expect(sut.search(auth)).resolves.toEqual([expect.objectContaining({ id: user.id, email: user.email })]); expect(mocks.user.getList).toHaveBeenCalledWith({ withDeleted: false }); }); @@ -107,17 +105,19 @@ describe(UserService.name, () => { it('should throw an error if the user profile could not be updated with the new image', async () => { const file = { path: '/profile/path' } as Express.Multer.File; - mocks.user.get.mockResolvedValue(userStub.profilePath); + const user = factory.userAdmin({ profileImagePath: '/path/to/profile.jpg' }); + mocks.user.get.mockResolvedValue(user); mocks.user.update.mockRejectedValue(new InternalServerErrorException('mocked error')); await expect(sut.createProfileImage(authStub.admin, file)).rejects.toThrowError(InternalServerErrorException); }); it('should delete the previous profile image', async () => { + const user = factory.userAdmin({ profileImagePath: '/path/to/profile.jpg' }); const file = { path: '/profile/path' } as Express.Multer.File; - const files = [userStub.profilePath.profileImagePath]; + const files = [user.profileImagePath]; - mocks.user.get.mockResolvedValue(userStub.profilePath); + mocks.user.get.mockResolvedValue(user); mocks.user.update.mockResolvedValue({ ...userStub.admin, profileImagePath: file.path }); await sut.createProfileImage(authStub.admin, file); @@ -149,8 +149,10 @@ describe(UserService.name, () => { }); it('should delete the profile image if user has one', async () => { - mocks.user.get.mockResolvedValue(userStub.profilePath); - const files = [userStub.profilePath.profileImagePath]; + const user = factory.userAdmin({ profileImagePath: '/path/to/profile.jpg' }); + const files = [user.profileImagePath]; + + mocks.user.get.mockResolvedValue(user); await sut.deleteProfileImage(authStub.admin); @@ -176,9 +178,10 @@ describe(UserService.name, () => { }); it('should return the profile picture', async () => { - mocks.user.get.mockResolvedValue(userStub.profilePath); + const user = factory.userAdmin({ profileImagePath: '/path/to/profile.jpg' }); + mocks.user.get.mockResolvedValue(user); - await expect(sut.getProfileImage(userStub.profilePath.id)).resolves.toEqual( + await expect(sut.getProfileImage(user.id)).resolves.toEqual( new ImmichFileResponse({ path: '/path/to/profile.jpg', contentType: 'image/jpeg', @@ -186,7 +189,7 @@ describe(UserService.name, () => { }), ); - expect(mocks.user.get).toHaveBeenCalledWith(userStub.profilePath.id, {}); + expect(mocks.user.get).toHaveBeenCalledWith(user.id, {}); }); }); diff --git a/server/test/fixtures/auth.stub.ts b/server/test/fixtures/auth.stub.ts index f5fbe07b53..dfa21fc707 100644 --- a/server/test/fixtures/auth.stub.ts +++ b/server/test/fixtures/auth.stub.ts @@ -52,24 +52,4 @@ export const authStub = { key: Buffer.from('shared-link-key'), } as SharedLinkEntity, }), - adminSharedLinkNoExif: Object.freeze({ - user: authUser.admin, - sharedLink: { - id: '123', - showExif: false, - allowDownload: true, - allowUpload: true, - key: Buffer.from('shared-link-key'), - } as SharedLinkEntity, - }), - passwordSharedLink: Object.freeze({ - user: authUser.admin, - sharedLink: { - id: '123', - allowUpload: false, - allowDownload: false, - password: 'password-123', - showExif: true, - } as SharedLinkEntity, - }), }; diff --git a/server/test/fixtures/user.stub.ts b/server/test/fixtures/user.stub.ts index 0dd5409602..f0043d174a 100644 --- a/server/test/fixtures/user.stub.ts +++ b/server/test/fixtures/user.stub.ts @@ -57,36 +57,4 @@ export const userStub = { quotaSizeInBytes: null, quotaUsageInBytes: 0, }, - storageLabel: { - ...authStub.user1.user, - status: UserStatus.ACTIVE, - profileChangedAt: new Date('2021-01-01'), - metadata: [], - name: 'immich_name', - storageLabel: 'label-1', - oauthId: '', - shouldChangePassword: false, - profileImagePath: '', - createdAt: new Date('2021-01-01'), - deletedAt: null, - updatedAt: new Date('2021-01-01'), - quotaSizeInBytes: null, - quotaUsageInBytes: 0, - }, - profilePath: { - ...authStub.user1.user, - status: UserStatus.ACTIVE, - profileChangedAt: new Date('2021-01-01'), - metadata: [], - name: 'immich_name', - storageLabel: 'label-1', - oauthId: '', - shouldChangePassword: false, - profileImagePath: '/path/to/profile.jpg', - createdAt: new Date('2021-01-01'), - deletedAt: null, - updatedAt: new Date('2021-01-01'), - quotaSizeInBytes: null, - quotaUsageInBytes: 0, - }, }; diff --git a/server/test/small.factory.ts b/server/test/small.factory.ts index 35984cabbd..313660bff4 100644 --- a/server/test/small.factory.ts +++ b/server/test/small.factory.ts @@ -4,6 +4,7 @@ import { ApiKey, Asset, AuthApiKey, + AuthSharedLink, AuthUser, Library, Memory, @@ -35,12 +36,20 @@ export const newEmbedding = () => { const authFactory = ({ apiKey, session, - ...user -}: Partial & { apiKey?: Partial; session?: { id: string } } = {}) => { + sharedLink, + user, +}: { + apiKey?: Partial; + session?: { id: string }; + user?: Partial; + sharedLink?: Partial; +} = {}) => { const auth: AuthDto = { - user: authUserFactory(user), + user: authUserFactory(userAdminFactory(user ?? {})), }; + const userId = auth.user.id; + if (apiKey) { auth.apiKey = authApiKeyFactory(apiKey); } @@ -49,24 +58,45 @@ const authFactory = ({ auth.session = { id: session.id }; } + if (sharedLink) { + auth.sharedLink = authSharedLinkFactory({ ...sharedLink, userId }); + } + return auth; }; +const authSharedLinkFactory = (sharedLink: Partial = {}) => { + const { + id = newUuid(), + expiresAt = null, + userId = newUuid(), + showExif = true, + allowUpload = false, + allowDownload = true, + password = null, + } = sharedLink; + + return { id, expiresAt, userId, showExif, allowUpload, allowDownload, password }; +}; + const authApiKeyFactory = (apiKey: Partial = {}) => ({ id: newUuid(), permissions: [Permission.ALL], ...apiKey, }); -const authUserFactory = (authUser: Partial = {}) => ({ - id: newUuid(), - isAdmin: false, - name: 'Test User', - email: 'test@immich.cloud', - quotaUsageInBytes: 0, - quotaSizeInBytes: null, - ...authUser, -}); +const authUserFactory = (authUser: Partial = {}) => { + const { + id = newUuid(), + isAdmin = false, + name = 'Test User', + email = 'test@immich.cloud', + quotaUsageInBytes = 0, + quotaSizeInBytes = null, + } = authUser; + + return { id, isAdmin, name, email, quotaUsageInBytes, quotaSizeInBytes }; +}; const partnerFactory = (partner: Partial = {}) => { const sharedBy = userFactory(partner.sharedBy || {}); @@ -112,25 +142,44 @@ const userFactory = (user: Partial = {}) => ({ ...user, }); -const userAdminFactory = (user: Partial = {}) => ({ - id: newUuid(), - name: 'Test User', - email: 'test@immich.cloud', - profileImagePath: '', - profileChangedAt: newDate(), - storageLabel: null, - shouldChangePassword: false, - isAdmin: false, - createdAt: newDate(), - updatedAt: newDate(), - deletedAt: null, - oauthId: '', - quotaSizeInBytes: null, - quotaUsageInBytes: 0, - status: UserStatus.ACTIVE, - metadata: [], - ...user, -}); +const userAdminFactory = (user: Partial = {}) => { + const { + id = newUuid(), + name = 'Test User', + email = 'test@immich.cloud', + profileImagePath = '', + profileChangedAt = newDate(), + storageLabel = null, + shouldChangePassword = false, + isAdmin = false, + createdAt = newDate(), + updatedAt = newDate(), + deletedAt = null, + oauthId = '', + quotaSizeInBytes = null, + quotaUsageInBytes = 0, + status = UserStatus.ACTIVE, + metadata = [], + } = user; + return { + id, + name, + email, + profileImagePath, + profileChangedAt, + storageLabel, + shouldChangePassword, + isAdmin, + createdAt, + updatedAt, + deletedAt, + oauthId, + quotaSizeInBytes, + quotaUsageInBytes, + status, + metadata, + }; +}; const assetFactory = (asset: Partial = {}) => ({ id: newUuid(),