Browse Source

fix(server): add missing extensions and mime types (#3318)

Add extensions and mime types which were accidentally removed in #3197.

Fixes: #3300
Thomas 2 năm trước cách đây
mục cha
commit
f0302670d2

+ 70 - 13
server/src/domain/asset/asset.service.spec.ts

@@ -12,7 +12,6 @@ import {
 import { when } from 'jest-when';
 import { when } from 'jest-when';
 import { Readable } from 'stream';
 import { Readable } from 'stream';
 import { ICryptoRepository } from '../crypto';
 import { ICryptoRepository } from '../crypto';
-import { mimeTypes } from '../domain.constant';
 import { IStorageRepository } from '../storage';
 import { IStorageRepository } from '../storage';
 import { AssetStats, IAssetRepository } from './asset.repository';
 import { AssetStats, IAssetRepository } from './asset.repository';
 import { AssetService, UploadFieldName } from './asset.service';
 import { AssetService, UploadFieldName } from './asset.service';
@@ -66,30 +65,78 @@ const uploadFile = {
   },
   },
 };
 };
 
 
+const validImages = [
+  '.3fr',
+  '.ari',
+  '.arw',
+  '.avif',
+  '.cap',
+  '.cin',
+  '.cr2',
+  '.cr3',
+  '.crw',
+  '.dcr',
+  '.dng',
+  '.erf',
+  '.fff',
+  '.gif',
+  '.heic',
+  '.heif',
+  '.iiq',
+  '.jpeg',
+  '.jpg',
+  '.jxl',
+  '.k25',
+  '.kdc',
+  '.mrw',
+  '.nef',
+  '.orf',
+  '.ori',
+  '.pef',
+  '.png',
+  '.raf',
+  '.raw',
+  '.rwl',
+  '.sr2',
+  '.srf',
+  '.srw',
+  '.tiff',
+  '.webp',
+  '.x3f',
+];
+
+const validVideos = ['.3gp', '.avi', '.flv', '.m2ts', '.mkv', '.mov', '.mp4', '.mpg', '.mts', '.webm', '.wmv'];
+
 const uploadTests = [
 const uploadTests = [
   {
   {
-    label: 'asset',
+    label: 'asset images',
+    fieldName: UploadFieldName.ASSET_DATA,
+    valid: validImages,
+    invalid: ['.html', '.xml'],
+  },
+  {
+    label: 'asset videos',
     fieldName: UploadFieldName.ASSET_DATA,
     fieldName: UploadFieldName.ASSET_DATA,
-    filetypes: Object.keys({ ...mimeTypes.image, ...mimeTypes.video }),
-    invalid: ['.xml', '.html'],
+    valid: validVideos,
+    invalid: ['.html', '.xml'],
   },
   },
   {
   {
     label: 'live photo',
     label: 'live photo',
     fieldName: UploadFieldName.LIVE_PHOTO_DATA,
     fieldName: UploadFieldName.LIVE_PHOTO_DATA,
-    filetypes: Object.keys(mimeTypes.video),
-    invalid: ['.xml', '.html', '.jpg', '.jpeg'],
+    valid: validVideos,
+    invalid: ['.html', '.jpeg', '.jpg', '.xml'],
   },
   },
   {
   {
     label: 'sidecar',
     label: 'sidecar',
     fieldName: UploadFieldName.SIDECAR_DATA,
     fieldName: UploadFieldName.SIDECAR_DATA,
-    filetypes: Object.keys(mimeTypes.sidecar),
-    invalid: ['.xml', '.html', '.jpg', '.jpeg', '.mov', '.mp4'],
+    valid: ['.xmp'],
+    invalid: ['.html', '.jpeg', '.jpg', '.mov', '.mp4', '.xml'],
   },
   },
   {
   {
     label: 'profile',
     label: 'profile',
     fieldName: UploadFieldName.PROFILE_DATA,
     fieldName: UploadFieldName.PROFILE_DATA,
-    filetypes: Object.keys(mimeTypes.profile),
-    invalid: ['.xml', '.html', '.cr2', '.arf', '.mov', '.mp4'],
+    valid: ['.avif', '.dng', '.heic', '.heif', '.jpeg', '.jpg', '.png', '.webp'],
+    invalid: ['.arf', '.cr2', '.html', '.mov', '.mp4', '.xml'],
   },
   },
 ];
 ];
 
 
