mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:29:32 -05:00 
			
		
		
		
	fix(server): exclude archived assets from orphaned files (#7334)
* fix(server): exclude archived assets from orphaned files * add e2e test
This commit is contained in:
		
							parent
							
								
									b3131dfe14
								
							
						
					
					
						commit
						07ef008b40
					
				@ -4,6 +4,7 @@ name: immich-e2e
 | 
			
		||||
 | 
			
		||||
x-server-build: &server-common
 | 
			
		||||
  image: immich-server:latest
 | 
			
		||||
  container_name: immich-e2e-server
 | 
			
		||||
  build:
 | 
			
		||||
    context: ../
 | 
			
		||||
    dockerfile: server/Dockerfile
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										51
									
								
								e2e/src/api/specs/audit.e2e-spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								e2e/src/api/specs/audit.e2e-spec.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
import {
 | 
			
		||||
  deleteAssets,
 | 
			
		||||
  getAuditFiles,
 | 
			
		||||
  updateAsset,
 | 
			
		||||
  type LoginResponseDto,
 | 
			
		||||
} from '@immich/sdk';
 | 
			
		||||
import { apiUtils, asBearerAuth, dbUtils, fileUtils } from 'src/utils';
 | 
			
		||||
import { beforeAll, describe, expect, it } from 'vitest';
 | 
			
		||||
 | 
			
		||||
describe('/audit', () => {
 | 
			
		||||
  let admin: LoginResponseDto;
 | 
			
		||||
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    apiUtils.setup();
 | 
			
		||||
    await dbUtils.reset();
 | 
			
		||||
    await fileUtils.reset();
 | 
			
		||||
 | 
			
		||||
    admin = await apiUtils.adminSetup();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('GET :/file-report', () => {
 | 
			
		||||
    it('excludes assets without issues from report', async () => {
 | 
			
		||||
      const [trashedAsset, archivedAsset, _] = await Promise.all([
 | 
			
		||||
        apiUtils.createAsset(admin.accessToken),
 | 
			
		||||
        apiUtils.createAsset(admin.accessToken),
 | 
			
		||||
        apiUtils.createAsset(admin.accessToken),
 | 
			
		||||
      ]);
 | 
			
		||||
 | 
			
		||||
      await Promise.all([
 | 
			
		||||
        deleteAssets(
 | 
			
		||||
          { assetBulkDeleteDto: { ids: [trashedAsset.id] } },
 | 
			
		||||
          { headers: asBearerAuth(admin.accessToken) }
 | 
			
		||||
        ),
 | 
			
		||||
        updateAsset(
 | 
			
		||||
          {
 | 
			
		||||
            id: archivedAsset.id,
 | 
			
		||||
            updateAssetDto: { isArchived: true },
 | 
			
		||||
          },
 | 
			
		||||
          { headers: asBearerAuth(admin.accessToken) }
 | 
			
		||||
        ),
 | 
			
		||||
      ]);
 | 
			
		||||
 | 
			
		||||
      const body = await getAuditFiles({
 | 
			
		||||
        headers: asBearerAuth(admin.accessToken),
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      expect(body.orphans).toHaveLength(0);
 | 
			
		||||
      expect(body.extras).toHaveLength(0);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -17,14 +17,17 @@ import {
 | 
			
		||||
  updatePerson,
 | 
			
		||||
} from '@immich/sdk';
 | 
			
		||||
import { BrowserContext } from '@playwright/test';
 | 
			
		||||
import { spawn } from 'child_process';
 | 
			
		||||
import { exec, spawn } from 'child_process';
 | 
			
		||||
import { randomBytes } from 'node:crypto';
 | 
			
		||||
import { access } from 'node:fs/promises';
 | 
			
		||||
import path from 'node:path';
 | 
			
		||||
import { promisify } from 'node:util';
 | 
			
		||||
import pg from 'pg';
 | 
			
		||||
import { loginDto, signupDto } from 'src/fixtures';
 | 
			
		||||
import request from 'supertest';
 | 
			
		||||
 | 
			
		||||
const execPromise = promisify(exec);
 | 
			
		||||
 | 
			
		||||
export const app = 'http://127.0.0.1:2283/api';
 | 
			
		||||
 | 
			
		||||
const directoryExists = (directory: string) =>
 | 
			
		||||
@ -35,6 +38,9 @@ const directoryExists = (directory: string) =>
 | 
			
		||||
// TODO move test assets into e2e/assets
 | 
			
		||||
export const testAssetDir = path.resolve(`./../server/test/assets/`);
 | 
			
		||||
 | 
			
		||||
const serverContainerName = 'immich-e2e-server';
 | 
			
		||||
const uploadMediaDir = '/usr/src/app/upload/upload';
 | 
			
		||||
 | 
			
		||||
if (!(await directoryExists(`${testAssetDir}/albums`))) {
 | 
			
		||||
  throw new Error(
 | 
			
		||||
    `Test assets not found. Please checkout https://github.com/immich-app/test-assets into ${testAssetDir} before testing`
 | 
			
		||||
@ -50,6 +56,14 @@ export const asKeyAuth = (key: string) => ({ 'x-api-key': key });
 | 
			
		||||
 | 
			
		||||
let client: pg.Client | null = null;
 | 
			
		||||
 | 
			
		||||
export const fileUtils = {
 | 
			
		||||
  reset: async () => {
 | 
			
		||||
    await execPromise(
 | 
			
		||||
      `docker exec -i "${serverContainerName}" rm -R "${uploadMediaDir}"`
 | 
			
		||||
    );
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const dbUtils = {
 | 
			
		||||
  createFace: async ({
 | 
			
		||||
    assetId,
 | 
			
		||||
 | 
			
		||||
@ -167,7 +167,7 @@ export class AuditService {
 | 
			
		||||
      `Found ${libraryFiles.size} original files, ${thumbFiles.size} thumbnails, ${videoFiles.size} encoded videos, ${profileFiles.size} profile files`,
 | 
			
		||||
    );
 | 
			
		||||
    const pagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (options) =>
 | 
			
		||||
      this.assetRepository.getAll(options, { withDeleted: true }),
 | 
			
		||||
      this.assetRepository.getAll(options, { withDeleted: true, withArchived: true }),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let assetCount = 0;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user