fix(server): make library exclusion matching consistently case-insensitive (#31973)

fix(server): make library exclusion matching consistently case-insensitive (#31970)

(cherry picked from commit de6dcf669c498192ce73b181ec6d5de5d844b03a)

Co-authored-by: Jonathan Jogenfors <jonathan@jogenfors.se>
This commit is contained in:
immich-push-o-matic[bot]
2026-10-01 10:26:56 +00:00
committed by GitHub
co-authored by Jonathan Jogenfors
parent d2136aae98
commit 84fb51a110
5 changed files with 55 additions and 6 deletions
@@ -476,6 +476,50 @@ describe(LibraryService.name, () => {
await expect(ctx.getAssetPaths(library.id)).resolves.toEqual([]);
});
// https://github.com/immich-app/immich/issues/23619
it.each([
{ pattern: '**/*.{arw,ARW,dng,DNG}', extensions: ['arw', 'ARW', 'dng', 'DNG'] },
{ pattern: '**/*.{arw,dng}', extensions: ['arw', 'ARW', 'ArW', 'dng', 'DNG', 'DnG'] },
{ pattern: '**/*.ARW', extensions: ['arw', 'ARW', 'ArW'] },
{ pattern: '**/*.{tif,jpg}', extensions: ['tif', 'TIF', 'TiF', 'jpg', 'JPG', 'JpG'] },
])(
'should offline existing assets covered by $pattern regardless of extension case',
async ({ pattern, extensions }) => {
const { sut, ctx } = setup();
const excludedAssets = await Promise.all(
extensions.map((extension, index) => createFile(join(importRoot, 'nested', `asset${index}.${extension}`))),
);
const includedAsset = await createFile(join(importRoot, 'included.png'));
const library = await ctx.createLibrary({ importPaths: [importRoot] });
await ctx.scan(library.id);
await expect(ctx.getAssetPaths(library.id)).resolves.toEqual([...excludedAssets, includedAsset].sort());
await sut.update(library.id, { exclusionPatterns: [pattern] });
await ctx.scan(library.id);
await expect(ctx.getAssetPaths(library.id)).resolves.toEqual([includedAsset]);
const excluded = await ctx.database
.selectFrom('asset')
.select(['originalPath', 'isOffline', 'deletedAt'])
.where('libraryId', '=', library.id)
.where('originalPath', 'in', excludedAssets)
.orderBy('originalPath')
.execute();
expect(excluded).toEqual(
excludedAssets.sort().map((originalPath) => ({
originalPath,
isOffline: true,
deletedAt: expect.any(Date),
})),
);
await ctx.scan(library.id);
await expect(ctx.getAssetPaths(library.id)).resolves.toEqual([includedAsset]);
},
);
// https://github.com/immich-app/immich/issues/17121
it('should respect exclusion patterns when using multiple import paths', async () => {
const { sut, ctx } = setup();