@@ -117,9 +164,9 @@ describe(AssetService.name, () => {
       expect(() => sut.canUploadFile(uploadFile.nullAuth)).toThrowError(UnauthorizedException);
       expect(() => sut.canUploadFile(uploadFile.nullAuth)).toThrowError(UnauthorizedException);
     });
     });
 
 
-    for (const { fieldName, filetypes, invalid } of uploadTests) {
-      describe(`${fieldName}`, () => {
-        for (const filetype of filetypes) {
+    for (const { fieldName, valid, invalid } of uploadTests) {
+      describe(fieldName, () => {
+        for (const filetype of valid) {
           it(`should accept ${filetype}`, () => {
           it(`should accept ${filetype}`, () => {
             expect(sut.canUploadFile(uploadFile.filename(fieldName, `asset${filetype}`))).toEqual(true);
             expect(sut.canUploadFile(uploadFile.filename(fieldName, `asset${filetype}`))).toEqual(true);
           });
           });
@@ -132,6 +179,16 @@ describe(AssetService.name, () => {
             );
             );
           });
           });
         }
         }
+
+        it('should be sorted (valid)', () => {
+          // TODO: use toSorted in NodeJS 20.
+          expect(valid).toEqual([...valid].sort());
+        });
+
+        it('should be sorted (invalid)', () => {
+          // TODO: use toSorted in NodeJS 20.
+          expect(invalid).toEqual([...invalid].sort());
+        });
       });
       });
     }
     }
   });
   });

+ 191 - 0
server/src/domain/domain.constant.spec.ts

