• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bpatrik / pigallery2 / 19797917245

30 Nov 2025 11:01AM UTC coverage: 68.883% (+0.01%) from 68.872%
19797917245

push

github

bpatrik
fixing tests

1452 of 2359 branches covered (61.55%)

Branch coverage included in aggregate %.

5273 of 7404 relevant lines covered (71.22%)

4169.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.62
/src/backend/model/database/AlbumManager.ts
1
import {SQLConnection} from './SQLConnection';
1✔
2
import {AlbumBaseEntity} from './enitites/album/AlbumBaseEntity';
1✔
3
import {AlbumBaseDTO} from '../../../common/entities/album/AlbumBaseDTO';
4
import {ObjectManagers} from '../ObjectManagers';
1✔
5
import {SearchQueryDTO} from '../../../common/entities/SearchQueryDTO';
6
import {SavedSearchEntity} from './enitites/album/SavedSearchEntity';
1✔
7
import {Logger} from '../../Logger';
1✔
8
import {SessionContext} from '../SessionContext';
9
import {ProjectedAlbumCacheEntity} from './enitites/album/ProjectedAlbumCacheEntity';
1✔
10
import {ProjectionAwareManager} from './ProjectionAwareManager';
1✔
11

12
const LOG_TAG = '[AlbumManager]';
1✔
13

14
export class AlbumManager extends ProjectionAwareManager<AlbumBaseEntity> {
1✔
15

16
  public async addIfNotExistSavedSearch(
17
    name: string,
18
    searchQuery: SearchQueryDTO,
19
    lockedAlbum: boolean
20
  ): Promise<void> {
21
    const connection = await SQLConnection.getConnection();
14✔
22
    const album = await connection
14✔
23
      .getRepository(SavedSearchEntity)
24
      .findOneBy({name, searchQuery});
25
    if (album) {
14!
26
      return;
×
27
    }
28
    await this.addSavedSearch(name, searchQuery, lockedAlbum);
14✔
29
  }
30

31
  public async addSavedSearch(
32
    name: string,
33
    searchQuery: SearchQueryDTO,
34
    lockedAlbum?: boolean
35
  ): Promise<void> {
36
    const connection = await SQLConnection.getConnection();
38✔
37
    await connection
38✔
38
      .getRepository(SavedSearchEntity)
39
      .save({name, searchQuery, locked: lockedAlbum});
40
  }
41

42
  public async deleteAlbum(id: number): Promise<void> {
43
    const connection = await SQLConnection.getConnection();
4✔
44

45
    if (
4✔
46
      (await connection
47
        .getRepository(AlbumBaseEntity)
48
        .countBy({id, locked: false})) !== 1
49
    ) {
50
      throw new Error('Could not delete album, id:' + id);
2✔
51
    }
52

53
    await connection
2✔
54
      .getRepository(AlbumBaseEntity)
55
      .delete({id, locked: false});
56
  }
57

58

59
  protected async loadEntities(session: SessionContext): Promise<AlbumBaseEntity[]> {
60
    await this.updateAlbums(session);
24✔
61
    const connection = await SQLConnection.getConnection();
24✔
62

63
    // Return albums with projected cache data
64
    const result = await connection
24✔
65
      .getRepository(AlbumBaseEntity)
66
      .createQueryBuilder('album')
67
      .leftJoin('album.cache', 'cache', 'cache.projectionKey = :pk AND cache.valid = 1', {pk: session.user.projectionKey})
68
      .leftJoin('cache.cover', 'cover')
69
      .leftJoin('cover.directory', 'directory')
70
      .select(['album', 'cache', 'cover.name', 'directory.name', 'directory.path'])
71
      .getMany();
72

73
    return result;
24✔
74
  }
75

76
  protected async invalidateDBCache(): Promise<void> {
77
    // Invalidate all album cache entries
78
    const connection = await SQLConnection.getConnection();
112✔
79
    await connection.getRepository(ProjectedAlbumCacheEntity)
112✔
80
      .createQueryBuilder()
81
      .update()
82
      .set({valid: false})
83
      .execute();
84
  }
85

86
  async deleteAll() {
87
    const connection = await SQLConnection.getConnection();
×
88
    await connection
×
89
      .getRepository(AlbumBaseEntity)
90
      .createQueryBuilder('album')
91
      .delete()
92
      .execute();
93
  }
94

95
  private async updateAlbums(session: SessionContext): Promise<void> {
96
    Logger.debug(LOG_TAG, 'Updating derived album data');
24✔
97
    const connection = await SQLConnection.getConnection();
24✔
98
    const albums = await connection
24✔
99
      .getRepository(SavedSearchEntity)
100
      .createQueryBuilder('album')
101
      .leftJoinAndSelect('album.cache', 'cache', 'cache.projectionKey = :pk AND cache.valid = 1', {pk: session.user.projectionKey})
102
      .getMany();
103

104
    for (const a of albums) {
24✔
105
      if (a.cache?.valid === true) {
34!
106
        continue;
×
107
      }
108
      await ObjectManagers.getInstance().ProjectedCacheManager
34✔
109
        .setAndGetCacheForAlbum(connection, session, {
110
          id: a.id,
111
          searchQuery: a.searchQuery
112
        });
113
      // giving back the control to the main event loop (Macrotask queue)
114
      // https://blog.insiderattack.net/promises-next-ticks-and-immediates-nodejs-event-loop-part-3-9226cbe7a6aa
115
      await new Promise(setImmediate);
34✔
116
    }
117
  }
118

119
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc