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

bpatrik / pigallery2 / 19937158142

04 Dec 2025 05:01PM UTC coverage: 68.847% (-0.002%) from 68.849%
19937158142

push

github

bpatrik
Fixing tests #1087

1452 of 2364 branches covered (61.42%)

Branch coverage included in aggregate %.

5284 of 7420 relevant lines covered (71.21%)

4160.13 hits per line

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

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

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

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

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

43
  public async deleteAlbum(id: number): Promise<void> {
44
    const connection = await SQLConnection.getConnection();
4✔
45
    const albumCount = await connection
4✔
46
      .getRepository(AlbumBaseEntity)
47
      .countBy({id, locked: false});
48

49
    if (albumCount == 0) {
4✔
50
      throw new Error(`Could not delete album, id: ${id}. Album id is not found or the album is locked.`);
2✔
51
    }
52

53
    if (albumCount > 1) {
2!
54
      throw new Error(`Could not delete album, id: ${id}. DB is inconsistent. More than one album is found with the given id.`);
×
55
    }
56

57
    await connection
2✔
58
      .getRepository(AlbumBaseEntity)
59
      .delete({id, locked: false});
60

61
    this.resetMemoryCache();
2✔
62
  }
63

64
  async deleteAll() {
65
    const connection = await SQLConnection.getConnection();
×
66
    await connection
×
67
      .getRepository(AlbumBaseEntity)
68
      .createQueryBuilder('album')
69
      .delete()
70
      .execute();
71
    this.resetMemoryCache();
×
72
  }
73

74
  protected async loadEntities(session: SessionContext): Promise<AlbumBaseEntity[]> {
75
    await this.updateAlbums(session);
24✔
76
    const connection = await SQLConnection.getConnection();
24✔
77

78
    // Return albums with projected cache data
79
    const result = await connection
24✔
80
      .getRepository(AlbumBaseEntity)
81
      .createQueryBuilder('album')
82
      .leftJoin('album.cache', 'cache', 'cache.projectionKey = :pk AND cache.valid = 1', {pk: session.user.projectionKey})
83
      .leftJoin('cache.cover', 'cover')
84
      .leftJoin('cover.directory', 'directory')
85
      .select(['album', 'cache', 'cover.name', 'directory.name', 'directory.path'])
86
      .getMany();
87

88
    return result;
24✔
89
  }
90

91
  protected async invalidateDBCache(): Promise<void> {
92
    // Invalidate all album cache entries
93
    const connection = await SQLConnection.getConnection();
112✔
94
    await connection.getRepository(ProjectedAlbumCacheEntity)
112✔
95
      .createQueryBuilder()
96
      .update()
97
      .set({valid: false})
98
      .execute();
99
  }
100

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

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

126
}
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