@@ -0,0 +1,191 @@
+import { mimeTypes } from '@app/domain';
+
+describe('mimeTypes', () => {
+  for (const { mimetype, extension } of [
+    // Please ensure this list is sorted.
+    { mimetype: 'image/3fr', extension: '.3fr' },
+    { mimetype: 'image/ari', extension: '.ari' },
+    { mimetype: 'image/arw', extension: '.arw' },
+    { mimetype: 'image/avif', extension: '.avif' },
+    { mimetype: 'image/cap', extension: '.cap' },
+    { mimetype: 'image/cin', extension: '.cin' },
+    { mimetype: 'image/cr2', extension: '.cr2' },
+    { mimetype: 'image/cr3', extension: '.cr3' },
+    { mimetype: 'image/crw', extension: '.crw' },
+    { mimetype: 'image/dcr', extension: '.dcr' },
+    { mimetype: 'image/dng', extension: '.dng' },
+    { mimetype: 'image/erf', extension: '.erf' },
+    { mimetype: 'image/fff', extension: '.fff' },
+    { mimetype: 'image/gif', extension: '.gif' },
+    { mimetype: 'image/heic', extension: '.heic' },
+    { mimetype: 'image/heif', extension: '.heif' },
+    { mimetype: 'image/iiq', extension: '.iiq' },
+    { mimetype: 'image/jpeg', extension: '.jpeg' },
+    { mimetype: 'image/jpeg', extension: '.jpg' },
+    { mimetype: 'image/jxl', extension: '.jxl' },
+    { mimetype: 'image/k25', extension: '.k25' },
+    { mimetype: 'image/kdc', extension: '.kdc' },
+    { mimetype: 'image/mrw', extension: '.mrw' },
+    { mimetype: 'image/nef', extension: '.nef' },
+    { mimetype: 'image/orf', extension: '.orf' },
+    { mimetype: 'image/ori', extension: '.ori' },
+    { mimetype: 'image/pef', extension: '.pef' },
+    { mimetype: 'image/png', extension: '.png' },
+    { mimetype: 'image/raf', extension: '.raf' },
+    { mimetype: 'image/raw', extension: '.raw' },
+    { mimetype: 'image/rwl', extension: '.rwl' },
+    { mimetype: 'image/sr2', extension: '.sr2' },
+    { mimetype: 'image/srf', extension: '.srf' },
+    { mimetype: 'image/srw', extension: '.srw' },
+    { mimetype: 'image/tiff', extension: '.tiff' },
+    { mimetype: 'image/webp', extension: '.webp' },
+    { mimetype: 'image/x-adobe-dng', extension: '.dng' },
+    { mimetype: 'image/x-arriflex-ari', extension: '.ari' },
+    { mimetype: 'image/x-canon-cr2', extension: '.cr2' },
+    { mimetype: 'image/x-canon-cr3', extension: '.cr3' },
+    { mimetype: 'image/x-canon-crw', extension: '.crw' },
+    { mimetype: 'image/x-epson-erf', extension: '.erf' },
+    { mimetype: 'image/x-fuji-raf', extension: '.raf' },
+    { mimetype: 'image/x-hasselblad-3fr', extension: '.3fr' },
+    { mimetype: 'image/x-hasselblad-fff', extension: '.fff' },
+    { mimetype: 'image/x-kodak-dcr', extension: '.dcr' },
+    { mimetype: 'image/x-kodak-k25', extension: '.k25' },
+    { mimetype: 'image/x-kodak-kdc', extension: '.kdc' },
+    { mimetype: 'image/x-leica-rwl', extension: '.rwl' },
+    { mimetype: 'image/x-minolta-mrw', extension: '.mrw' },
+    { mimetype: 'image/x-nikon-nef', extension: '.nef' },
+    { mimetype: 'image/x-olympus-orf', extension: '.orf' },
+    { mimetype: 'image/x-olympus-ori', extension: '.ori' },
+    { mimetype: 'image/x-panasonic-raw', extension: '.raw' },
+    { mimetype: 'image/x-pentax-pef', extension: '.pef' },
+    { mimetype: 'image/x-phantom-cin', extension: '.cin' },
+    { mimetype: 'image/x-phaseone-cap', extension: '.cap' },
+    { mimetype: 'image/x-phaseone-iiq', extension: '.iiq' },
+    { mimetype: 'image/x-samsung-srw', extension: '.srw' },
+    { mimetype: 'image/x-sigma-x3f', extension: '.x3f' },
+    { mimetype: 'image/x-sony-arw', extension: '.arw' },
+    { mimetype: 'image/x-sony-sr2', extension: '.sr2' },
+    { mimetype: 'image/x-sony-srf', extension: '.srf' },
+    { mimetype: 'image/x3f', extension: '.x3f' },
+    { mimetype: 'video/3gpp', extension: '.3gp' },
+    { mimetype: 'video/avi', extension: '.avi' },
+    { mimetype: 'video/mp2t', extension: '.m2ts' },
+    { mimetype: 'video/mp2t', extension: '.mts' },
+    { mimetype: 'video/mp4', extension: '.mp4' },
+    { mimetype: 'video/mpeg', extension: '.mpg' },
+    { mimetype: 'video/msvideo', extension: '.avi' },
+    { mimetype: 'video/quicktime', extension: '.mov' },
+    { mimetype: 'video/vnd.avi', extension: '.avi' },
+    { mimetype: 'video/webm', extension: '.webm' },
+    { mimetype: 'video/x-flv', extension: '.flv' },
+    { mimetype: 'video/x-matroska', extension: '.mkv' },
+    { mimetype: 'video/x-ms-wmv', extension: '.wmv' },
+    { mimetype: 'video/x-msvideo', extension: '.avi' },
+  ]) {
+    it(`should map ${extension} to ${mimetype}`, async () => {
+      expect({ ...mimeTypes.image, ...mimeTypes.video }[extension]).toContain(mimetype);
+    });
+  }
+
+  describe('profile', () => {
+    it('should contain only lowercase mime types', () => {
+      const keys = Object.keys(mimeTypes.profile);
+      expect(keys).toEqual(keys.map((mimeType) => mimeType.toLowerCase()));
+
+      const values = Object.values(mimeTypes.profile).flat();
+      expect(values).toEqual(values.map((mimeType) => mimeType.toLowerCase()));
+    });
+
+    it('should be a sorted list', () => {
+      const keys = Object.keys(mimeTypes.profile);
+      // TODO: use toSorted in NodeJS 20.
+      expect(keys).toEqual([...keys].sort());
+    });
+
+    for (const [ext, v] of Object.entries(mimeTypes.profile)) {
+      it(`should lookup ${ext}`, () => {
+        expect(mimeTypes.lookup(`test.${ext}`)).toEqual(v[0]);
+      });
+    }
+  });
+
+  describe('image', () => {
+    it('should contain only lowercase mime types', () => {
+      const keys = Object.keys(mimeTypes.image);
+      expect(keys).toEqual(keys.map((mimeType) => mimeType.toLowerCase()));
+
+      const values = Object.values(mimeTypes.image).flat();
+      expect(values).toEqual(values.map((mimeType) => mimeType.toLowerCase()));
+    });
+
+    it('should be a sorted list', () => {
+      const keys = Object.keys(mimeTypes.image);
+      // TODO: use toSorted in NodeJS 20.
+      expect(keys).toEqual([...keys].sort());
+    });
+
+    it('should contain only image mime types', () => {
+      const values = Object.values(mimeTypes.image).flat();
+      expect(values).toEqual(values.filter((mimeType) => mimeType.startsWith('image/')));
+    });
+
+    for (const [ext, v] of Object.entries(mimeTypes.image)) {
+      it(`should lookup ${ext}`, () => {
+        expect(mimeTypes.lookup(`test.${ext}`)).toEqual(v[0]);
+      });
+    }
+  });
+
+  describe('video', () => {
+    it('should contain only lowercase mime types', () => {
+      const keys = Object.keys(mimeTypes.video);
+      expect(keys).toEqual(keys.map((mimeType) => mimeType.toLowerCase()));
+
+      const values = Object.values(mimeTypes.video).flat();
+      expect(values).toEqual(values.map((mimeType) => mimeType.toLowerCase()));
+    });
+
+    it('should be a sorted list', () => {
+      const keys = Object.keys(mimeTypes.video);
+      // TODO: use toSorted in NodeJS 20.
+      expect(keys).toEqual([...keys].sort());
+    });
+
+    it('should contain only video mime types', () => {
+      const values = Object.values(mimeTypes.video).flat();
+      expect(values).toEqual(values.filter((mimeType) => mimeType.startsWith('video/')));
+    });
+
+    for (const [ext, v] of Object.entries(mimeTypes.video)) {
+      it(`should lookup ${ext}`, () => {
+        expect(mimeTypes.lookup(`test.${ext}`)).toEqual(v[0]);
+      });
+    }
+  });
+
+  describe('sidecar', () => {
+    it('should contain only lowercase mime types', () => {
+      const keys = Object.keys(mimeTypes.sidecar);
+      expect(keys).toEqual(keys.map((mimeType) => mimeType.toLowerCase()));
+
+      const values = Object.values(mimeTypes.sidecar).flat();
+      expect(values).toEqual(values.map((mimeType) => mimeType.toLowerCase()));
+    });
+
+    it('should be a sorted list', () => {
+      const keys = Object.keys(mimeTypes.sidecar);
+      // TODO: use toSorted in NodeJS 20.
+      expect(keys).toEqual([...keys].sort());
+    });
+
+    it('should contain only xml mime types', () => {
+      expect(Object.values(mimeTypes.sidecar).flat()).toEqual(['application/xml', 'text/xml']);
+    });
+
+    for (const [ext, v] of Object.entries(mimeTypes.sidecar)) {
+      it(`should lookup ${ext}`, () => {
+        expect(mimeTypes.lookup(`test.${ext}`)).toEqual(v[0]);
+      });
+    }
+  });
+});

