Compare commits

...

2 Commits

Author SHA1 Message Date
shenlong-tanwen 6c432a592a move tests to be medium tests 2026-07-09 13:31:17 +05:30
shenlong-tanwen fbecc16e2d fix: remove partner assets from existing memories 2026-07-09 04:29:19 +05:30
7 changed files with 80 additions and 6 deletions
@@ -0,0 +1,16 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
// Delete cross-owner memory assets
await sql`
DELETE FROM memory_asset
USING memory, asset
WHERE memory_asset."memoriesId" = memory.id
AND memory_asset."assetId" = asset.id
AND memory."ownerId" != asset."ownerId"
`.execute(db);
}
export async function down(): Promise<void> {
// Not implemented: the deleted rows were cross-owner entries
}
+1 -1
View File
@@ -175,7 +175,7 @@ export class AlbumService extends BaseService {
const results = await addAssets(
auth,
{ access: this.accessRepository, bulk: this.albumRepository },
{ parentId: id, assetIds: dto.ids },
{ parentId: id, assetIds: dto.ids, permission: Permission.AssetShare },
);
const { id: firstNewAssetId } = results.find(({ success }) => success) || {};
+6 -2
View File
@@ -93,7 +93,7 @@ export class MemoryService extends BaseService {
const assetIds = dto.assetIds || [];
const allowedAssetIds = await this.checkAccess({
auth,
permission: Permission.AssetShare,
permission: Permission.AssetUpdate,
ids: assetIds,
});
const memory = await this.memoryRepository.create(
@@ -134,7 +134,11 @@ export class MemoryService extends BaseService {
await this.requireAccess({ auth, permission: Permission.MemoryRead, ids: [id] });
const repos = { access: this.accessRepository, bulk: this.memoryRepository };
const results = await addAssets(auth, repos, { parentId: id, assetIds: dto.ids });
const results = await addAssets(auth, repos, {
parentId: id,
assetIds: dto.ids,
permission: Permission.AssetUpdate,
});
const hasSuccess = results.find(({ success }) => success);
if (hasSuccess) {
+1 -1
View File
@@ -104,7 +104,7 @@ export class TagService extends BaseService {
const results = await addAssets(
auth,
{ access: this.accessRepository, bulk: this.tagRepository },
{ parentId: id, assetIds: dto.ids },
{ parentId: id, assetIds: dto.ids, permission: Permission.AssetUpdate },
);
for (const { id: assetId, success } of results) {
+2 -2
View File
@@ -33,14 +33,14 @@ export const getAssetFiles = (files: AssetFile[]) => ({
export const addAssets = async (
auth: AuthDto,
repositories: { access: AccessRepository; bulk: IBulkAsset },
dto: { parentId: string; assetIds: string[] },
dto: { parentId: string; assetIds: string[]; permission: Permission },
) => {
const { access, bulk } = repositories;
const existingAssetIds = await bulk.getAssetIds(dto.parentId, dto.assetIds);
const notPresentAssetIds = dto.assetIds.filter((id) => !existingAssetIds.has(id));
const allowedAssetIds = await checkAccess(access, {
auth,
permission: Permission.AssetShare,
permission: dto.permission,
ids: notPresentAssetIds,
});
@@ -1,5 +1,6 @@
import { Kysely } from 'kysely';
import { DateTime } from 'luxon';
import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto';
import { AssetFileType, MemoryType } from 'src/enum';
import { AccessRepository } from 'src/repositories/access.repository';
import { AssetRepository } from 'src/repositories/asset.repository';
@@ -105,6 +106,43 @@ describe(MemoryService.name, () => {
}),
);
});
it('should not link a partner asset', async () => {
const { sut, ctx } = setup();
const { user: owner } = await ctx.newUser();
const { user: partner } = await ctx.newUser();
await ctx.newPartner({ sharedById: partner.id, sharedWithId: owner.id, inTimeline: true });
const { asset } = await ctx.newAsset({ ownerId: partner.id });
const auth = factory.auth({ user: owner });
const dto = {
type: MemoryType.OnThisDay,
data: { year: 2021 },
memoryAt: new Date(2021),
assetIds: [asset.id],
};
await expect(sut.create(auth, dto)).resolves.toEqual(expect.objectContaining({ assets: [] }));
});
});
describe('addAssets', () => {
it('should not link a partner asset', async () => {
const { sut, ctx } = setup();
const { user: owner } = await ctx.newUser();
const { user: partner } = await ctx.newUser();
await ctx.newPartner({ sharedById: partner.id, sharedWithId: owner.id, inTimeline: true });
const { asset } = await ctx.newAsset({ ownerId: partner.id });
const auth = factory.auth({ user: owner });
const memory = await sut.create(auth, {
type: MemoryType.OnThisDay,
data: { year: 2021 },
memoryAt: new Date(2021),
});
await expect(sut.addAssets(auth, memory.id, { ids: [asset.id] })).resolves.toEqual([
{ id: asset.id, success: false, error: BulkIdErrorReason.NO_PERMISSION },
]);
});
});
describe('onMemoryCreate', () => {
@@ -1,4 +1,5 @@
import { Kysely } from 'kysely';
import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto';
import { JobStatus } from 'src/enum';
import { AccessRepository } from 'src/repositories/access.repository';
import { AssetRepository } from 'src/repositories/asset.repository';
@@ -52,6 +53,21 @@ describe(TagService.name, () => {
);
await expect(ctx.get(TagRepository).getAssetIds(tag.id, [asset.id])).resolves.toContain(asset.id);
});
it('should not tag a partner asset', async () => {
const { sut, ctx } = setup();
ctx.getMock(EventRepository).emit.mockResolvedValue();
const { user: owner } = await ctx.newUser();
const { user: partner } = await ctx.newUser();
await ctx.newPartner({ sharedById: partner.id, sharedWithId: owner.id, inTimeline: true });
const { asset } = await ctx.newAsset({ ownerId: partner.id });
const [tag] = await upsertTags(ctx.get(TagRepository), { userId: owner.id, tags: ['tag-1'] });
const authDto = factory.auth({ user: owner });
await expect(sut.addAssets(authDto, tag.id, { ids: [asset.id] })).resolves.toEqual([
{ id: asset.id, success: false, error: BulkIdErrorReason.NO_PERMISSION },
]);
});
});
describe('deleteEmptyTags', () => {
it('single tag exists, not connected to any assets, and is deleted', async () => {