mirror of
https://github.com/immich-app/immich.git
synced 2025-04-17 21:46:25 +02:00
fix(server): searchRandom
response (#15580)
* fix searchRandom * add e2e * set outer limit
This commit is contained in:
parent
065d885ca0
commit
ba105d9f19
5 changed files with 60 additions and 9 deletions
e2e/src
server/src
|
@ -3,11 +3,11 @@ import {
|
||||||
AssetMediaStatus,
|
AssetMediaStatus,
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
AssetTypeEnum,
|
AssetTypeEnum,
|
||||||
LoginResponseDto,
|
|
||||||
SharedLinkType,
|
|
||||||
getAssetInfo,
|
getAssetInfo,
|
||||||
getConfig,
|
getConfig,
|
||||||
getMyUser,
|
getMyUser,
|
||||||
|
LoginResponseDto,
|
||||||
|
SharedLinkType,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { exiftool } from 'exiftool-vendored';
|
import { exiftool } from 'exiftool-vendored';
|
||||||
|
@ -19,7 +19,7 @@ import { Socket } from 'socket.io-client';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { makeRandomImage } from 'src/generators';
|
import { makeRandomImage } from 'src/generators';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, asBearerAuth, tempDir, testAssetDir, utils } from 'src/utils';
|
import { app, asBearerAuth, tempDir, TEN_TIMES, testAssetDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
@ -41,8 +41,6 @@ const makeUploadDto = (options?: { omit: string }): Record<string, any> => {
|
||||||
return dto;
|
return dto;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TEN_TIMES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
||||||
|
|
||||||
const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
||||||
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
||||||
const facesAssetFilepath = `${testAssetDir}/metadata/faces/portrait.jpg`;
|
const facesAssetFilepath = `${testAssetDir}/metadata/faces/portrait.jpg`;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { AssetMediaResponseDto, LoginResponseDto, deleteAssets, updateAsset } from '@immich/sdk';
|
import { AssetMediaResponseDto, AssetResponseDto, deleteAssets, LoginResponseDto, updateAsset } from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, asBearerAuth, testAssetDir, utils } from 'src/utils';
|
import { app, asBearerAuth, TEN_TIMES, testAssetDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||||
const today = DateTime.now();
|
const today = DateTime.now();
|
||||||
|
@ -462,6 +462,55 @@ describe('/search', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('POST /search/random', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await Promise.all([
|
||||||
|
utils.createAsset(admin.accessToken),
|
||||||
|
utils.createAsset(admin.accessToken),
|
||||||
|
utils.createAsset(admin.accessToken),
|
||||||
|
utils.createAsset(admin.accessToken),
|
||||||
|
utils.createAsset(admin.accessToken),
|
||||||
|
utils.createAsset(admin.accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).post('/search/random').send({ size: 1 });
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/search/random')
|
||||||
|
.send({ size: 1 })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
|
||||||
|
const assets: AssetResponseDto[] = body;
|
||||||
|
expect(assets.length).toBe(1);
|
||||||
|
expect(assets[0].ownerId).toBe(admin.userId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(TEN_TIMES)('should return 2 random assets', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post('/search/random')
|
||||||
|
.send({ size: 2 })
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
|
||||||
|
const assets: AssetResponseDto[] = body;
|
||||||
|
expect(assets.length).toBe(2);
|
||||||
|
expect(assets[0].ownerId).toBe(admin.userId);
|
||||||
|
expect(assets[1].ownerId).toBe(admin.userId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('GET /search/explore', () => {
|
describe('GET /search/explore', () => {
|
||||||
it('should require authentication', async () => {
|
it('should require authentication', async () => {
|
||||||
const { status, body } = await request(app).get('/search/explore');
|
const { status, body } = await request(app).get('/search/explore');
|
||||||
|
|
|
@ -76,6 +76,7 @@ export const immichCli = (args: string[]) =>
|
||||||
export const immichAdmin = (args: string[]) =>
|
export const immichAdmin = (args: string[]) =>
|
||||||
executeCommand('docker', ['exec', '-i', 'immich-e2e-server', '/bin/bash', '-c', `immich-admin ${args.join(' ')}`]);
|
executeCommand('docker', ['exec', '-i', 'immich-e2e-server', '/bin/bash', '-c', `immich-admin ${args.join(' ')}`]);
|
||||||
export const specialCharStrings = ["'", '"', ',', '{', '}', '*'];
|
export const specialCharStrings = ["'", '"', ',', '{', '}', '*'];
|
||||||
|
export const TEN_TIMES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
|
|
||||||
const executeCommand = (command: string, args: string[]) => {
|
const executeCommand = (command: string, args: string[]) => {
|
||||||
let _resolve: (value: CommandResponse) => void;
|
let _resolve: (value: CommandResponse) => void;
|
||||||
|
|
|
@ -60,6 +60,8 @@ union all
|
||||||
limit
|
limit
|
||||||
$14
|
$14
|
||||||
)
|
)
|
||||||
|
limit
|
||||||
|
$15
|
||||||
|
|
||||||
-- SearchRepository.searchSmart
|
-- SearchRepository.searchSmart
|
||||||
select
|
select
|
||||||
|
|
|
@ -69,12 +69,13 @@ export class SearchRepository implements ISearchRepository {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
searchRandom(size: number, options: AssetSearchOptions): Promise<AssetEntity[]> {
|
async searchRandom(size: number, options: AssetSearchOptions): Promise<AssetEntity[]> {
|
||||||
const uuid = randomUUID();
|
const uuid = randomUUID();
|
||||||
const builder = searchAssetBuilder(this.db, options);
|
const builder = searchAssetBuilder(this.db, options);
|
||||||
const lessThan = builder.where('assets.id', '<', uuid).orderBy('assets.id').limit(size);
|
const lessThan = builder.where('assets.id', '<', uuid).orderBy('assets.id').limit(size);
|
||||||
const greaterThan = builder.where('assets.id', '>', uuid).orderBy('assets.id').limit(size);
|
const greaterThan = builder.where('assets.id', '>', uuid).orderBy('assets.id').limit(size);
|
||||||
return sql`${lessThan} union all ${greaterThan}`.execute(this.db) as any as Promise<AssetEntity[]>;
|
const { rows } = await sql`${lessThan} union all ${greaterThan} limit ${size}`.execute(this.db);
|
||||||
|
return rows as any as AssetEntity[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({
|
@GenerateSql({
|
||||||
|
|
Loading…
Add table
Reference in a new issue