Просмотр исходного кода

fix: improve image content type validation and test cases for clarity

Yuki Takei 11 месяцев назад
Родитель
Сommit
f62df41dc0

+ 53 - 59
apps/app/src/server/routes/attachment/image-content-type-validator.spec.ts

@@ -2,70 +2,64 @@ import { describe, test, expect } from 'vitest';
 
 import { validateImageContentType, type SupportedImageMimeType } from './image-content-type-validator';
 
-describe('imageContentTypeValidator', () => {
-  describe('validateImageContentType', () => {
-    describe('valid cases', () => {
-      test.each([
-        ['single MIME type', 'image/png'],
-        ['MIME type with whitespace', 'image/png '],
-        ['multiple MIME types (last is valid)', 'text/html, image/jpeg'],
-        ['multiple MIME types with whitespace', 'image/png , image/jpeg'],
-      ])('should accept %s', (_, input) => {
-        const result = validateImageContentType(input);
-        expect(result.isValid).toBe(true);
-        expect(result.error).toBeUndefined();
-      });
+describe('validateImageContentType', () => {
+  describe('valid cases', () => {
+    // Test supported MIME types
+    const supportedTypes: SupportedImageMimeType[] = [
+      'image/png',
+      'image/jpeg',
+      'image/jpg',
+      'image/gif',
+      'image/webp',
+      'image/avif',
+      'image/heic',
+      'image/heif',
+      'image/tiff',
+      'image/svg+xml',
+    ];
 
-      describe('should accept all supported image types', () => {
-        const supportedTypes: SupportedImageMimeType[] = [
-          'image/png',
-          'image/jpeg',
-          'image/jpg',
-          'image/gif',
-          'image/webp',
-          'image/avif',
-          'image/heic',
-          'image/heif',
-          'image/tiff',
-          'image/svg+xml',
-        ];
+    test.each(supportedTypes)('should accept %s', (mimeType) => {
+      const result = validateImageContentType(mimeType);
+      expect(result.isValid).toBe(true);
+      expect(result.contentType).toBe(mimeType);
+      expect(result.error).toBeUndefined();
+    });
 
-        test.each(supportedTypes)('%s', (mimeType) => {
-          const result = validateImageContentType(mimeType);
-          expect(result.isValid).toBe(true);
-          expect(result.contentType).toBe(mimeType);
-          expect(result.error).toBeUndefined();
-        });
-      });
+    test('should accept MIME type with surrounding whitespace', () => {
+      const result = validateImageContentType('  image/png  ');
+      expect(result.isValid).toBe(true);
+      expect(result.contentType).toBe('image/png');
+      expect(result.error).toBeUndefined();
     });
+  });
 
-    describe('invalid cases', () => {
-      describe('invalid MIME types', () => {
-        test.each([
-          ['non-image MIME type', 'application/json'],
-          ['multiple MIME types (last is invalid)', 'image/png, text/html'],
-          ['empty string', ''],
-          ['unknown image type', 'image/unknown'],
-        ])('should reject %s', (_, input) => {
-          const result = validateImageContentType(input);
-          expect(result.isValid).toBe(false);
-          expect(result.error).toContain('Invalid file type');
-        });
-      });
+  describe('invalid cases', () => {
+    // Test invalid input types
+    test.each([
+      ['undefined', undefined],
+      ['null', null],
+      ['number', 42],
+      ['object', {}],
+    ])('should reject %s', (_, input) => {
+      const result = validateImageContentType(input as unknown as string);
+      expect(result.isValid).toBe(false);
+      expect(result.contentType).toBeNull();
+      expect(result.error).toBe('Invalid MIME type format');
+    });
 
-      describe('invalid input types', () => {
-        test.each([
-          ['undefined', undefined],
-          ['null', null],
-          ['number', 42],
-          ['object', {}],
-        ])('should reject %s', (_, input) => {
-          const result = validateImageContentType(input as unknown as string);
-          expect(result.isValid).toBe(false);
-          expect(result.contentType).toBeNull();
-          expect(result.error).toBe('Invalid MIME type format');
-        });
-      });
+    // Test invalid MIME types
+    test.each([
+      ['empty string', ''],
+      ['whitespace only', '   '],
+      ['non-image type', 'text/plain'],
+      ['unknown image type', 'image/unknown'],
+      ['multiple MIME types', 'text/plain, image/png'],
+      ['multiple image types', 'image/png, image/jpeg'],
+      ['MIME type with comma', 'image/png,'],
+    ])('should reject %s', (_, input) => {
+      const result = validateImageContentType(input);
+      expect(result.isValid).toBe(false);
+      expect(result.error).toContain('Invalid file type');
     });
   });
 });

+ 5 - 10
apps/app/src/server/routes/attachment/image-content-type-validator.ts

@@ -25,7 +25,7 @@ export interface ImageContentTypeValidatorResult {
 
 /**
  * Validate and extract content type from MIME type string
- * @param mimeType MIME type string, possibly containing multiple values
+ * @param mimeType MIME type string
  * @returns Validation result containing isValid flag and extracted content type
  */
 export const validateImageContentType = (mimeType: string): ImageContentTypeValidatorResult => {
@@ -37,25 +37,20 @@ export const validateImageContentType = (mimeType: string): ImageContentTypeVali
     };
   }
 
-  // Extract the last content type from comma-separated values
-  const contentType = mimeType.split(',')
-    .map(type => type.trim())
-    .pop() ?? '';
-
-  // Check if the content type is in supported list
-  const isValid = SUPPORTED_IMAGE_MIME_TYPES.includes(contentType as SupportedImageMimeType);
+  const trimmedType = mimeType.trim();
+  const isValid = SUPPORTED_IMAGE_MIME_TYPES.includes(trimmedType as SupportedImageMimeType);
 
   if (!isValid) {
     const supportedFormats = 'PNG, JPEG, GIF, WebP, AVIF, HEIC/HEIF, TIFF, SVG';
     return {
       isValid: false,
-      contentType,
+      contentType: trimmedType,
       error: `Invalid file type. Supported formats: ${supportedFormats}`,
     };
   }
 
   return {
     isValid: true,
-    contentType,
+    contentType: trimmedType,
   };
 };