From 395c28f5fab702a16fbeab7ce23fd69ad2160141 Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Sat, 30 Mar 2024 22:29:02 -0400 Subject: [PATCH] fix(server): parameter for all places query (#8346) * fix parameter * add e2e --- e2e/src/api/specs/search.e2e-spec.ts | 136 ++++++++++++++++++++++++--- e2e/src/utils.ts | 4 +- 2 files changed, 125 insertions(+), 15 deletions(-) diff --git a/e2e/src/api/specs/search.e2e-spec.ts b/e2e/src/api/specs/search.e2e-spec.ts index 9c554abc56..afe131228a 100644 --- a/e2e/src/api/specs/search.e2e-spec.ts +++ b/e2e/src/api/specs/search.e2e-spec.ts @@ -1,4 +1,4 @@ -import { AssetFileUploadResponseDto, LoginResponseDto, deleteAssets } from '@immich/sdk'; +import { AssetFileUploadResponseDto, LoginResponseDto, deleteAssets, updateAsset } from '@immich/sdk'; import { DateTime } from 'luxon'; import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; @@ -7,7 +7,6 @@ import { errorDto } from 'src/responses'; import { app, asBearerAuth, testAssetDir, utils } from 'src/utils'; import request from 'supertest'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; - const today = DateTime.now(); describe('/search', () => { @@ -19,7 +18,7 @@ describe('/search', () => { let assetCyclamen: AssetFileUploadResponseDto; let assetNotocactus: AssetFileUploadResponseDto; let assetSilver: AssetFileUploadResponseDto; - // let assetDensity: AssetFileUploadResponseDto; + let assetDensity: AssetFileUploadResponseDto; // let assetPhiladelphia: AssetFileUploadResponseDto; // let assetOrychophragmus: AssetFileUploadResponseDto; // let assetRidge: AssetFileUploadResponseDto; @@ -79,6 +78,37 @@ describe('/search', () => { await utils.waitForWebsocketEvent({ event: 'assetUpload', id: asset.id }); } + // note: the coordinates here are not the actual coordinates of the images and are random for most of them + const cities = [ + { latitude: 48.853_41, longitude: 2.3488 }, // paris + { latitude: 63.0695, longitude: -151.0074 }, // denali + { latitude: 52.524_37, longitude: 13.410_53 }, // berlin + { latitude: 1.314_663_1, longitude: 103.845_409_3 }, // singapore + { latitude: 41.013_84, longitude: 28.949_66 }, // istanbul + { latitude: 5.556_02, longitude: -0.1969 }, // accra + { latitude: 37.544_270_6, longitude: -4.727_752_8 }, // andalusia + { latitude: 23.133_02, longitude: -82.383_04 }, // havana + { latitude: 41.694_11, longitude: 44.833_68 }, // tbilisi + { latitude: 31.222_22, longitude: 121.458_06 }, // shanghai + { latitude: 47.040_57, longitude: 9.068_04 }, // glarus + { latitude: 38.9711, longitude: -109.7137 }, // thompson springs + { latitude: 40.714_27, longitude: -74.005_97 }, // new york + { latitude: 32.771_52, longitude: -89.116_73 }, // philadelphia + { latitude: 31.634_16, longitude: -7.999_94 }, // marrakesh + { latitude: 38.523_735_4, longitude: -78.488_619_4 }, // tanners ridge + { latitude: 59.938_63, longitude: 30.314_13 }, // st. petersburg + { latitude: 35.6895, longitude: 139.691_71 }, // tokyo + ]; + + const updates = assets.map((asset, i) => + updateAsset({ id: asset.id, updateAssetDto: cities[i] }, { headers: asBearerAuth(admin.accessToken) }), + ); + + await Promise.all(updates); + for (const asset of assets) { + await utils.waitForWebsocketEvent({ event: 'assetUpdate', id: asset.id }); + } + [ assetFalcon, assetDenali, @@ -92,7 +122,7 @@ describe('/search', () => { assetOneJpg5, assetGlarus, assetSprings, - // assetDensity, + assetDensity, // assetPhiladelphia, // assetOrychophragmus, // assetRidge, @@ -106,7 +136,7 @@ describe('/search', () => { }); afterAll(async () => { - await utils.disconnectWebsocket(websocket); + utils.disconnectWebsocket(websocket); }); describe('POST /search/metadata', () => { @@ -298,15 +328,15 @@ describe('/search', () => { }, { should: 'should search by city', - deferred: () => ({ dto: { city: 'Ralston' }, assets: [assetHeic] }), + deferred: () => ({ dto: { city: 'Accra' }, assets: [assetHeic] }), }, { should: 'should search by state', - deferred: () => ({ dto: { state: 'Douglas County, Nebraska' }, assets: [assetHeic] }), + deferred: () => ({ dto: { state: 'New York' }, assets: [assetDensity] }), }, { should: 'should search by country', - deferred: () => ({ dto: { country: 'United States of America' }, assets: [assetHeic] }), + deferred: () => ({ dto: { country: 'France' }, assets: [assetFalcon] }), }, { should: 'should search by make', @@ -370,13 +400,44 @@ describe('/search', () => { expect(body).toEqual(errorDto.unauthorized); }); - it('should get places', async () => { + it('should get relevant places', async () => { + const name = 'Paris'; + const { status, body } = await request(app) - .get('/search/places?name=Paris') + .get(`/search/places?name=${name}`) .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); expect(Array.isArray(body)).toBe(true); - expect(body.length).toBeGreaterThan(10); + if (Array.isArray(body)) { + expect(body.length).toBeGreaterThan(10); + expect(body[0].name).toEqual(name); + expect(body[0].admin2name).toEqual(name); + } + }); + }); + + describe('GET /search/cities', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/search/cities'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should get all cities', async () => { + const { status, body } = await request(app) + .get('/search/cities') + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(Array.isArray(body)).toBe(true); + if (Array.isArray(body)) { + expect(body.length).toBeGreaterThan(10); + const assetsWithCity = body.filter((asset) => !!asset.exifInfo?.city); + expect(assetsWithCity.length).toEqual(body.length); + const cities = new Set(assetsWithCity.map((asset) => asset.exifInfo.city)); + expect(cities.size).toEqual(body.length); + } }); }); @@ -391,7 +452,21 @@ describe('/search', () => { const { status, body } = await request(app) .get('/search/suggestions?type=country') .set('Authorization', `Bearer ${admin.accessToken}`); - expect(body).toEqual(['United States of America']); + expect(body).toEqual([ + 'Cuba', + 'France', + 'Georgia', + 'Germany', + 'Ghana', + 'Japan', + 'Morocco', + "People's Republic of China", + 'Russian Federation', + 'Singapore', + 'Spain', + 'Switzerland', + 'United States of America', + ]); expect(status).toBe(200); }); @@ -399,7 +474,23 @@ describe('/search', () => { const { status, body } = await request(app) .get('/search/suggestions?type=state') .set('Authorization', `Bearer ${admin.accessToken}`); - expect(body).toEqual(['Douglas County, Nebraska', 'Mesa County, Colorado']); + expect(body).toEqual([ + 'Accra, Greater Accra', + 'Berlin', + 'Glarus, Glarus', + 'Havana', + 'Marrakech, Marrakesh-Safi', + 'Mesa County, Colorado', + 'Neshoba County, Mississippi', + 'New York', + 'Page County, Virginia', + 'Paris, Île-de-France', + 'Province of Córdoba, Andalusia', + 'Shanghai Municipality, Shanghai', + 'St.-Petersburg', + 'Tbilisi', + 'Tokyo', + ]); expect(status).toBe(200); }); @@ -407,7 +498,24 @@ describe('/search', () => { const { status, body } = await request(app) .get('/search/suggestions?type=city') .set('Authorization', `Bearer ${admin.accessToken}`); - expect(body).toEqual(['Palisade', 'Ralston']); + expect(body).toEqual([ + 'Accra', + 'Berlin', + 'Glarus', + 'Havana', + 'Marrakesh', + 'Montalbán de Córdoba', + 'New York City', + 'Palisade', + 'Paris', + 'Philadelphia', + 'Saint Petersburg', + 'Shanghai', + 'Singapore', + 'Stanley', + 'Tbilisi', + 'Tokyo', + ]); expect(status).toBe(200); }); diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 6b538129a0..d8302a9e31 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -39,7 +39,7 @@ import { makeRandomImage } from 'src/generators'; import request from 'supertest'; type CliResponse = { stdout: string; stderr: string; exitCode: number | null }; -type EventType = 'assetUpload' | 'assetDelete' | 'userDelete'; +type EventType = 'assetUpload' | 'assetUpdate' | 'assetDelete' | 'userDelete'; type WaitOptions = { event: EventType; id?: string; total?: number; timeout?: number }; type AdminSetupOptions = { onboarding?: boolean }; type AssetData = { bytes?: Buffer; filename: string }; @@ -82,6 +82,7 @@ let client: pg.Client | null = null; const events: Record> = { assetUpload: new Set(), + assetUpdate: new Set(), assetDelete: new Set(), userDelete: new Set(), }; @@ -185,6 +186,7 @@ export const utils = { websocket .on('connect', () => resolve(websocket)) .on('on_upload_success', (data: AssetResponseDto) => onEvent({ event: 'assetUpload', id: data.id })) + .on('on_asset_update', (data: AssetResponseDto) => onEvent({ event: 'assetUpdate', id: data.id })) .on('on_asset_delete', (assetId: string) => onEvent({ event: 'assetDelete', id: assetId })) .on('on_user_delete', (userId: string) => onEvent({ event: 'userDelete', id: userId })) .connect();