+ 65 - 64
server/src/domain/domain.constant.ts

@@ -30,70 +30,73 @@ export function assertMachineLearningEnabled() {
   }
   }
 }
 }
 
 
-const profile: Record<string, string> = {
-  '.avif': 'image/avif',
-  '.dng': 'image/x-adobe-dng',
-  '.heic': 'image/heic',
-  '.heif': 'image/heif',
-  '.jpeg': 'image/jpeg',
-  '.jpg': 'image/jpeg',
-  '.png': 'image/png',
-  '.webp': 'image/webp',
+const image: Record<string, string[]> = {
+  '.3fr': ['image/3fr', 'image/x-hasselblad-3fr'],
+  '.ari': ['image/ari', 'image/x-arriflex-ari'],
+  '.arw': ['image/arw', 'image/x-sony-arw'],
+  '.avif': ['image/avif'],
+  '.cap': ['image/cap', 'image/x-phaseone-cap'],
+  '.cin': ['image/cin', 'image/x-phantom-cin'],
+  '.cr2': ['image/cr2', 'image/x-canon-cr2'],
+  '.cr3': ['image/cr3', 'image/x-canon-cr3'],
+  '.crw': ['image/crw', 'image/x-canon-crw'],
+  '.dcr': ['image/dcr', 'image/x-kodak-dcr'],
+  '.dng': ['image/dng', 'image/x-adobe-dng'],
+  '.erf': ['image/erf', 'image/x-epson-erf'],
+  '.fff': ['image/fff', 'image/x-hasselblad-fff'],
+  '.gif': ['image/gif'],
+  '.heic': ['image/heic'],
+  '.heif': ['image/heif'],
+  '.iiq': ['image/iiq', 'image/x-phaseone-iiq'],
+  '.jpeg': ['image/jpeg'],
+  '.jpg': ['image/jpeg'],
+  '.jxl': ['image/jxl'],
+  '.k25': ['image/k25', 'image/x-kodak-k25'],
+  '.kdc': ['image/kdc', 'image/x-kodak-kdc'],
+  '.mrw': ['image/mrw', 'image/x-minolta-mrw'],
+  '.nef': ['image/nef', 'image/x-nikon-nef'],
+  '.orf': ['image/orf', 'image/x-olympus-orf'],
+  '.ori': ['image/ori', 'image/x-olympus-ori'],
+  '.pef': ['image/pef', 'image/x-pentax-pef'],
+  '.png': ['image/png'],
+  '.raf': ['image/raf', 'image/x-fuji-raf'],
+  '.raw': ['image/raw', 'image/x-panasonic-raw'],
+  '.rwl': ['image/rwl', 'image/x-leica-rwl'],
+  '.sr2': ['image/sr2', 'image/x-sony-sr2'],
+  '.srf': ['image/srf', 'image/x-sony-srf'],
+  '.srw': ['image/srw', 'image/x-samsung-srw'],
+  '.tiff': ['image/tiff'],
+  '.webp': ['image/webp'],
+  '.x3f': ['image/x3f', 'image/x-sigma-x3f'],
 };
 };
 
 
