Files
immich-folio/lib/__tests__/assetLocation.test.ts

90 lines
3.4 KiB
TypeScript
Raw Permalink Normal View History

import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { LocationPrecision } from '@/lib/mapPrecision';
/**
* The per-album `location:` setting governed the map and nothing else, so an
* album asking to be absent from it still had its city named in the lightbox
* info panel. These pin the join: an asset inherits the setting of the albums
* that carry it.
*/
const albumLocationPrecision: Record<string, LocationPrecision> = {};
vi.mock('@/lib/config', async () => {
const actual = await vi.importActual<typeof import('@/lib/config')>('@/lib/config');
return {
...actual,
getConfig: () => ({ albumLocationPrecision }),
};
});
const getAlbum = vi.fn();
vi.mock('@/lib/immich', () => ({
immich: { getAlbum: (id: string) => getAlbum(id) },
}));
import { assetLocationPrecision } from '@/lib/assetLocation';
const ASSET = 'asset-1';
const OTHER = 'asset-2';
/** An album whose assets are just ids. */
const album = (id: string, assetIds: string[]) => ({
id,
assets: assetIds.map((a) => ({ id: a })),
});
beforeEach(() => {
for (const key of Object.keys(albumLocationPrecision)) delete albumLocationPrecision[key];
getAlbum.mockReset();
});
describe('assetLocationPrecision', () => {
it('is exact when no album restricts anything, without asking Immich at all', async () => {
await expect(assetLocationPrecision(ASSET)).resolves.toBe('exact');
// The common case must not cost a single album fetch.
expect(getAlbum).not.toHaveBeenCalled();
});
it('takes the setting of an album that carries the asset', async () => {
albumLocationPrecision['album-a'] = 'country';
getAlbum.mockResolvedValue(album('album-a', [ASSET]));
await expect(assetLocationPrecision(ASSET)).resolves.toBe('country');
});
it('ignores an album that restricts but does not carry the asset', async () => {
albumLocationPrecision['album-a'] = 'hidden';
getAlbum.mockResolvedValue(album('album-a', [OTHER]));
await expect(assetLocationPrecision(ASSET)).resolves.toBe('exact');
});
// Same rule as a merged map marker: the cautious setting governs.
it('takes the strictest when several albums carry the asset', async () => {
albumLocationPrecision['album-a'] = 'city';
albumLocationPrecision['album-b'] = 'hidden';
getAlbum.mockImplementation(async (id: string) => album(id, [ASSET]));
await expect(assetLocationPrecision(ASSET)).resolves.toBe('hidden');
});
it('only asks about albums that actually restrict something', async () => {
albumLocationPrecision['album-a'] = 'exact';
albumLocationPrecision['album-b'] = 'city';
getAlbum.mockImplementation(async (id: string) => album(id, [ASSET]));
await assetLocationPrecision(ASSET);
expect(getAlbum).toHaveBeenCalledTimes(1);
expect(getAlbum).toHaveBeenCalledWith('album-b');
});
// Failing open would publish the position the setting exists to withhold.
it('withholds the place when an album cannot be read', async () => {
albumLocationPrecision['album-a'] = 'city';
getAlbum.mockRejectedValue(new Error('Immich is down'));
await expect(assetLocationPrecision(ASSET)).resolves.toBe('hidden');
});
it('withholds it when the album is gone from the allowlist', async () => {
albumLocationPrecision['album-a'] = 'city';
getAlbum.mockResolvedValue(null);
await expect(assetLocationPrecision(ASSET)).resolves.toBe('hidden');
});
});