2023-01-06 06:11:58 +08:00
|
|
|
import {SearchManager} from '../../../../../src/backend/model/database/SearchManager';
|
2021-07-07 03:37:19 +08:00
|
|
|
import {DBTestHelper} from '../../../DBTestHelper';
|
|
|
|
import {SearchQueryDTO, SearchQueryTypes, TextSearch} from '../../../../../src/common/entities/SearchQueryDTO';
|
2023-01-06 06:11:58 +08:00
|
|
|
import {IndexingManager} from '../../../../../src/backend/model/database/IndexingManager';
|
2021-07-07 03:37:19 +08:00
|
|
|
import {DirectoryBaseDTO, ParentDirectoryDTO, SubDirectoryDTO} from '../../../../../src/common/entities/DirectoryDTO';
|
2022-06-25 00:05:45 +08:00
|
|
|
import {TestHelper} from '../../../../TestHelper';
|
2021-07-07 03:37:19 +08:00
|
|
|
import {ObjectManagers} from '../../../../../src/backend/model/ObjectManagers';
|
2023-01-06 06:11:58 +08:00
|
|
|
import {GalleryManager} from '../../../../../src/backend/model/database/GalleryManager';
|
2021-07-07 03:37:19 +08:00
|
|
|
import {Connection} from 'typeorm';
|
|
|
|
import {PhotoDTO} from '../../../../../src/common/entities/PhotoDTO';
|
|
|
|
import {VideoDTO} from '../../../../../src/common/entities/VideoDTO';
|
|
|
|
import {FileDTO} from '../../../../../src/common/entities/FileDTO';
|
2023-08-26 03:24:05 +08:00
|
|
|
import {CoverManager} from '../../../../../src/backend/model/database/CoverManager';
|
2021-07-07 03:37:19 +08:00
|
|
|
import {Config} from '../../../../../src/common/config/private/Config';
|
2023-08-31 05:35:42 +08:00
|
|
|
import {SortByTypes} from '../../../../../src/common/entities/SortingMethods';
|
2021-07-07 03:37:19 +08:00
|
|
|
import {Utils} from '../../../../../src/common/Utils';
|
2023-01-06 06:11:58 +08:00
|
|
|
import {SQLConnection} from '../../../../../src/backend/model/database/SQLConnection';
|
|
|
|
import {DirectoryEntity} from '../../../../../src/backend/model/database/enitites/DirectoryEntity';
|
2023-08-31 05:35:42 +08:00
|
|
|
import {ClientSortingConfig} from '../../../../../src/common/config/public/ClientConfig';
|
2021-07-07 03:37:19 +08:00
|
|
|
|
2022-12-05 05:23:51 +08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
2021-07-07 03:37:19 +08:00
|
|
|
const deepEqualInAnyOrder = require('deep-equal-in-any-order');
|
2022-12-05 05:23:51 +08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
2021-07-07 03:37:19 +08:00
|
|
|
const chai = require('chai');
|
|
|
|
|
|
|
|
chai.use(deepEqualInAnyOrder);
|
|
|
|
const {expect} = chai;
|
|
|
|
|
|
|
|
// to help WebStorm to handle the test cases
|
|
|
|
declare let describe: any;
|
|
|
|
declare const after: any;
|
|
|
|
declare const before: any;
|
|
|
|
const tmpDescribe = describe;
|
|
|
|
describe = DBTestHelper.describe(); // fake it os IDE plays nicely (recognize the test)
|
|
|
|
|
|
|
|
|
|
|
|
class IndexingManagerTest extends IndexingManager {
|
|
|
|
|
|
|
|
public async saveToDB(scannedDirectory: ParentDirectoryDTO): Promise<void> {
|
|
|
|
return super.saveToDB(scannedDirectory);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SearchManagerTest extends SearchManager {
|
|
|
|
|
|
|
|
public flattenSameOfQueries(query: SearchQueryDTO): SearchQueryDTO {
|
|
|
|
return super.flattenSameOfQueries(query);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
class GalleryManagerTest extends GalleryManager {
|
|
|
|
|
2022-12-05 05:23:51 +08:00
|
|
|
public async getDirIdAndTime(connection: Connection, directoryName: string, directoryParent: string) {
|
|
|
|
return super.getDirIdAndTime(connection, directoryName, directoryParent);
|
2021-07-07 03:37:19 +08:00
|
|
|
}
|
|
|
|
|
2022-12-05 05:23:51 +08:00
|
|
|
public async getParentDirFromId(connection: Connection, dir: number): Promise<ParentDirectoryDTO> {
|
|
|
|
return super.getParentDirFromId(connection, dir);
|
2021-07-07 03:37:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-26 03:24:05 +08:00
|
|
|
describe('CoverManager', (sqlHelper: DBTestHelper) => {
|
2021-07-07 03:37:19 +08:00
|
|
|
describe = tmpDescribe;
|
|
|
|
/**
|
|
|
|
* dir
|
|
|
|
* |-> subDir
|
|
|
|
* |- pFaceLess
|
|
|
|
* |- v
|
|
|
|
* |- p
|
|
|
|
* |- p2
|
|
|
|
* |-> subDir2
|
|
|
|
* |- p4
|
|
|
|
*/
|
|
|
|
|
|
|
|
let dir: ParentDirectoryDTO;
|
|
|
|
let subDir: SubDirectoryDTO;
|
|
|
|
let subDir2: SubDirectoryDTO;
|
|
|
|
let v: VideoDTO;
|
|
|
|
let p: PhotoDTO;
|
|
|
|
let p2: PhotoDTO;
|
|
|
|
let pFaceLess: PhotoDTO;
|
|
|
|
let p4: PhotoDTO;
|
|
|
|
|
|
|
|
|
|
|
|
const setUpTestGallery = async (): Promise<void> => {
|
2023-10-09 04:32:32 +08:00
|
|
|
const directory: ParentDirectoryDTO = TestHelper.getDirectoryEntry(null, 'éűáúőóüöÉŰÚŐÓÜÖ[]^[[]]][asd]');
|
2021-07-07 03:37:19 +08:00
|
|
|
subDir = TestHelper.getDirectoryEntry(directory, 'The Phantom Menace');
|
|
|
|
subDir2 = TestHelper.getDirectoryEntry(directory, 'Return of the Jedi');
|
|
|
|
p = TestHelper.getPhotoEntry1(subDir);
|
|
|
|
p.metadata.rating = 4;
|
|
|
|
p.metadata.creationDate = 10000;
|
|
|
|
p2 = TestHelper.getPhotoEntry2(subDir);
|
|
|
|
p2.metadata.rating = 4;
|
|
|
|
p2.metadata.creationDate = 20000;
|
|
|
|
v = TestHelper.getVideoEntry1(subDir);
|
|
|
|
v.metadata.creationDate = 500;
|
|
|
|
const pFaceLessTmp = TestHelper.getPhotoEntry3(subDir);
|
|
|
|
pFaceLessTmp.metadata.rating = 0;
|
|
|
|
pFaceLessTmp.metadata.creationDate = 400000;
|
|
|
|
delete pFaceLessTmp.metadata.faces;
|
|
|
|
p4 = TestHelper.getPhotoEntry4(subDir2);
|
|
|
|
p4.metadata.rating = 5;
|
|
|
|
p4.metadata.creationDate = 100;
|
|
|
|
|
|
|
|
dir = await DBTestHelper.persistTestDir(directory);
|
|
|
|
|
|
|
|
subDir = dir.directories[0];
|
|
|
|
subDir2 = dir.directories[1];
|
|
|
|
p = (subDir.media.filter(m => m.name === p.name)[0] as any);
|
2022-12-05 05:23:51 +08:00
|
|
|
p.directory = subDir;
|
2021-07-07 03:37:19 +08:00
|
|
|
p2 = (subDir.media.filter(m => m.name === p2.name)[0] as any);
|
2022-12-05 05:23:51 +08:00
|
|
|
p2.directory = subDir;
|
2021-07-07 03:37:19 +08:00
|
|
|
v = (subDir.media.filter(m => m.name === v.name)[0] as any);
|
2022-12-05 05:23:51 +08:00
|
|
|
v.directory = subDir;
|
2021-07-07 03:37:19 +08:00
|
|
|
pFaceLess = (subDir.media.filter(m => m.name === pFaceLessTmp.name)[0] as any);
|
2022-12-05 05:23:51 +08:00
|
|
|
pFaceLess.directory = subDir;
|
2021-07-07 03:37:19 +08:00
|
|
|
p4 = (subDir2.media[0] as any);
|
2022-12-05 05:23:51 +08:00
|
|
|
p4.directory = subDir2;
|
2021-07-07 03:37:19 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
const setUpSqlDB = async () => {
|
|
|
|
await sqlHelper.initDB();
|
|
|
|
await setUpTestGallery();
|
2023-11-17 06:41:05 +08:00
|
|
|
await ObjectManagers.getInstance().init();
|
2021-07-07 03:37:19 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
before(async () => {
|
|
|
|
await setUpSqlDB();
|
2024-05-12 00:26:28 +08:00
|
|
|
Config.Gallery.ignoreTimestampOffset = false;
|
2021-07-07 03:37:19 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const previewifyMedia = <T extends FileDTO | PhotoDTO>(m: T): T => {
|
|
|
|
const tmpDir: DirectoryBaseDTO = m.directory as DirectoryBaseDTO;
|
|
|
|
const tmpM = tmpDir.media;
|
|
|
|
const tmpD = tmpDir.directories;
|
2023-08-26 03:24:05 +08:00
|
|
|
const tmpP = tmpDir.cover;
|
2021-07-07 03:37:19 +08:00
|
|
|
const tmpMT = tmpDir.metaFile;
|
|
|
|
delete tmpDir.directories;
|
|
|
|
delete tmpDir.media;
|
2023-08-26 03:24:05 +08:00
|
|
|
delete tmpDir.cover;
|
|
|
|
delete tmpDir.validCover;
|
2021-07-07 03:37:19 +08:00
|
|
|
delete tmpDir.metaFile;
|
|
|
|
const ret = Utils.clone(m);
|
|
|
|
delete (ret.directory as DirectoryBaseDTO).id;
|
|
|
|
delete (ret.directory as DirectoryBaseDTO).lastScanned;
|
|
|
|
delete (ret.directory as DirectoryBaseDTO).lastModified;
|
|
|
|
delete (ret.directory as DirectoryBaseDTO).mediaCount;
|
2023-09-11 19:19:51 +08:00
|
|
|
delete (ret.directory as DirectoryBaseDTO).youngestMedia;
|
|
|
|
delete (ret.directory as DirectoryBaseDTO).oldestMedia;
|
2021-07-07 03:37:19 +08:00
|
|
|
delete (ret as PhotoDTO).metadata;
|
|
|
|
tmpDir.directories = tmpD;
|
|
|
|
tmpDir.media = tmpM;
|
2023-08-26 03:24:05 +08:00
|
|
|
tmpDir.cover = tmpP;
|
2021-07-07 03:37:19 +08:00
|
|
|
tmpDir.metaFile = tmpMT;
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
after(async () => {
|
|
|
|
await sqlHelper.clearDB();
|
2022-02-02 07:24:16 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(() => {
|
2023-08-26 03:24:05 +08:00
|
|
|
Config.AlbumCover.SearchQuery = null;
|
2023-08-31 05:35:42 +08:00
|
|
|
Config.AlbumCover.Sorting = [new ClientSortingConfig(SortByTypes.Rating, false),
|
|
|
|
new ClientSortingConfig(SortByTypes.Date, false)];
|
2021-07-07 03:37:19 +08:00
|
|
|
});
|
|
|
|
|
2022-01-18 06:05:10 +08:00
|
|
|
|
2023-08-26 03:24:05 +08:00
|
|
|
it('should list directories without cover', async () => {
|
|
|
|
const pm = new CoverManager();
|
2022-01-18 06:05:10 +08:00
|
|
|
const partialDir = (d: DirectoryBaseDTO) => {
|
|
|
|
return {id: d.id, name: d.name, path: d.path};
|
|
|
|
};
|
2023-08-26 03:24:05 +08:00
|
|
|
expect(await pm.getPartialDirsWithoutCovers()).to.deep.equalInAnyOrder([partialDir(dir)]);
|
2022-01-18 06:05:10 +08:00
|
|
|
const conn = await SQLConnection.getConnection();
|
|
|
|
|
|
|
|
await conn.createQueryBuilder()
|
2023-10-09 04:32:32 +08:00
|
|
|
.update(DirectoryEntity).set({validCover: false}).execute();
|
2022-01-18 06:05:10 +08:00
|
|
|
|
2023-08-26 03:24:05 +08:00
|
|
|
expect(await pm.getPartialDirsWithoutCovers()).to.deep.equalInAnyOrder([dir, subDir, subDir2].map(d => partialDir(d)));
|
2022-01-18 06:05:10 +08:00
|
|
|
});
|
|
|
|
|
2023-08-26 03:24:05 +08:00
|
|
|
it('should sort directory cover', async () => {
|
|
|
|
const pm = new CoverManager();
|
2023-08-31 05:35:42 +08:00
|
|
|
Config.AlbumCover.Sorting = [new ClientSortingConfig(SortByTypes.Rating, false),
|
|
|
|
new ClientSortingConfig(SortByTypes.Date, false)];
|
2023-08-26 03:24:05 +08:00
|
|
|
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p2));
|
2023-08-31 05:35:42 +08:00
|
|
|
Config.AlbumCover.Sorting = [
|
|
|
|
new ClientSortingConfig(SortByTypes.Date, false)];
|
2023-08-26 03:24:05 +08:00
|
|
|
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(pFaceLess));
|
2023-08-31 05:35:42 +08:00
|
|
|
Config.AlbumCover.Sorting = [new ClientSortingConfig(SortByTypes.Rating, false)];
|
2023-08-26 03:24:05 +08:00
|
|
|
expect(Utils.clone(await pm.setAndGetCoverForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(p4));
|
2023-08-31 05:35:42 +08:00
|
|
|
Config.AlbumCover.Sorting = [new ClientSortingConfig(SortByTypes.Name, false)];
|
2023-08-26 03:24:05 +08:00
|
|
|
expect(Utils.clone(await pm.setAndGetCoverForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(v));
|
2021-07-07 03:37:19 +08:00
|
|
|
});
|
|
|
|
|
2023-08-26 03:24:05 +08:00
|
|
|
it('should get cover for directory', async () => {
|
|
|
|
const pm = new CoverManager();
|
2021-07-07 03:37:19 +08:00
|
|
|
|
2023-08-26 03:24:05 +08:00
|
|
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch;
|
|
|
|
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p));
|
|
|
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
|
|
|
|
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p2));
|
|
|
|
expect(Utils.clone(await pm.setAndGetCoverForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(p2));
|
|
|
|
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir2))).to.deep.equalInAnyOrder(previewifyMedia(p4));
|
2021-07-07 03:37:19 +08:00
|
|
|
|
|
|
|
});
|
|
|
|
|
2023-08-26 03:24:05 +08:00
|
|
|
it('should get cover for saved search', async () => {
|
|
|
|
const pm = new CoverManager();
|
|
|
|
Config.AlbumCover.SearchQuery = null;
|
2023-11-01 03:38:08 +08:00
|
|
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
2021-07-07 03:37:19 +08:00
|
|
|
searchQuery: {
|
|
|
|
type: SearchQueryTypes.any_text,
|
|
|
|
text: 'sw'
|
|
|
|
} as TextSearch
|
|
|
|
}))).to.deep.equalInAnyOrder(previewifyMedia(p4));
|
2023-08-26 03:24:05 +08:00
|
|
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch;
|
2023-11-01 03:38:08 +08:00
|
|
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
2021-07-07 03:37:19 +08:00
|
|
|
searchQuery: {
|
|
|
|
type: SearchQueryTypes.any_text,
|
|
|
|
text: 'sw'
|
|
|
|
} as TextSearch
|
|
|
|
}))).to.deep.equalInAnyOrder(previewifyMedia(p));
|
2023-08-26 03:24:05 +08:00
|
|
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
|
2023-11-01 03:38:08 +08:00
|
|
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
2021-07-07 03:37:19 +08:00
|
|
|
searchQuery: {
|
|
|
|
type: SearchQueryTypes.any_text,
|
|
|
|
text: 'sw'
|
|
|
|
} as TextSearch
|
|
|
|
}))).to.deep.equalInAnyOrder(previewifyMedia(p2));
|
2022-02-02 07:24:16 +08:00
|
|
|
// Having a preview search query that does not return valid result
|
2023-08-26 03:24:05 +08:00
|
|
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'wont find it'} as TextSearch;
|
2023-11-01 03:38:08 +08:00
|
|
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
2022-02-02 07:24:16 +08:00
|
|
|
searchQuery: {
|
|
|
|
type: SearchQueryTypes.any_text,
|
|
|
|
text: 'Derem'
|
|
|
|
} as TextSearch
|
|
|
|
}))).to.deep.equalInAnyOrder(previewifyMedia(p2));
|
2022-02-05 19:19:09 +08:00
|
|
|
// having a saved search that does not have any image
|
2023-08-26 03:24:05 +08:00
|
|
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
|
2023-11-01 03:38:08 +08:00
|
|
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
2022-02-05 19:19:09 +08:00
|
|
|
searchQuery: {
|
|
|
|
type: SearchQueryTypes.any_text,
|
|
|
|
text: 'wont find it'
|
|
|
|
} as TextSearch
|
|
|
|
}))).to.deep.equal(null);
|
2021-07-07 03:37:19 +08:00
|
|
|
});
|
|
|
|
|
2023-08-26 03:24:05 +08:00
|
|
|
it('should invalidate and update cover', async () => {
|
2022-01-28 06:43:34 +08:00
|
|
|
const gm = new GalleryManagerTest();
|
2023-08-26 03:24:05 +08:00
|
|
|
const pm = new CoverManager();
|
2022-01-28 06:43:34 +08:00
|
|
|
const conn = await SQLConnection.getConnection();
|
|
|
|
|
|
|
|
const selectDir = async () => {
|
2022-03-31 04:18:02 +08:00
|
|
|
return await conn.getRepository(DirectoryEntity).findOne({
|
|
|
|
where: {id: subDir.id},
|
2022-01-28 06:43:34 +08:00
|
|
|
join: {
|
|
|
|
alias: 'dir',
|
2023-08-26 03:24:05 +08:00
|
|
|
leftJoinAndSelect: {cover: 'dir.cover'}
|
2022-01-28 06:43:34 +08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let subdir = await selectDir();
|
|
|
|
|
2023-08-26 03:24:05 +08:00
|
|
|
expect(subdir.validCover).to.equal(true);
|
|
|
|
expect(subdir.cover.id).to.equal(p2.id);
|
2022-01-28 06:43:34 +08:00
|
|
|
|
|
|
|
// new version should invalidate
|
|
|
|
await pm.onNewDataVersion(subDir as ParentDirectoryDTO);
|
|
|
|
subdir = await selectDir();
|
2023-08-26 03:24:05 +08:00
|
|
|
expect(subdir.validCover).to.equal(false);
|
|
|
|
// during invalidation, we do not remove the previous cover (it's good to show at least some photo)
|
|
|
|
expect(subdir.cover.id).to.equal(p2.id);
|
2022-01-28 06:43:34 +08:00
|
|
|
|
|
|
|
await conn.createQueryBuilder()
|
2023-10-09 04:32:32 +08:00
|
|
|
.update(DirectoryEntity)
|
|
|
|
.set({validCover: false, cover: null}).execute();
|
2023-08-26 03:24:05 +08:00
|
|
|
expect((await selectDir()).cover).to.equal(null);
|
2022-01-28 06:43:34 +08:00
|
|
|
|
2022-12-05 05:23:51 +08:00
|
|
|
|
|
|
|
const res = await gm.getParentDirFromId(conn,
|
2023-10-09 04:32:32 +08:00
|
|
|
(await gm.getDirIdAndTime(conn, dir.name, dir.path)).id);
|
2022-01-28 06:43:34 +08:00
|
|
|
subdir = await selectDir();
|
2023-08-26 03:24:05 +08:00
|
|
|
expect(subdir.validCover).to.equal(true);
|
|
|
|
expect(subdir.cover.id).to.equal(p2.id);
|
2022-01-28 06:43:34 +08:00
|
|
|
|
|
|
|
});
|
|
|
|
|
2021-07-07 03:37:19 +08:00
|
|
|
});
|