-const image: Record<string, string> = {
-  ...profile,
-  '.3fr': 'image/x-hasselblad-3fr',
-  '.ari': 'image/x-arriflex-ari',
-  '.arw': 'image/x-sony-arw',
-  '.cap': 'image/x-phaseone-cap',
-  '.cin': 'image/x-phantom-cin',
-  '.cr2': 'image/x-canon-cr2',
-  '.cr3': 'image/x-canon-cr3',
-  '.crw': 'image/x-canon-crw',
-  '.dcr': 'image/x-kodak-dcr',
-  '.erf': 'image/x-epson-erf',
-  '.fff': 'image/x-hasselblad-fff',
-  '.gif': 'image/gif',
-  '.iiq': 'image/x-phaseone-iiq',
-  '.k25': 'image/x-kodak-k25',
-  '.kdc': 'image/x-kodak-kdc',
-  '.mrw': 'image/x-minolta-mrw',
-  '.nef': 'image/x-nikon-nef',
-  '.orf': 'image/x-olympus-orf',
-  '.ori': 'image/x-olympus-ori',
-  '.pef': 'image/x-pentax-pef',
-  '.raf': 'image/x-fuji-raf',
-  '.raw': 'image/x-panasonic-raw',
-  '.rwl': 'image/x-leica-rwl',
-  '.sr2': 'image/x-sony-sr2',
-  '.srf': 'image/x-sony-srf',
-  '.srw': 'image/x-samsung-srw',
-  '.tiff': 'image/tiff',
-  '.x3f': 'image/x-sigma-x3f',
-};
+const profileExtensions = ['.avif', '.dng', '.heic', '.heif', '.jpeg', '.jpg', '.png', '.webp'];
+const profile: Record<string, string[]> = Object.fromEntries(
+  Object.entries(image).filter(([key]) => profileExtensions.includes(key)),
+);
 
 
-const video: Record<string, string> = {
-  '.3gp': 'video/3gpp',
-  '.avi': 'video/x-msvideo',
-  '.flv': 'video/x-flv',
-  '.mkv': 'video/x-matroska',
-  '.mov': 'video/quicktime',
-  '.mp2t': 'video/mp2t',
-  '.mp4': 'video/mp4',
-  '.mpeg': 'video/mpeg',
-  '.webm': 'video/webm',
-  '.wmv': 'video/x-ms-wmv',
+const video: Record<string, string[]> = {
+  '.3gp': ['video/3gpp'],
+  '.avi': ['video/avi', 'video/msvideo', 'video/vnd.avi', 'video/x-msvideo'],
+  '.flv': ['video/x-flv'],
+  '.m2ts': ['video/mp2t'],
+  '.mkv': ['video/x-matroska'],
+  '.mov': ['video/quicktime'],
+  '.mp4': ['video/mp4'],
+  '.mpg': ['video/mpeg'],
+  '.mts': ['video/mp2t'],
+  '.webm': ['video/webm'],
+  '.wmv': ['video/x-ms-wmv'],
 };
 };
 
 
