You've already forked immich
							
							
				mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 00:18:28 +02:00 
			
		
		
		
	refactor(e2e): use better dummy assets (#7536)
This commit is contained in:
		
							
								
								
									
										20
									
								
								e2e/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								e2e/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -15,6 +15,7 @@ | ||||
|         "@types/luxon": "^3.4.2", | ||||
|         "@types/node": "^20.11.17", | ||||
|         "@types/pg": "^8.11.0", | ||||
|         "@types/pngjs": "^6.0.4", | ||||
|         "@types/supertest": "^6.0.2", | ||||
|         "@typescript-eslint/eslint-plugin": "^7.1.0", | ||||
|         "@typescript-eslint/parser": "^7.1.0", | ||||
| @@ -26,6 +27,7 @@ | ||||
|         "exiftool-vendored": "^24.5.0", | ||||
|         "luxon": "^3.4.4", | ||||
|         "pg": "^8.11.3", | ||||
|         "pngjs": "^7.0.0", | ||||
|         "prettier": "^3.2.5", | ||||
|         "prettier-plugin-organize-imports": "^3.2.4", | ||||
|         "socket.io-client": "^4.7.4", | ||||
| @@ -1236,6 +1238,15 @@ | ||||
|         "node": ">=12" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/pngjs": { | ||||
|       "version": "6.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.4.tgz", | ||||
|       "integrity": "sha512-atAK9xLKOnxiuArxcHovmnOUUGBZOQ3f0vCf43FnoKs6XnqiambT1kkJWmdo71IR+BoXSh+CueeFR0GfH3dTlQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "@types/node": "*" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/semver": { | ||||
|       "version": "7.5.8", | ||||
|       "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", | ||||
| @@ -3897,6 +3908,15 @@ | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pngjs": { | ||||
|       "version": "7.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", | ||||
|       "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=14.19.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postcss": { | ||||
|       "version": "8.4.35", | ||||
|       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
|     "@types/luxon": "^3.4.2", | ||||
|     "@types/node": "^20.11.17", | ||||
|     "@types/pg": "^8.11.0", | ||||
|     "@types/pngjs": "^6.0.4", | ||||
|     "@types/supertest": "^6.0.2", | ||||
|     "@typescript-eslint/eslint-plugin": "^7.1.0", | ||||
|     "@typescript-eslint/parser": "^7.1.0", | ||||
| @@ -34,6 +35,7 @@ | ||||
|     "exiftool-vendored": "^24.5.0", | ||||
|     "luxon": "^3.4.4", | ||||
|     "pg": "^8.11.3", | ||||
|     "pngjs": "^7.0.0", | ||||
|     "prettier": "^3.2.5", | ||||
|     "prettier-plugin-organize-imports": "^3.2.4", | ||||
|     "socket.io-client": "^4.7.4", | ||||
|   | ||||
| @@ -256,7 +256,7 @@ describe('/album', () => { | ||||
|       expect(status).toBe(200); | ||||
|       expect(body).toEqual({ | ||||
|         ...user1Albums[0], | ||||
|         assets: [expect.objectContaining(user1Albums[0].assets[0])], | ||||
|         assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })], | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
| @@ -268,7 +268,7 @@ describe('/album', () => { | ||||
|       expect(status).toBe(200); | ||||
|       expect(body).toEqual({ | ||||
|         ...user2Albums[0], | ||||
|         assets: [expect.objectContaining(user2Albums[0].assets[0])], | ||||
|         assets: [expect.objectContaining({ id: user2Albums[0].assets[0].id })], | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
| @@ -280,7 +280,7 @@ describe('/album', () => { | ||||
|       expect(status).toBe(200); | ||||
|       expect(body).toEqual({ | ||||
|         ...user1Albums[0], | ||||
|         assets: [expect.objectContaining(user1Albums[0].assets[0])], | ||||
|         assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })], | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -59,30 +59,25 @@ describe('/asset', () => { | ||||
|     ]); | ||||
|  | ||||
|     // asset location | ||||
|     assetLocation = await apiUtils.createAsset( | ||||
|       admin.accessToken, | ||||
|       {}, | ||||
|       { | ||||
|     assetLocation = await apiUtils.createAsset(admin.accessToken, { | ||||
|       assetData: { | ||||
|         filename: 'thompson-springs.jpg', | ||||
|         bytes: await readFile(locationAssetFilepath), | ||||
|       }, | ||||
|     ); | ||||
|     }); | ||||
|  | ||||
|     await wsUtils.waitForEvent({ event: 'upload', assetId: assetLocation.id }); | ||||
|  | ||||
|     user1Assets = await Promise.all([ | ||||
|       apiUtils.createAsset(user1.accessToken), | ||||
|       apiUtils.createAsset(user1.accessToken), | ||||
|       apiUtils.createAsset( | ||||
|         user1.accessToken, | ||||
|         { | ||||
|           isFavorite: true, | ||||
|           isReadOnly: true, | ||||
|           fileCreatedAt: yesterday.toISO(), | ||||
|           fileModifiedAt: yesterday.toISO(), | ||||
|         }, | ||||
|         { filename: 'example.mp4' }, | ||||
|       ), | ||||
|       apiUtils.createAsset(user1.accessToken, { | ||||
|         isFavorite: true, | ||||
|         isReadOnly: true, | ||||
|         fileCreatedAt: yesterday.toISO(), | ||||
|         fileModifiedAt: yesterday.toISO(), | ||||
|         assetData: { filename: 'example.mp4' }, | ||||
|       }), | ||||
|       apiUtils.createAsset(user1.accessToken), | ||||
|       apiUtils.createAsset(user1.accessToken), | ||||
|     ]); | ||||
| @@ -98,14 +93,11 @@ describe('/asset', () => { | ||||
|       apiUtils.createAsset(userStats.accessToken), | ||||
|       apiUtils.createAsset(userStats.accessToken, { isFavorite: true }), | ||||
|       apiUtils.createAsset(userStats.accessToken, { isArchived: true }), | ||||
|       apiUtils.createAsset( | ||||
|         userStats.accessToken, | ||||
|         { | ||||
|           isArchived: true, | ||||
|           isFavorite: true, | ||||
|         }, | ||||
|         { filename: 'example.mp4' }, | ||||
|       ), | ||||
|       apiUtils.createAsset(userStats.accessToken, { | ||||
|         isArchived: true, | ||||
|         isFavorite: true, | ||||
|         assetData: { filename: 'example.mp4' }, | ||||
|       }), | ||||
|     ]); | ||||
|  | ||||
|     const person1 = await apiUtils.createPerson(user1.accessToken, { | ||||
| @@ -615,11 +607,9 @@ describe('/asset', () => { | ||||
|     for (const { input, expected } of tests) { | ||||
|       it(`should generate a thumbnail for ${input}`, async () => { | ||||
|         const filepath = join(testAssetDir, input); | ||||
|         const { id, duplicate } = await apiUtils.createAsset( | ||||
|           admin.accessToken, | ||||
|           {}, | ||||
|           { bytes: await readFile(filepath), filename: basename(filepath) }, | ||||
|         ); | ||||
|         const { id, duplicate } = await apiUtils.createAsset(admin.accessToken, { | ||||
|           assetData: { bytes: await readFile(filepath), filename: basename(filepath) }, | ||||
|         }); | ||||
|  | ||||
|         expect(duplicate).toBe(false); | ||||
|  | ||||
| @@ -635,14 +625,12 @@ describe('/asset', () => { | ||||
|  | ||||
|     it('should handle a duplicate', async () => { | ||||
|       const filepath = 'formats/jpeg/el_torcal_rocks.jpeg'; | ||||
|       const { duplicate } = await apiUtils.createAsset( | ||||
|         admin.accessToken, | ||||
|         {}, | ||||
|         { | ||||
|       const { duplicate } = await apiUtils.createAsset(admin.accessToken, { | ||||
|         assetData: { | ||||
|           bytes: await readFile(join(testAssetDir, filepath)), | ||||
|           filename: basename(filepath), | ||||
|         }, | ||||
|       ); | ||||
|       }); | ||||
|  | ||||
|       expect(duplicate).toBe(true); | ||||
|     }); | ||||
| @@ -669,14 +657,12 @@ describe('/asset', () => { | ||||
|  | ||||
|     for (const { filepath, checksum } of motionTests) { | ||||
|       it(`should extract motionphoto video from ${filepath}`, async () => { | ||||
|         const response = await apiUtils.createAsset( | ||||
|           admin.accessToken, | ||||
|           {}, | ||||
|           { | ||||
|         const response = await apiUtils.createAsset(admin.accessToken, { | ||||
|           assetData: { | ||||
|             bytes: await readFile(join(testAssetDir, filepath)), | ||||
|             filename: basename(filepath), | ||||
|           }, | ||||
|         ); | ||||
|         }); | ||||
|  | ||||
|         await wsUtils.waitForEvent({ event: 'upload', assetId: response.id }); | ||||
|  | ||||
|   | ||||
| @@ -54,7 +54,7 @@ describe('/download', () => { | ||||
|         .set('Authorization', `Bearer ${admin.accessToken}`); | ||||
|  | ||||
|       expect(response.status).toBe(200); | ||||
|       expect(response.headers['content-type']).toEqual('image/jpeg'); | ||||
|       expect(response.headers['content-type']).toEqual('image/png'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
							
								
								
									
										31
									
								
								e2e/src/generators.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								e2e/src/generators.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import { PNG } from 'pngjs'; | ||||
|  | ||||
| const createPNG = (r: number, g: number, b: number) => { | ||||
|   const image = new PNG({ width: 1, height: 1 }); | ||||
|   image.data[0] = r; | ||||
|   image.data[1] = g; | ||||
|   image.data[2] = b; | ||||
|   image.data[3] = 255; | ||||
|   return PNG.sync.write(image); | ||||
| }; | ||||
|  | ||||
| function* newPngFactory() { | ||||
|   for (let r = 0; r < 255; r++) { | ||||
|     for (let g = 0; g < 255; g++) { | ||||
|       for (let b = 0; b < 255; b++) { | ||||
|         yield createPNG(r, g, b); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| const pngFactory = newPngFactory(); | ||||
|  | ||||
| export const makeRandomImage = () => { | ||||
|   const { value } = pngFactory.next(); | ||||
|   if (!value) { | ||||
|     throw new Error('Ran out of random asset data'); | ||||
|   } | ||||
|  | ||||
|   return value; | ||||
| }; | ||||
| @@ -21,7 +21,6 @@ import { | ||||
| } from '@immich/sdk'; | ||||
| import { BrowserContext } from '@playwright/test'; | ||||
| import { exec, spawn } from 'node:child_process'; | ||||
| import { randomBytes } from 'node:crypto'; | ||||
| import { access } from 'node:fs/promises'; | ||||
| import { tmpdir } from 'node:os'; | ||||
| import path from 'node:path'; | ||||
| @@ -29,6 +28,7 @@ import { promisify } from 'node:util'; | ||||
| import pg from 'pg'; | ||||
| import { io, type Socket } from 'socket.io-client'; | ||||
| import { loginDto, signupDto } from 'src/fixtures'; | ||||
| import { makeRandomImage } from 'src/generators'; | ||||
| import request from 'supertest'; | ||||
|  | ||||
| const execPromise = promisify(exec); | ||||
| @@ -241,6 +241,8 @@ export const wsUtils = { | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| type AssetData = { bytes?: Buffer; filename: string }; | ||||
|  | ||||
| export const apiUtils = { | ||||
|   setup: () => { | ||||
|     defaults.baseUrl = app; | ||||
| @@ -269,11 +271,7 @@ export const apiUtils = { | ||||
|     createAlbum({ createAlbumDto: dto }, { headers: asBearerAuth(accessToken) }), | ||||
|   createAsset: async ( | ||||
|     accessToken: string, | ||||
|     dto?: Partial<Omit<CreateAssetDto, 'assetData'>>, | ||||
|     data?: { | ||||
|       bytes?: Buffer; | ||||
|       filename: string; | ||||
|     }, | ||||
|     dto?: Partial<Omit<CreateAssetDto, 'assetData'>> & { assetData?: AssetData }, | ||||
|   ) => { | ||||
|     const _dto = { | ||||
|       deviceAssetId: 'test-1', | ||||
| @@ -283,15 +281,12 @@ export const apiUtils = { | ||||
|       ...dto, | ||||
|     }; | ||||
|  | ||||
|     const _assetData = { | ||||
|       bytes: randomBytes(32), | ||||
|       filename: 'example.jpg', | ||||
|       ...data, | ||||
|     }; | ||||
|     const assetData = dto?.assetData?.bytes || makeRandomImage(); | ||||
|     const filename = dto?.assetData?.filename || 'example.png'; | ||||
|  | ||||
|     const builder = request(app) | ||||
|       .post(`/asset/upload`) | ||||
|       .attach('assetData', _assetData.bytes, _assetData.filename) | ||||
|       .attach('assetData', assetData, filename) | ||||
|       .set('Authorization', `Bearer ${accessToken}`); | ||||
|  | ||||
|     for (const [key, value] of Object.entries(_dto)) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user