mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-12-25 02:04:15 +02:00
improving db scheme. Adding Mysql tests
This commit is contained in:
parent
8ed202b53d
commit
d92003eeee
12
.travis.yml
12
.travis.yml
@ -1,9 +1,15 @@
|
||||
dist: trusty
|
||||
language: node_js
|
||||
node_js:
|
||||
- '10'
|
||||
- '11'
|
||||
|
||||
- '10'
|
||||
- '11'
|
||||
env:
|
||||
- Server-database-mysql-host='localhost'
|
||||
- Server-database-mysql-username='root'
|
||||
- Server-database-mysql-password=''
|
||||
- Server-database-mysql-database='pigallery2_travis'
|
||||
services:
|
||||
- mysql
|
||||
addons:
|
||||
chrome: stable
|
||||
before_install:
|
||||
|
@ -222,7 +222,7 @@ export class GalleryMWs {
|
||||
}
|
||||
|
||||
public static async autocomplete(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.Search.autocompleteEnabled === false) {
|
||||
if (Config.Client.Search.AutoComplete.enabled === false) {
|
||||
return next();
|
||||
}
|
||||
if (!(req.params.text)) {
|
||||
|
@ -17,16 +17,17 @@ import {DataStructureVersion} from '../../../common/DataStructureVersion';
|
||||
import {FileEntity} from './enitites/FileEntity';
|
||||
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
||||
import {PersonEntry} from './enitites/PersonEntry';
|
||||
import {Utils} from '../../../common/Utils';
|
||||
|
||||
|
||||
export class SQLConnection {
|
||||
|
||||
|
||||
private static connection: Connection = null;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
private static connection: Connection = null;
|
||||
|
||||
public static async getConnection(): Promise<Connection> {
|
||||
if (this.connection == null) {
|
||||
const options: any = this.getDriver(Config.Server.database);
|
||||
@ -44,8 +45,10 @@ export class SQLConnection {
|
||||
VersionEntity
|
||||
];
|
||||
options.synchronize = false;
|
||||
//options.logging = 'all';
|
||||
this.connection = await createConnection(options);
|
||||
// options.logging = 'all';
|
||||
|
||||
|
||||
this.connection = await this.createConnection(options);
|
||||
await SQLConnection.schemeSync(this.connection);
|
||||
}
|
||||
return this.connection;
|
||||
@ -72,7 +75,7 @@ export class SQLConnection {
|
||||
];
|
||||
options.synchronize = false;
|
||||
// options.logging = "all";
|
||||
const conn = await createConnection(options);
|
||||
const conn = await this.createConnection(options);
|
||||
await SQLConnection.schemeSync(conn);
|
||||
await conn.close();
|
||||
return true;
|
||||
@ -92,6 +95,38 @@ export class SQLConnection {
|
||||
|
||||
}
|
||||
|
||||
public static async close() {
|
||||
try {
|
||||
if (this.connection != null) {
|
||||
await this.connection.close();
|
||||
this.connection = null;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
private static async createConnection(options: ConnectionOptions) {
|
||||
if (options.type === 'sqlite') {
|
||||
return await createConnection(options);
|
||||
}
|
||||
try {
|
||||
return await createConnection(options);
|
||||
} catch (e) {
|
||||
if (e.sqlMessage === 'Unknown database \'' + options.database + '\'') {
|
||||
Logger.debug('creating database: ' + options.database);
|
||||
const tmpOption = Utils.clone(options);
|
||||
// @ts-ignore
|
||||
delete tmpOption.database;
|
||||
const tmpConn = await createConnection(tmpOption);
|
||||
await tmpConn.query('CREATE DATABASE IF NOT EXISTS ' + options.database);
|
||||
await tmpConn.close();
|
||||
return await createConnection(options);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static async schemeSync(connection: Connection) {
|
||||
let version = null;
|
||||
try {
|
||||
@ -145,16 +180,5 @@ export class SQLConnection {
|
||||
return driver;
|
||||
}
|
||||
|
||||
public static async close() {
|
||||
try {
|
||||
if (this.connection != null) {
|
||||
await this.connection.close();
|
||||
this.connection = null;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import {VideoEntity} from './enitites/VideoEntity';
|
||||
import {PersonEntry} from './enitites/PersonEntry';
|
||||
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
||||
import {SelectQueryBuilder} from 'typeorm';
|
||||
import {Config} from '../../../common/config/private/Config';
|
||||
|
||||
export class SearchManager implements ISearchManager {
|
||||
|
||||
@ -40,7 +41,7 @@ export class SearchManager implements ISearchManager {
|
||||
.createQueryBuilder('photo')
|
||||
.select('DISTINCT(photo.metadata.keywords)')
|
||||
.where('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.limit(5)
|
||||
.limit(Config.Client.Search.AutoComplete.maxItemsPerCategory)
|
||||
.getRawMany())
|
||||
.map(r => <Array<string>>(<string>r.metadataKeywords).split(','))
|
||||
.forEach(keywords => {
|
||||
@ -52,7 +53,8 @@ export class SearchManager implements ISearchManager {
|
||||
.createQueryBuilder('person')
|
||||
.select('DISTINCT(person.name)')
|
||||
.where('person.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.limit(5)
|
||||
.limit(Config.Client.Search.AutoComplete.maxItemsPerCategory)
|
||||
.orderBy('person.name')
|
||||
.getRawMany())
|
||||
.map(r => r.name), SearchTypes.person));
|
||||
|
||||
@ -64,7 +66,7 @@ export class SearchManager implements ISearchManager {
|
||||
.orWhere('photo.metadata.positionData.state LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.orWhere('photo.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.groupBy('photo.metadata.positionData.country, photo.metadata.positionData.state, photo.metadata.positionData.city')
|
||||
.limit(5)
|
||||
.limit(Config.Client.Search.AutoComplete.maxItemsPerCategory)
|
||||
.getRawMany())
|
||||
.filter(pm => !!pm)
|
||||
.map(pm => <Array<string>>[pm.city || '', pm.country || '', pm.state || ''])
|
||||
@ -77,7 +79,7 @@ export class SearchManager implements ISearchManager {
|
||||
.createQueryBuilder('media')
|
||||
.select('DISTINCT(media.name)')
|
||||
.where('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.limit(5)
|
||||
.limit(Config.Client.Search.AutoComplete.maxItemsPerCategory)
|
||||
.getRawMany())
|
||||
.map(r => r.name), SearchTypes.photo));
|
||||
|
||||
@ -86,7 +88,7 @@ export class SearchManager implements ISearchManager {
|
||||
.createQueryBuilder('media')
|
||||
.select('DISTINCT(media.metadata.caption) as caption')
|
||||
.where('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.limit(5)
|
||||
.limit(Config.Client.Search.AutoComplete.maxItemsPerCategory)
|
||||
.getRawMany())
|
||||
.map(r => r.caption), SearchTypes.photo));
|
||||
|
||||
@ -95,7 +97,7 @@ export class SearchManager implements ISearchManager {
|
||||
.createQueryBuilder('media')
|
||||
.select('DISTINCT(media.name)')
|
||||
.where('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.limit(5)
|
||||
.limit(Config.Client.Search.AutoComplete.maxItemsPerCategory)
|
||||
.getRawMany())
|
||||
.map(r => r.name), SearchTypes.video));
|
||||
|
||||
@ -103,7 +105,7 @@ export class SearchManager implements ISearchManager {
|
||||
.createQueryBuilder('dir')
|
||||
.select('DISTINCT(dir.name)')
|
||||
.where('dir.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'})
|
||||
.limit(5)
|
||||
.limit(Config.Client.Search.AutoComplete.maxItemsPerCategory)
|
||||
.getRawMany())
|
||||
.map(r => r.name), SearchTypes.directory));
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, Unique, Index} from 'typeorm';
|
||||
import {Column, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn, Unique} from 'typeorm';
|
||||
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||
import {MediaEntity} from './MediaEntity';
|
||||
import {FileEntity} from './FileEntity';
|
||||
@ -8,7 +8,7 @@ import {FileEntity} from './FileEntity';
|
||||
export class DirectoryEntity implements DirectoryDTO {
|
||||
|
||||
@Index()
|
||||
@PrimaryGeneratedColumn()
|
||||
@PrimaryGeneratedColumn({unsigned: true})
|
||||
id: number;
|
||||
|
||||
@Index()
|
||||
@ -22,18 +22,28 @@ export class DirectoryEntity implements DirectoryDTO {
|
||||
/**
|
||||
* last time the directory was modified (from outside, eg.: a new media was added)
|
||||
*/
|
||||
@Column('bigint')
|
||||
@Column('bigint', {
|
||||
unsigned: true, transformer: {
|
||||
from: v => parseInt(v, 10),
|
||||
to: v => v
|
||||
}
|
||||
})
|
||||
public lastModified: number;
|
||||
|
||||
/**
|
||||
* Last time the directory was fully scanned, not only for a few media to create a preview
|
||||
*/
|
||||
@Column({type: 'bigint', nullable: true})
|
||||
@Column({
|
||||
type: 'bigint', nullable: true, unsigned: true, transformer: {
|
||||
from: v => parseInt(v, 10),
|
||||
to: v => v
|
||||
}
|
||||
})
|
||||
public lastScanned: number;
|
||||
|
||||
isPartial?: boolean;
|
||||
|
||||
@Column('smallint')
|
||||
@Column('smallint', {unsigned: true})
|
||||
mediaCount: number;
|
||||
|
||||
@Index()
|
||||
|
@ -20,7 +20,7 @@ export class FaceRegionBoxEntry implements FaceRegionBox {
|
||||
@Entity()
|
||||
export class FaceRegionEntry {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
@PrimaryGeneratedColumn({unsigned: true})
|
||||
id: number;
|
||||
|
||||
@Column(type => FaceRegionBoxEntry)
|
||||
|
@ -7,7 +7,7 @@ import {FileDTO} from '../../../../common/entities/FileDTO';
|
||||
export class FileEntity implements FileDTO {
|
||||
|
||||
@Index()
|
||||
@PrimaryGeneratedColumn()
|
||||
@PrimaryGeneratedColumn({unsigned: true})
|
||||
id: number;
|
||||
|
||||
@Column('text')
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Column, Entity, OneToMany, ManyToOne, PrimaryGeneratedColumn, TableInheritance, Unique, Index} from 'typeorm';
|
||||
import {Column, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn, TableInheritance, Unique} from 'typeorm';
|
||||
import {DirectoryEntity} from './DirectoryEntity';
|
||||
import {MediaDimension, MediaDTO, MediaMetadata} from '../../../../common/entities/MediaDTO';
|
||||
import {OrientationTypes} from 'ts-exif-parser';
|
||||
@ -22,10 +22,15 @@ export class MediaMetadataEntity implements MediaMetadata {
|
||||
@Column(type => MediaDimensionEntity)
|
||||
size: MediaDimensionEntity;
|
||||
|
||||
@Column('bigint')
|
||||
@Column('bigint', {
|
||||
unsigned: true, transformer: {
|
||||
from: v => parseInt(v, 10),
|
||||
to: v => v
|
||||
}
|
||||
})
|
||||
creationDate: number;
|
||||
|
||||
@Column('int')
|
||||
@Column('int', {unsigned: true})
|
||||
fileSize: number;
|
||||
|
||||
@Column('simple-array')
|
||||
@ -37,30 +42,30 @@ export class MediaMetadataEntity implements MediaMetadata {
|
||||
@Column(type => PositionMetaDataEntity)
|
||||
positionData: PositionMetaDataEntity;
|
||||
|
||||
@Column('tinyint', {default: OrientationTypes.TOP_LEFT})
|
||||
@Column('tinyint', {unsigned: true, default: OrientationTypes.TOP_LEFT})
|
||||
orientation: OrientationTypes;
|
||||
|
||||
@OneToMany(type => FaceRegionEntry, faceRegion => faceRegion.media)
|
||||
faces: FaceRegionEntry[];
|
||||
|
||||
@Column('int')
|
||||
@Column('int', {unsigned: true})
|
||||
bitRate: number;
|
||||
|
||||
@Column('bigint')
|
||||
@Column('int', {unsigned: true})
|
||||
duration: number;
|
||||
}
|
||||
|
||||
// TODO: fix inheritance once its working in typeorm
|
||||
@Entity()
|
||||
@Unique(['name', 'directory'])
|
||||
@TableInheritance({column: {type: 'varchar', name: 'type'}})
|
||||
@TableInheritance({column: {type: 'varchar', name: 'type', length: 32}})
|
||||
export abstract class MediaEntity implements MediaDTO {
|
||||
|
||||
@Index()
|
||||
@PrimaryGeneratedColumn()
|
||||
@PrimaryGeneratedColumn({unsigned: true})
|
||||
id: number;
|
||||
|
||||
@Column('text')
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Index()
|
||||
|
@ -6,7 +6,7 @@ import {FaceRegionEntry} from './FaceRegionEntry';
|
||||
@Unique(['name'])
|
||||
export class PersonEntry {
|
||||
@Index()
|
||||
@PrimaryGeneratedColumn()
|
||||
@PrimaryGeneratedColumn({unsigned: true})
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
|
@ -5,7 +5,7 @@ import {UserDTO} from '../../../../common/entities/UserDTO';
|
||||
|
||||
@Entity()
|
||||
export class SharingEntity implements SharingDTO {
|
||||
@PrimaryGeneratedColumn()
|
||||
@PrimaryGeneratedColumn({unsigned: true})
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
@ -17,10 +17,20 @@ export class SharingEntity implements SharingDTO {
|
||||
@Column({type: 'text', nullable: true})
|
||||
password: string;
|
||||
|
||||
@Column()
|
||||
@Column('bigint', {
|
||||
unsigned: true, transformer: {
|
||||
from: v => parseInt(v, 10),
|
||||
to: v => v
|
||||
}
|
||||
})
|
||||
expires: number;
|
||||
|
||||
@Column()
|
||||
@Column('bigint', {
|
||||
unsigned: true, transformer: {
|
||||
from: v => parseInt(v, 10),
|
||||
to: v => v
|
||||
}
|
||||
})
|
||||
timeStamp: number;
|
||||
|
||||
@Column()
|
||||
|
@ -1 +1 @@
|
||||
export const DataStructureVersion = 8;
|
||||
export const DataStructureVersion = 9;
|
||||
|
@ -7,14 +7,19 @@ export module ClientConfig {
|
||||
OpenStreetMap, Mapbox, Custom
|
||||
}
|
||||
|
||||
export interface AutoCompleteConfig {
|
||||
enabled: boolean;
|
||||
maxItemsPerCategory: number;
|
||||
cacheTimeout: number;
|
||||
}
|
||||
|
||||
export interface SearchConfig {
|
||||
enabled: boolean;
|
||||
instantSearchEnabled: boolean;
|
||||
autocompleteEnabled: boolean;
|
||||
InstantSearchTimeout: number;
|
||||
autocompleteCacheTimeout: number;
|
||||
instantSearchCacheTimeout: number;
|
||||
searchCacheTimeout: number;
|
||||
AutoComplete: AutoCompleteConfig;
|
||||
}
|
||||
|
||||
export interface SharingConfig {
|
||||
@ -95,11 +100,14 @@ export class PublicConfigClass {
|
||||
Search: {
|
||||
enabled: true,
|
||||
instantSearchEnabled: true,
|
||||
autocompleteEnabled: true,
|
||||
InstantSearchTimeout: 3000,
|
||||
autocompleteCacheTimeout: 1000 * 60 * 60,
|
||||
searchCacheTimeout: 1000 * 60 * 60,
|
||||
instantSearchCacheTimeout: 1000 * 60 * 60
|
||||
instantSearchCacheTimeout: 1000 * 60 * 60,
|
||||
AutoComplete: {
|
||||
enabled: true,
|
||||
cacheTimeout: 1000 * 60 * 60,
|
||||
maxItemsPerCategory: 5
|
||||
}
|
||||
},
|
||||
Sharing: {
|
||||
enabled: true,
|
||||
|
@ -77,7 +77,7 @@ export class GalleryCacheService {
|
||||
const tmp = localStorage.getItem(key);
|
||||
if (tmp != null) {
|
||||
const value: CacheItem<AutoCompleteItem[]> = JSON.parse(tmp);
|
||||
if (value.timestamp < Date.now() - Config.Client.Search.autocompleteCacheTimeout) {
|
||||
if (value.timestamp < Date.now() - Config.Client.Search.AutoComplete.cacheTimeout) {
|
||||
localStorage.removeItem(key);
|
||||
return null;
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ export class GallerySearchComponent implements OnDestroy {
|
||||
|
||||
const searchText = (<HTMLInputElement>event.target).value.trim();
|
||||
|
||||
if (Config.Client.Search.autocompleteEnabled &&
|
||||
if (Config.Client.Search.AutoComplete.enabled &&
|
||||
this.cache.lastAutocomplete !== searchText) {
|
||||
this.cache.lastAutocomplete = searchText;
|
||||
this.autocomplete(searchText).catch(console.error);
|
||||
@ -92,7 +92,7 @@ export class GallerySearchComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
private async autocomplete(searchText: string) {
|
||||
if (!Config.Client.Search.autocompleteEnabled) {
|
||||
if (!Config.Client.Search.AutoComplete.enabled) {
|
||||
return;
|
||||
}
|
||||
if (searchText.trim() === '.') {
|
||||
|
@ -15,12 +15,15 @@ export class SettingsService {
|
||||
Client: {
|
||||
Search: {
|
||||
enabled: true,
|
||||
autocompleteEnabled: true,
|
||||
AutoComplete: {
|
||||
enabled: true,
|
||||
cacheTimeout: 1000 * 60 * 60,
|
||||
maxItemsPerCategory: 5
|
||||
},
|
||||
instantSearchEnabled: true,
|
||||
InstantSearchTimeout: 0,
|
||||
searchCacheTimeout: 1000 * 60 * 60,
|
||||
instantSearchCacheTimeout: 1000 * 60 * 60,
|
||||
autocompleteCacheTimeout: 1000 * 60 * 60
|
||||
},
|
||||
Thumbnail: {
|
||||
concurrentThumbnailGenerations: null,
|
||||
|
107
test/backend/SQLTestHelper.ts
Normal file
107
test/backend/SQLTestHelper.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import {Config} from '../../common/config/private/Config';
|
||||
import {DatabaseType} from '../../common/config/private/IPrivateConfig';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {SQLConnection} from '../../backend/model/sql/SQLConnection';
|
||||
|
||||
declare let describe: any;
|
||||
const savedDescribe = describe;
|
||||
|
||||
export class SQLTestHelper {
|
||||
|
||||
static enable = {
|
||||
sqlite: true,
|
||||
mysql: true
|
||||
};
|
||||
public static readonly savedDescribe = savedDescribe;
|
||||
tempDir: string;
|
||||
dbPath: string;
|
||||
|
||||
constructor(public dbType: DatabaseType) {
|
||||
this.tempDir = path.resolve(__dirname, './tmp');
|
||||
this.dbPath = path.resolve(__dirname, './tmp', 'test.db');
|
||||
|
||||
}
|
||||
|
||||
static describe(name: string, tests: (helper?: SQLTestHelper) => void) {
|
||||
savedDescribe(name, async () => {
|
||||
if (SQLTestHelper.enable.sqlite) {
|
||||
const helper = new SQLTestHelper(DatabaseType.sqlite);
|
||||
savedDescribe('sqlite', () => {
|
||||
return tests(helper);
|
||||
});
|
||||
}
|
||||
if (SQLTestHelper.enable.mysql) {
|
||||
const helper = new SQLTestHelper(DatabaseType.mysql);
|
||||
savedDescribe('mysql', function () {
|
||||
this.timeout(99999999);
|
||||
// @ts-ignore
|
||||
return tests(helper);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async initDB() {
|
||||
if (this.dbType === DatabaseType.sqlite) {
|
||||
await this.initSQLite();
|
||||
} else {
|
||||
await this.initMySQL();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async clearDB() {
|
||||
if (this.dbType === DatabaseType.sqlite) {
|
||||
await this.clearUpSQLite();
|
||||
} else {
|
||||
await this.clearUpMysql();
|
||||
}
|
||||
}
|
||||
|
||||
private async initSQLite() {
|
||||
await this.resetSQLite();
|
||||
|
||||
Config.Server.database.type = DatabaseType.sqlite;
|
||||
Config.Server.database.sqlite.storage = this.dbPath;
|
||||
}
|
||||
|
||||
private async initMySQL() {
|
||||
Config.Server.database.type = DatabaseType.mysql;
|
||||
Config.Server.database.mysql.database = 'pigallery2_test';
|
||||
|
||||
await this.resetMySQL();
|
||||
}
|
||||
|
||||
private async resetSQLite() {
|
||||
await SQLConnection.close();
|
||||
|
||||
if (fs.existsSync(this.dbPath)) {
|
||||
fs.unlinkSync(this.dbPath);
|
||||
}
|
||||
if (fs.existsSync(this.tempDir)) {
|
||||
fs.rmdirSync(this.tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
private async resetMySQL() {
|
||||
Config.Server.database.type = DatabaseType.mysql;
|
||||
Config.Server.database.mysql.database = 'pigallery2_test';
|
||||
const conn = await SQLConnection.getConnection();
|
||||
await conn.query('DROP DATABASE IF EXISTS ' + conn.options.database);
|
||||
await conn.query('CREATE DATABASE IF NOT EXISTS ' + conn.options.database);
|
||||
await SQLConnection.close();
|
||||
}
|
||||
|
||||
private async clearUpMysql() {
|
||||
Config.Server.database.type = DatabaseType.mysql;
|
||||
Config.Server.database.mysql.database = 'pigallery2_test';
|
||||
const conn = await SQLConnection.getConnection();
|
||||
await conn.query('DROP DATABASE IF EXISTS ' + conn.options.database);
|
||||
await SQLConnection.close();
|
||||
}
|
||||
|
||||
private async clearUpSQLite() {
|
||||
return this.resetSQLite();
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ import {
|
||||
PositionMetaDataEntity
|
||||
} from '../../../../../backend/model/sql/enitites/PhotoEntity';
|
||||
import {MediaDimensionEntity} from '../../../../../backend/model/sql/enitites/MediaEntity';
|
||||
import {DataStructureVersion} from '../../../../../common/DataStructureVersion';
|
||||
import {VersionEntity} from '../../../../../backend/model/sql/enitites/VersionEntity';
|
||||
|
||||
describe('Typeorm integration', () => {
|
||||
|
58
test/backend/unit/model/sql/GalleryManager.ts
Normal file
58
test/backend/unit/model/sql/GalleryManager.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import {expect} from 'chai';
|
||||
import {TestHelper} from './TestHelper';
|
||||
import {SQLTestHelper} from '../../../SQLTestHelper';
|
||||
import {GalleryManager} from '../../../../../backend/model/sql/GalleryManager';
|
||||
import {IndexingManager} from '../../../../../backend/model/sql/IndexingManager';
|
||||
import {DirectoryDTO} from '../../../../../common/entities/DirectoryDTO';
|
||||
import {Utils} from '../../../../../common/Utils';
|
||||
import {ObjectManagerRepository} from '../../../../../backend/model/ObjectManagerRepository';
|
||||
import {PersonManager} from '../../../../../backend/model/sql/PersonManager';
|
||||
import {MediaEntity} from '../../../../../backend/model/sql/enitites/MediaEntity';
|
||||
|
||||
class IndexingManagerTest extends IndexingManager {
|
||||
|
||||
public async saveToDB(scannedDirectory: DirectoryDTO): Promise<void> {
|
||||
return super.saveToDB(scannedDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
// to help WebStorm to handle the test cases
|
||||
declare let describe: any;
|
||||
declare const after: any;
|
||||
describe = SQLTestHelper.describe;
|
||||
|
||||
describe('GalleryManager', (sqlHelper: SQLTestHelper) => {
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
await sqlHelper.initDB();
|
||||
ObjectManagerRepository.getInstance().PersonManager = new PersonManager();
|
||||
});
|
||||
|
||||
|
||||
after(async () => {
|
||||
await sqlHelper.clearDB();
|
||||
});
|
||||
|
||||
it('should get random photo', async () => {
|
||||
const gm = new GalleryManager();
|
||||
const im = new IndexingManagerTest();
|
||||
|
||||
const parent = TestHelper.getRandomizedDirectoryEntry();
|
||||
const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1');
|
||||
expect(await gm.getRandomPhoto({})).to.not.exist;
|
||||
DirectoryDTO.removeReferences(parent);
|
||||
await im.saveToDB(Utils.clone(parent));
|
||||
|
||||
delete p1.metadata.faces;
|
||||
delete p1.directory;
|
||||
delete p1.id;
|
||||
const found: MediaEntity = <any>await gm.getRandomPhoto({});
|
||||
delete found.metadata.bitRate;
|
||||
delete found.metadata.duration;
|
||||
delete found.directory;
|
||||
delete found.id;
|
||||
expect(Utils.clone(found)).to.be.deep.equal(Utils.clone(p1));
|
||||
});
|
||||
|
||||
});
|
@ -1,8 +1,7 @@
|
||||
import {expect} from 'chai';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {Config} from '../../../../../common/config/private/Config';
|
||||
import {DatabaseType, ReIndexingSensitivity} from '../../../../../common/config/private/IPrivateConfig';
|
||||
import {ReIndexingSensitivity} from '../../../../../common/config/private/IPrivateConfig';
|
||||
import {SQLConnection} from '../../../../../backend/model/sql/SQLConnection';
|
||||
import {GalleryManager} from '../../../../../backend/model/sql/GalleryManager';
|
||||
import {DirectoryDTO} from '../../../../../common/entities/DirectoryDTO';
|
||||
@ -15,6 +14,7 @@ import {FileDTO} from '../../../../../common/entities/FileDTO';
|
||||
import {IndexingManager} from '../../../../../backend/model/sql/IndexingManager';
|
||||
import {ObjectManagerRepository} from '../../../../../backend/model/ObjectManagerRepository';
|
||||
import {PersonManager} from '../../../../../backend/model/sql/PersonManager';
|
||||
import {SQLTestHelper} from '../../../SQLTestHelper';
|
||||
|
||||
class GalleryManagerTest extends GalleryManager {
|
||||
|
||||
@ -41,43 +41,22 @@ class IndexingManagerTest extends IndexingManager {
|
||||
}
|
||||
}
|
||||
|
||||
describe('IndexingManager', () => {
|
||||
// to help WebStorm to handle the test cases
|
||||
declare let describe: any;
|
||||
declare const after: any;
|
||||
describe = SQLTestHelper.describe;
|
||||
|
||||
describe('IndexingManager', (sqlHelper: SQLTestHelper) => {
|
||||
|
||||
const tempDir = path.join(__dirname, '../../tmp');
|
||||
const dbPath = path.join(tempDir, 'test.db');
|
||||
|
||||
|
||||
const setUpSqlDB = async () => {
|
||||
if (fs.existsSync(dbPath)) {
|
||||
fs.unlinkSync(dbPath);
|
||||
}
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir);
|
||||
}
|
||||
|
||||
Config.Server.database.type = DatabaseType.sqlite;
|
||||
Config.Server.database.sqlite.storage = dbPath;
|
||||
ObjectManagerRepository.getInstance().PersonManager = new PersonManager();
|
||||
|
||||
};
|
||||
|
||||
const tearDownSqlDB = async () => {
|
||||
await SQLConnection.close();
|
||||
if (fs.existsSync(dbPath)) {
|
||||
fs.unlinkSync(dbPath);
|
||||
}
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.rmdirSync(tempDir);
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await setUpSqlDB();
|
||||
await sqlHelper.initDB();
|
||||
ObjectManagerRepository.getInstance().PersonManager = new PersonManager();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await tearDownSqlDB();
|
||||
|
||||
after(async () => {
|
||||
await sqlHelper.clearDB();
|
||||
});
|
||||
|
||||
const removeIds = (dir: DirectoryDTO) => {
|
||||
@ -245,7 +224,7 @@ describe('IndexingManager', () => {
|
||||
expect(selected.media.length).to.deep.equal(subDir.media.length);
|
||||
}) as any).timeout(40000);
|
||||
|
||||
describe('Test listDirectory', () => {
|
||||
SQLTestHelper.savedDescribe('Test listDirectory', () => {
|
||||
const statSync = fs.statSync;
|
||||
let dirTime = 0;
|
||||
const indexedTime = {
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {expect} from 'chai';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {Config} from '../../../../../common/config/private/Config';
|
||||
import {DatabaseType} from '../../../../../common/config/private/IPrivateConfig';
|
||||
import {SQLConnection} from '../../../../../backend/model/sql/SQLConnection';
|
||||
import {PhotoEntity} from '../../../../../backend/model/sql/enitites/PhotoEntity';
|
||||
import {SearchManager} from '../../../../../backend/model/sql/SearchManager';
|
||||
@ -15,12 +11,15 @@ import {VideoEntity} from '../../../../../backend/model/sql/enitites/VideoEntity
|
||||
import {PersonEntry} from '../../../../../backend/model/sql/enitites/PersonEntry';
|
||||
import {FaceRegionEntry} from '../../../../../backend/model/sql/enitites/FaceRegionEntry';
|
||||
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
||||
import {SQLTestHelper} from '../../../SQLTestHelper';
|
||||
import {Config} from '../../../../../common/config/private/Config';
|
||||
|
||||
describe('SearchManager', () => {
|
||||
// to help WebStorm to handle the test cases
|
||||
declare let describe: any;
|
||||
declare const after: any;
|
||||
describe = SQLTestHelper.describe;
|
||||
|
||||
|
||||
const tempDir = path.join(__dirname, '../../tmp');
|
||||
const dbPath = path.join(tempDir, 'test.db');
|
||||
describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
||||
|
||||
const dir = TestHelper.getDirectoryEntry();
|
||||
const p = TestHelper.getPhotoEntry1(dir);
|
||||
@ -31,15 +30,7 @@ describe('SearchManager', () => {
|
||||
const v = TestHelper.getVideoEntry1(dir);
|
||||
|
||||
const setUpSqlDB = async () => {
|
||||
if (fs.existsSync(dbPath)) {
|
||||
fs.unlinkSync(dbPath);
|
||||
}
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir);
|
||||
}
|
||||
|
||||
Config.Server.database.type = DatabaseType.sqlite;
|
||||
Config.Server.database.sqlite.storage = dbPath;
|
||||
await sqlHelper.initDB();
|
||||
|
||||
const savePhoto = async (photo: PhotoDTO) => {
|
||||
const savedPhoto = await pr.save(photo);
|
||||
@ -66,24 +57,15 @@ describe('SearchManager', () => {
|
||||
await SQLConnection.close();
|
||||
};
|
||||
|
||||
const tearDownSqlDB = async () => {
|
||||
await SQLConnection.close();
|
||||
if (fs.existsSync(dbPath)) {
|
||||
fs.unlinkSync(dbPath);
|
||||
}
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.rmdirSync(tempDir);
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await setUpSqlDB();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await tearDownSqlDB();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await sqlHelper.clearDB();
|
||||
});
|
||||
|
||||
it('should get autocomplete', async () => {
|
||||
const sm = new SearchManager();
|
||||
@ -103,6 +85,8 @@ describe('SearchManager', () => {
|
||||
new AutoCompleteItem('wars dir', SearchTypes.directory)]);
|
||||
|
||||
expect((await sm.autocomplete('arch'))).eql([new AutoCompleteItem('Research City', SearchTypes.position)]);
|
||||
|
||||
Config.Client.Search.AutoComplete.maxItemsPerCategory = 99999;
|
||||
expect((await sm.autocomplete('a')).sort(cmp)).eql([
|
||||
new AutoCompleteItem('Boba Fett', SearchTypes.keyword),
|
||||
new AutoCompleteItem('Boba Fett', SearchTypes.person),
|
||||
@ -113,6 +97,7 @@ describe('SearchManager', () => {
|
||||
new AutoCompleteItem('Han Solo', SearchTypes.person),
|
||||
new AutoCompleteItem('death star', SearchTypes.keyword),
|
||||
new AutoCompleteItem('Padmé Amidala', SearchTypes.person),
|
||||
new AutoCompleteItem('Obivan Kenobi', SearchTypes.person),
|
||||
new AutoCompleteItem('Padmé Amidala', SearchTypes.keyword),
|
||||
new AutoCompleteItem('Natalie Portman', SearchTypes.keyword),
|
||||
new AutoCompleteItem('Han Solo\'s dice', SearchTypes.photo),
|
||||
@ -121,10 +106,24 @@ describe('SearchManager', () => {
|
||||
new AutoCompleteItem('wars dir', SearchTypes.directory),
|
||||
new AutoCompleteItem('Research City', SearchTypes.position)].sort(cmp));
|
||||
|
||||
Config.Client.Search.AutoComplete.maxItemsPerCategory = 1;
|
||||
expect((await sm.autocomplete('a')).sort(cmp)).eql([
|
||||
new AutoCompleteItem('Anakin', SearchTypes.keyword),
|
||||
new AutoCompleteItem('star wars', SearchTypes.keyword),
|
||||
new AutoCompleteItem('death star', SearchTypes.keyword),
|
||||
new AutoCompleteItem('Anakin Skywalker', SearchTypes.person),
|
||||
new AutoCompleteItem('Han Solo\'s dice', SearchTypes.photo),
|
||||
new AutoCompleteItem('Kamino', SearchTypes.position),
|
||||
new AutoCompleteItem('Research City', SearchTypes.position),
|
||||
new AutoCompleteItem('wars dir', SearchTypes.directory),
|
||||
new AutoCompleteItem('Boba Fett', SearchTypes.keyword)].sort(cmp));
|
||||
Config.Client.Search.AutoComplete.maxItemsPerCategory = 5;
|
||||
|
||||
expect((await sm.autocomplete('sw')).sort(cmp)).to.deep.equal([new AutoCompleteItem('sw1', SearchTypes.photo),
|
||||
new AutoCompleteItem('sw2', SearchTypes.photo), new AutoCompleteItem(v.name, SearchTypes.video)].sort(cmp));
|
||||
|
||||
expect((await sm.autocomplete(v.name)).sort(cmp)).to.deep.equal([new AutoCompleteItem(v.name, SearchTypes.video)]);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
@ -1,32 +1,23 @@
|
||||
import {expect} from 'chai';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {Config} from '../../../../../common/config/private/Config';
|
||||
import {DatabaseType} from '../../../../../common/config/private/IPrivateConfig';
|
||||
import {SQLConnection} from '../../../../../backend/model/sql/SQLConnection';
|
||||
import {SharingManager} from '../../../../../backend/model/sql/SharingManager';
|
||||
import {SharingDTO} from '../../../../../common/entities/SharingDTO';
|
||||
import {UserEntity} from '../../../../../backend/model/sql/enitites/UserEntity';
|
||||
import {UserDTO, UserRoles} from '../../../../../common/entities/UserDTO';
|
||||
import {SQLTestHelper} from '../../../SQLTestHelper';
|
||||
|
||||
describe('SharingManager', () => {
|
||||
// to help WebStorm to handle the test cases
|
||||
declare let describe: any;
|
||||
declare const after: any;
|
||||
describe = SQLTestHelper.describe;
|
||||
|
||||
describe('SharingManager', (sqlHelper: SQLTestHelper) => {
|
||||
|
||||
const tempDir = path.join(__dirname, '../../tmp');
|
||||
const dbPath = path.join(tempDir, 'test.db');
|
||||
|
||||
let creator: UserDTO = null;
|
||||
|
||||
const setUpSqlDB = async () => {
|
||||
if (fs.existsSync(dbPath)) {
|
||||
fs.unlinkSync(dbPath);
|
||||
}
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir);
|
||||
}
|
||||
|
||||
Config.Server.database.type = DatabaseType.sqlite;
|
||||
Config.Server.database.sqlite.storage = dbPath;
|
||||
await sqlHelper.initDB();
|
||||
|
||||
const conn = await SQLConnection.getConnection();
|
||||
|
||||
@ -41,22 +32,13 @@ describe('SharingManager', () => {
|
||||
await SQLConnection.close();
|
||||
};
|
||||
|
||||
const teardownUpSqlDB = async () => {
|
||||
await SQLConnection.close();
|
||||
if (fs.existsSync(dbPath)) {
|
||||
fs.unlinkSync(dbPath);
|
||||
}
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.rmdirSync(tempDir);
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await setUpSqlDB();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await teardownUpSqlDB();
|
||||
after(async () => {
|
||||
await sqlHelper.clearDB();
|
||||
});
|
||||
|
||||
|
||||
|
@ -258,7 +258,7 @@ export class TestHelper {
|
||||
name: rndStr() + '.jpg',
|
||||
directory: dir,
|
||||
metadata: m,
|
||||
readyThumbnails: null,
|
||||
readyThumbnails: [],
|
||||
readyIcon: false
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user