-const sidecar: Record<string, string> = {
-  '.xmp': 'application/xml',
+const sidecar: Record<string, string[]> = {
+  '.xmp': ['application/xml', 'text/xml'],
 };
 };
 
 
-const isType = (filename: string, lookup: Record<string, string>) => !!lookup[extname(filename).toLowerCase()];
-const getType = (filename: string, lookup: Record<string, string>) => lookup[extname(filename).toLowerCase()];
+const isType = (filename: string, r: Record<string, string[]>) => extname(filename).toLowerCase() in r;
+
 const lookup = (filename: string) =>
 const lookup = (filename: string) =>
-  getType(filename, { ...image, ...video, ...sidecar }) || 'application/octet-stream';
+  ({ ...image, ...video, ...sidecar }[extname(filename).toLowerCase()]?.[0] ?? 'application/octet-stream');
 
 
 export const mimeTypes = {
 export const mimeTypes = {
   image,
   image,
@@ -107,14 +110,12 @@ export const mimeTypes = {
   isVideo: (filename: string) => isType(filename, video),
   isVideo: (filename: string) => isType(filename, video),
   lookup,
   lookup,
   assetType: (filename: string) => {
   assetType: (filename: string) => {
-    const contentType = lookup(filename).split('/')[0];
-    switch (contentType) {
-      case 'image':
-        return AssetType.IMAGE;
-      case 'video':
-        return AssetType.VIDEO;
-      default:
-        return AssetType.OTHER;
+    const contentType = lookup(filename);
+    if (contentType.startsWith('image/')) {
+      return AssetType.IMAGE;
+    } else if (contentType.startsWith('video/')) {
+      return AssetType.VIDEO;
     }
     }
+    return AssetType.OTHER;
   },
   },
 };
 };

+ 1 - 79
server/src/immich/api-v1/asset/asset.service.spec.ts

@@ -1,4 +1,4 @@
-import { ICryptoRepository, IJobRepository, IStorageRepository, JobName, mimeTypes } from '@app/domain';
+import { ICryptoRepository, IJobRepository, IStorageRepository, JobName } from '@app/domain';
 import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
 import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
 import { BadRequestException } from '@nestjs/common';
 import { BadRequestException } from '@nestjs/common';
 import {
 import {
@@ -139,84 +139,6 @@ describe('AssetService', () => {
       .mockResolvedValue(assetEntityStub.livePhotoMotionAsset);
       .mockResolvedValue(assetEntityStub.livePhotoMotionAsset);
   });
   });
 
 
-  describe('mime types linting', () => {
-    describe('profile', () => {
-      it('should contain only lowercase mime types', () => {
-        const keys = Object.keys(mimeTypes.profile);
-        expect(keys).toEqual(keys.map((mimeType) => mimeType.toLowerCase()));
-        const values = Object.values(mimeTypes.profile);
-        expect(values).toEqual(values.map((mimeType) => mimeType.toLowerCase()));
-      });
-
-      it('should be a sorted list', () => {
-        const keys = Object.keys(mimeTypes.profile);
-        expect(keys).toEqual([...keys].sort());
-      });
-    });
-
-    describe('image', () => {
-      it('should contain only lowercase mime types', () => {
-        const keys = Object.keys(mimeTypes.image);
-        expect(keys).toEqual(keys.map((mimeType) => mimeType.toLowerCase()));
-        const values = Object.values(mimeTypes.image);
-        expect(values).toEqual(values.map((mimeType) => mimeType.toLowerCase()));
-      });
-
-      it('should be a sorted list', () => {
-        const keys = Object.keys(mimeTypes.image).filter((key) => key in mimeTypes.profile === false);
-        expect(keys).toEqual([...keys].sort());
-      });
-
-      it('should contain only image mime types', () => {
-        expect(Object.values(mimeTypes.image)).toEqual(
-          Object.values(mimeTypes.image).filter((mimeType) => mimeType.startsWith('image/')),
-        );
-      });
-    });
-
-    describe('video', () => {
-      it('should contain only lowercase mime types', () => {
-        const keys = Object.keys(mimeTypes.video);
-        expect(keys).toEqual(keys.map((mimeType) => mimeType.toLowerCase()));
-        const values = Object.values(mimeTypes.video);
-        expect(values).toEqual(values.map((mimeType) => mimeType.toLowerCase()));
-      });
-
-      it('should be a sorted list', () => {
-        const keys = Object.keys(mimeTypes.video);
-        expect(keys).toEqual([...keys].sort());
-      });
-
-      it('should contain only video mime types', () => {
-        expect(Object.values(mimeTypes.video)).toEqual(
-          Object.values(mimeTypes.video).filter((mimeType) => mimeType.startsWith('video/')),
-        );
-      });
-    });
-
-    describe('sidecar', () => {
-      it('should contain only lowercase mime types', () => {
-        const keys = Object.keys(mimeTypes.sidecar);
-        expect(keys).toEqual(keys.map((mimeType) => mimeType.toLowerCase()));
-        const values = Object.values(mimeTypes.sidecar);
-        expect(values).toEqual(values.map((mimeType) => mimeType.toLowerCase()));
-      });
-
-      it('should be a sorted list', () => {
-        const keys = Object.keys(mimeTypes.sidecar);
-        expect(keys).toEqual([...keys].sort());
-      });
-    });
-
-    describe('sidecar', () => {
-      it('should contain only be xml mime type', () => {
-        expect(Object.values(mimeTypes.sidecar)).toEqual(
-          Object.values(mimeTypes.sidecar).filter((mimeType) => mimeType === 'application/xml'),
-        );
-      });
-    });
-  });
-
   describe('uploadFile', () => {
   describe('uploadFile', () => {
     it('should handle a file upload', async () => {
     it('should handle a file upload', async () => {
       const assetEntity = _getAsset_1();
       const assetEntity = _getAsset_1();