1301 lines
46 KiB
TypeScript
1301 lines
46 KiB
TypeScript
import { assert, describe, expect, it } from 'vitest';
|
|
import { mock } from 'vitest-mock-extended';
|
|
|
|
import type { CameraManager } from '../../src/camera-manager/manager';
|
|
import type { CameraManagerStore } from '../../src/camera-manager/store';
|
|
import { QueryType } from '../../src/camera-manager/types';
|
|
import type { FoldersManager } from '../../src/card-controller/folders/manager';
|
|
import type {
|
|
FolderPathComponent,
|
|
FolderQuery,
|
|
} from '../../src/card-controller/folders/types';
|
|
import type { FolderConfig } from '../../src/config/schema/folders';
|
|
import { QuerySource } from '../../src/query-source';
|
|
import {
|
|
MediaTypeSpec,
|
|
UnifiedQueryBuilder,
|
|
} from '../../src/view/unified-query-builder';
|
|
import { createCapabilities } from '../camera-manager/test-utils';
|
|
import { createCameraConfig } from '../config/test-utils';
|
|
import { createFolder } from '../test-utils';
|
|
import { isRecordingQuery, isReviewQuery } from './test-utils';
|
|
|
|
// Helper to create FolderQuery for tests
|
|
const createFolderQueryParams = (
|
|
folder: FolderConfig,
|
|
path: [FolderPathComponent, ...FolderPathComponent[]],
|
|
): FolderQuery => ({
|
|
source: QuerySource.Folder,
|
|
folder,
|
|
path,
|
|
});
|
|
|
|
const createMocks = () => {
|
|
const cameraManager = mock<CameraManager>();
|
|
const foldersManager = mock<FoldersManager>();
|
|
const store = mock<CameraManagerStore>();
|
|
|
|
cameraManager.getStore.mockReturnValue(store);
|
|
cameraManager.getDefaultQueryParameters.mockReturnValue({});
|
|
|
|
store.getCameraIDs.mockReturnValue(new Set());
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set());
|
|
store.getAllDependentCameras.mockReturnValue(new Set());
|
|
store.getCameraConfig.mockReturnValue(createCameraConfig());
|
|
|
|
// By default, make getCameraCapabilities return a set of capabilities that
|
|
// matches everything, unless the test mocks it otherwise.
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({
|
|
clips: true,
|
|
snapshots: true,
|
|
recordings: true,
|
|
reviews: true,
|
|
}),
|
|
);
|
|
|
|
return { cameraManager, foldersManager, store };
|
|
};
|
|
|
|
describe('MediaTypeSpec', () => {
|
|
describe('factory helpers', () => {
|
|
it('clips() returns correct spec', () => {
|
|
expect(MediaTypeSpec.clips()).toEqual({
|
|
mediaType: 'events',
|
|
eventsSubtype: 'clips',
|
|
});
|
|
});
|
|
|
|
it('snapshots() returns correct spec', () => {
|
|
expect(MediaTypeSpec.snapshots()).toEqual({
|
|
mediaType: 'events',
|
|
eventsSubtype: 'snapshots',
|
|
});
|
|
});
|
|
|
|
it('recordings() returns correct spec', () => {
|
|
expect(MediaTypeSpec.recordings()).toEqual({ mediaType: 'recordings' });
|
|
});
|
|
|
|
it('reviews() returns correct spec', () => {
|
|
expect(MediaTypeSpec.reviews()).toEqual({ mediaType: 'reviews' });
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('UnifiedQueryBuilder', () => {
|
|
describe('buildClipsQuery', () => {
|
|
it('should return query with no nodes when empty camera IDs', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildClipsQuery(new Set());
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should build clips query with hasClip=true', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildClipsQuery(new Set(['camera1']));
|
|
|
|
assert(query);
|
|
expect(query.getNodes()).toHaveLength(1);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
source: QuerySource.Camera,
|
|
type: QueryType.Event,
|
|
hasClip: true,
|
|
cameraIDs: new Set(['camera1']),
|
|
});
|
|
expect(query.getNodes()[0]).not.toHaveProperty('hasSnapshot');
|
|
});
|
|
|
|
it('should apply all MediaQueryBuildOptions', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const start = new Date('2024-01-01');
|
|
const end = new Date('2024-01-02');
|
|
|
|
const query = builder.buildClipsQuery(new Set(['camera1']), {
|
|
start,
|
|
end,
|
|
limit: 25,
|
|
});
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
start,
|
|
end,
|
|
limit: 25,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('buildSnapshotsQuery', () => {
|
|
it('should build snapshots query with hasSnapshot=true', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildSnapshotsQuery(new Set(['camera1']));
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasSnapshot: true,
|
|
});
|
|
expect(query.getNodes()[0]).not.toHaveProperty('hasClip');
|
|
});
|
|
});
|
|
|
|
describe('buildEventsQuery', () => {
|
|
it('should build events query without clip/snapshot filter', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildEventsQuery(new Set(['camera1']));
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({ type: QueryType.Event });
|
|
expect(query.getNodes()[0]).not.toHaveProperty('hasClip');
|
|
expect(query.getNodes()[0]).not.toHaveProperty('hasSnapshot');
|
|
});
|
|
});
|
|
|
|
describe('buildRecordingsQuery', () => {
|
|
it('should return null if no camera IDs provided', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
expect(builder.buildRecordingsQuery(new Set())).toBeNull();
|
|
});
|
|
|
|
it('should build recordings query', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildRecordingsQuery(new Set(['camera1']));
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Recording,
|
|
cameraIDs: new Set(['camera1']),
|
|
});
|
|
});
|
|
|
|
it('should apply options to recordings query', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildRecordingsQuery(new Set(['camera1']), {
|
|
limit: 10,
|
|
});
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Recording,
|
|
limit: 10,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('buildReviewsQuery', () => {
|
|
it('should return query with no nodes when empty camera IDs', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildReviewsQuery(new Set());
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should build reviews query', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildReviewsQuery(new Set(['camera1']));
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({ type: QueryType.Review });
|
|
});
|
|
|
|
it('should apply reviewed option', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildReviewsQuery(new Set(['camera1']), {
|
|
reviewed: true,
|
|
});
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Review,
|
|
reviewed: true,
|
|
});
|
|
});
|
|
|
|
it('should apply reviewed=false option', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildReviewsQuery(new Set(['camera1']), {
|
|
reviewed: false,
|
|
});
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Review,
|
|
reviewed: false,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('getAllMediaCapableCameraIDs', () => {
|
|
it('should return cameras with any media capability', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set(['camera1', 'camera2']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const result = builder.getAllMediaCapableCameraIDs();
|
|
|
|
expect(result).toEqual(new Set(['camera1', 'camera2']));
|
|
expect(store.getCameraIDsWithCapability).toHaveBeenCalledWith({
|
|
anyCapabilities: ['clips', 'snapshots', 'recordings', 'reviews'],
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('buildFilterQuery', () => {
|
|
it('should use default cameras when null provided', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set(['camera.office']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildFilterQuery(null, new Set(['clips']));
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
cameraIDs: new Set(['camera.office']),
|
|
});
|
|
});
|
|
|
|
it('should use default media types when null provided', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set(['camera.office']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildFilterQuery(new Set(['camera.office']), null);
|
|
|
|
assert(query);
|
|
|
|
// clips, snapshots, recordings, reviews
|
|
expect(query.getNodes()).toHaveLength(4);
|
|
});
|
|
|
|
it('should build clips filter query', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['clips']),
|
|
);
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasClip: true,
|
|
});
|
|
});
|
|
|
|
it('should build snapshots filter query', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['snapshots']),
|
|
);
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasSnapshot: true,
|
|
});
|
|
});
|
|
|
|
it('should build recordings filter query', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['recordings']),
|
|
);
|
|
|
|
assert(query);
|
|
assert(isRecordingQuery(query.getNodes()[0]));
|
|
});
|
|
|
|
it('should build reviews filter query', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['reviews']),
|
|
);
|
|
|
|
assert(query);
|
|
assert(isReviewQuery(query.getNodes()[0]));
|
|
});
|
|
|
|
it('should apply all filter options', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const start = new Date('2024-01-01');
|
|
const end = new Date('2024-01-02');
|
|
const tags = new Set(['tag1']);
|
|
const what = new Set(['person']);
|
|
const where = new Set(['zone1']);
|
|
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['clips']),
|
|
{
|
|
start,
|
|
end,
|
|
limit: 5,
|
|
favorite: true,
|
|
tags,
|
|
what,
|
|
where,
|
|
reviewed: false,
|
|
},
|
|
);
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
start,
|
|
end,
|
|
limit: 5,
|
|
favorite: true,
|
|
tags,
|
|
what,
|
|
where,
|
|
reviewed: false,
|
|
});
|
|
});
|
|
|
|
it('should apply reviewed option to reviews query', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['reviews']),
|
|
{
|
|
reviewed: true,
|
|
},
|
|
);
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Review,
|
|
reviewed: true,
|
|
});
|
|
});
|
|
|
|
it('should return null when no cameras available', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set());
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildFilterQuery(new Set(), null);
|
|
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
describe('filter application', () => {
|
|
it('should apply filters to query nodes', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['clips']),
|
|
{
|
|
favorite: true,
|
|
tags: new Set(['tag']),
|
|
},
|
|
);
|
|
|
|
assert(query);
|
|
const nodes = query.getNodes();
|
|
expect(nodes).toHaveLength(1);
|
|
expect(nodes[0].favorite).toBe(true);
|
|
expect(nodes[0].tags).toEqual(new Set(['tag']));
|
|
});
|
|
|
|
it('should apply reviewed filter to reviews', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['reviews']),
|
|
{
|
|
reviewed: true,
|
|
},
|
|
);
|
|
|
|
assert(query);
|
|
const nodes = query.getNodes();
|
|
expect(nodes).toHaveLength(1);
|
|
expect(nodes[0].reviewed).toBe(true);
|
|
});
|
|
|
|
it('should apply what/where filters', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['clips']),
|
|
{
|
|
what: new Set(['person']),
|
|
where: new Set(['zone']),
|
|
},
|
|
);
|
|
|
|
assert(query);
|
|
const nodes = query.getNodes();
|
|
expect(nodes).toHaveLength(1);
|
|
expect(nodes[0].what).toEqual(new Set(['person']));
|
|
expect(nodes[0].where).toEqual(new Set(['zone']));
|
|
});
|
|
|
|
it('should apply severity filter', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
|
|
const query = builder.buildFilterQuery(
|
|
new Set(['camera.office']),
|
|
new Set(['clips']),
|
|
{
|
|
severity: new Set(['high']),
|
|
},
|
|
);
|
|
|
|
assert(query);
|
|
const nodes = query.getNodes();
|
|
expect(nodes).toHaveLength(1);
|
|
expect(nodes[0].severity).toEqual(new Set(['high']));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('buildFolderQueryWithPath', () => {
|
|
it('should build folder query', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const folder = createFolder({ id: 'folder1', title: 'Test' });
|
|
const path: [FolderPathComponent] = [{ ha: { id: 'Root' } }];
|
|
|
|
const query = builder.buildFolderQueryWithPath(folder, path);
|
|
|
|
const nodes = query.getNodes();
|
|
expect(nodes).toHaveLength(1);
|
|
expect(nodes[0]).toMatchObject({
|
|
source: QuerySource.Folder,
|
|
folder,
|
|
path,
|
|
});
|
|
});
|
|
|
|
it('should apply limit option', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const folder = createFolder({ id: 'folder1', title: 'Test' });
|
|
const path: [FolderPathComponent] = [{ ha: { id: 'Root' } }];
|
|
const query = builder.buildFolderQueryWithPath(folder, path, {
|
|
limit: 10,
|
|
});
|
|
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
source: QuerySource.Folder,
|
|
limit: 10,
|
|
});
|
|
});
|
|
|
|
it('should not include limit when not provided', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const folder = createFolder({ id: 'folder1', title: 'Test' });
|
|
const path: [FolderPathComponent] = [{ ha: { id: 'Root' } }];
|
|
|
|
const query = builder.buildFolderQueryWithPath(folder, path);
|
|
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
source: QuerySource.Folder,
|
|
});
|
|
expect(query.getNodes()[0]).not.toHaveProperty('limit');
|
|
});
|
|
});
|
|
|
|
describe('buildDefaultFolderQuery', () => {
|
|
it('should build query from folder manager params', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const folder = createFolder({ id: 'folder1', title: 'Test' });
|
|
const path: [FolderPathComponent] = [{ ha: { id: 'Root' } }];
|
|
|
|
foldersManager.getFolder.mockReturnValue(folder);
|
|
foldersManager.getDefaultQueryParameters.mockReturnValue(
|
|
createFolderQueryParams(folder, path),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultFolderQuery('folder1');
|
|
|
|
assert(query);
|
|
expect(query.hasNodes()).toBe(true);
|
|
expect(foldersManager.getFolder).toHaveBeenCalledWith('folder1');
|
|
});
|
|
|
|
it('should return empty query when folder not found', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
foldersManager.getFolder.mockReturnValue(null);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultFolderQuery('nonexistent');
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should return empty query when no default params', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const folder = createFolder({ id: 'folder1', title: 'Test' });
|
|
foldersManager.getFolder.mockReturnValue(folder);
|
|
foldersManager.getDefaultQueryParameters.mockReturnValue(null);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultFolderQuery('folder1');
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should apply limit option', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
const folder = createFolder({ id: 'folder1', title: 'Test' });
|
|
const path: [FolderPathComponent] = [{ ha: { id: 'Root' } }];
|
|
|
|
foldersManager.getFolder.mockReturnValue(folder);
|
|
foldersManager.getDefaultQueryParameters.mockReturnValue(
|
|
createFolderQueryParams(folder, path),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultFolderQuery('folder1', { limit: 20 });
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
source: QuerySource.Folder,
|
|
limit: 20,
|
|
});
|
|
});
|
|
|
|
it('should use default folder when no ID provided', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
foldersManager.getFolder.mockReturnValue(null);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
builder.buildDefaultFolderQuery();
|
|
|
|
expect(foldersManager.getFolder).toHaveBeenCalledWith(undefined);
|
|
});
|
|
});
|
|
|
|
describe('buildDefaultCameraQuery', () => {
|
|
it('should return null when no cameras available', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set());
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should use all cameras when no ID provided', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office', 'cam2']));
|
|
cameraManager.getCameraCapabilities.mockReturnValue(null);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
builder.buildDefaultCameraQuery();
|
|
|
|
expect(store.getCameraIDs).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should use dependent cameras when ID provided', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getAllDependentCameras.mockReturnValue(new Set(['camera.office', 'dep1']));
|
|
cameraManager.getCameraCapabilities.mockReturnValue(null);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
builder.buildDefaultCameraQuery('camera.office');
|
|
|
|
expect(store.getAllDependentCameras).toHaveBeenCalledWith('camera.office');
|
|
});
|
|
|
|
it('should build reviews query for camera with reviews capability', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'auto' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ reviews: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
assert(isReviewQuery(query.getNodes()[0]));
|
|
});
|
|
|
|
it('should apply reviewed filter from config for reviews query', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'auto', reviewed: 'unreviewed' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ reviews: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Review,
|
|
reviewed: false,
|
|
});
|
|
});
|
|
|
|
it('should apply reviewed=true filter from config', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'auto', reviewed: 'reviewed' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ reviews: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Review,
|
|
reviewed: true,
|
|
});
|
|
});
|
|
|
|
it('should not apply reviewed filter when config is all', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'auto', reviewed: 'all' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ reviews: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
assert(isReviewQuery(query.getNodes()[0]));
|
|
expect(query.getNodes()[0]).not.toHaveProperty('reviewed');
|
|
});
|
|
|
|
it('should not apply reviewed filter to non-review queries', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({
|
|
media: { type: 'auto', reviewed: 'unreviewed' },
|
|
}),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ clips: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasClip: true,
|
|
});
|
|
expect(query.getNodes()[0]).not.toHaveProperty('reviewed');
|
|
});
|
|
|
|
it('should build clips query for camera with clips capability', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'auto' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ clips: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasClip: true,
|
|
});
|
|
});
|
|
|
|
it('should build snapshots query for camera with only snapshots', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'auto' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ snapshots: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasSnapshot: true,
|
|
});
|
|
});
|
|
|
|
it('should build recordings query for camera with only recordings', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'auto' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ recordings: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
assert(isRecordingQuery(query.getNodes()[0]));
|
|
});
|
|
|
|
it('should respect explicit recordings media type config', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'recordings' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ recordings: true, reviews: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
assert(isRecordingQuery(query.getNodes()[0]));
|
|
});
|
|
|
|
it('should respect explicit reviews media type config', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'reviews' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ reviews: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
assert(isReviewQuery(query.getNodes()[0]));
|
|
});
|
|
|
|
it('should handle explicit events type with clips', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'events', events_type: 'clips' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ clips: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasClip: true,
|
|
});
|
|
});
|
|
|
|
it('should handle explicit events type with snapshots', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'events', events_type: 'snapshots' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ snapshots: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasSnapshot: true,
|
|
});
|
|
});
|
|
|
|
it('should handle events_type=all with both capabilities', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'events', events_type: 'all' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ clips: true, snapshots: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({ type: QueryType.Event });
|
|
// Both capabilities present with 'all' means no specific filter
|
|
expect(query.getNodes()[0]).not.toHaveProperty('hasClip');
|
|
expect(query.getNodes()[0]).not.toHaveProperty('hasSnapshot');
|
|
});
|
|
|
|
it('should handle events_type=all with only clips', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'events', events_type: 'all' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ clips: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasClip: true,
|
|
});
|
|
});
|
|
|
|
it('should handle events_type=all with only snapshots', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'events', events_type: 'all' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ snapshots: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasSnapshot: true,
|
|
});
|
|
});
|
|
|
|
it('should apply limit option', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'recordings' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(
|
|
createCapabilities({ recordings: true }),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery(undefined, { limit: 15 });
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Recording,
|
|
limit: 15,
|
|
});
|
|
});
|
|
|
|
it('should return null when camera has no capabilities', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
cameraManager.getCameraCapabilities.mockReturnValue(null);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should return null for auto mode when no matching capabilities', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'auto' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(createCapabilities());
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should build folder query for folder media type', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'folder', folders: ['folder1'] } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(createCapabilities());
|
|
|
|
const folder = createFolder({ id: 'folder1', title: 'Test Folder' });
|
|
const path: [FolderPathComponent] = [{ ha: { id: 'Root' } }];
|
|
|
|
foldersManager.getFolder.mockReturnValue(folder);
|
|
foldersManager.getDefaultQueryParameters.mockReturnValue(
|
|
createFolderQueryParams(folder, path),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
source: QuerySource.Folder,
|
|
folder: expect.objectContaining({ id: 'folder1' }),
|
|
});
|
|
});
|
|
|
|
it('should build default folder query for folder media type without specific folders', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'folder' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(createCapabilities());
|
|
|
|
const folder = createFolder({ id: 'default-folder', title: 'Default' });
|
|
const path: [FolderPathComponent] = [{ ha: { id: 'Root' } }];
|
|
|
|
// Mock getting the default folder (id undefined)
|
|
foldersManager.getFolder.calledWith(undefined).mockReturnValue(folder);
|
|
foldersManager.getDefaultQueryParameters.mockReturnValue(
|
|
createFolderQueryParams(folder, path),
|
|
);
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
source: QuerySource.Folder,
|
|
folder: expect.objectContaining({ id: 'default-folder' }),
|
|
});
|
|
});
|
|
|
|
it('should return null when events type specified but no clips/snapshots capability', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'events', events_type: 'clips' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(createCapabilities());
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should return null when recordings type specified but no recordings capability', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'recordings' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(createCapabilities());
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should return null when reviews type specified but no reviews capability', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({ media: { type: 'reviews' } }),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockReturnValue(createCapabilities());
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
expect(query).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('buildMediaQuery', () => {
|
|
it('should build clips query', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set(['camera.office']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('events', { eventsSubtype: 'clips' });
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasClip: true,
|
|
});
|
|
});
|
|
|
|
it('should build snapshots query', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set(['camera.office']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('events', {
|
|
eventsSubtype: 'snapshots',
|
|
});
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
hasSnapshot: true,
|
|
});
|
|
});
|
|
|
|
it('should build recordings query', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set(['camera.office']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('recordings');
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({ type: QueryType.Recording });
|
|
});
|
|
|
|
it('should build reviews query', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set(['camera.office']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('reviews');
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({ type: QueryType.Review });
|
|
});
|
|
|
|
it('should use dependent cameras when cameraID provided', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getAllDependentCameras.mockReturnValue(new Set(['camera.office', 'dep1']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('events', {
|
|
cameraID: 'camera.office',
|
|
eventsSubtype: 'clips',
|
|
});
|
|
|
|
assert(query);
|
|
expect(store.getAllDependentCameras).toHaveBeenCalledWith(
|
|
'camera.office',
|
|
'clips',
|
|
);
|
|
});
|
|
|
|
it('should apply limit option', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set(['camera.office']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('recordings', {
|
|
limit: 30,
|
|
});
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Recording,
|
|
limit: 30,
|
|
});
|
|
});
|
|
|
|
it('should return null when no capable cameras', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set());
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('events', { eventsSubtype: 'clips' });
|
|
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should return null for folder media type', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('folder');
|
|
|
|
expect(query).toBeNull();
|
|
});
|
|
|
|
it('should handle events without subtype', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(new Set(['camera.office']));
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('events');
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({ type: QueryType.Event });
|
|
expect(query.getNodes()[0]).not.toHaveProperty('hasClip');
|
|
expect(query.getNodes()[0]).not.toHaveProperty('hasSnapshot');
|
|
|
|
expect(store.getCameraIDsWithCapability).toHaveBeenCalledWith({
|
|
anyCapabilities: ['clips', 'snapshots'],
|
|
});
|
|
});
|
|
|
|
it('should skip cameras without capabilities', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDsWithCapability.mockReturnValue(
|
|
new Set(['capable', 'not-capable']),
|
|
);
|
|
|
|
cameraManager.getCameraCapabilities.mockImplementation((id) => {
|
|
if (id === 'capable') {
|
|
return createCapabilities({ clips: true });
|
|
}
|
|
return null;
|
|
});
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildCameraMediaQuery('events', { eventsSubtype: 'clips' });
|
|
|
|
assert(query);
|
|
expect(query.getNodes()).toHaveLength(1);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
cameraIDs: new Set(['capable']),
|
|
});
|
|
});
|
|
|
|
it('should build folder query when camera media type is folder', () => {
|
|
const { cameraManager, foldersManager, store } = createMocks();
|
|
store.getCameraIDs.mockReturnValue(new Set(['cam1']));
|
|
store.getCameraConfig.mockReturnValue(
|
|
createCameraConfig({
|
|
media: { type: 'folder', folders: ['good', 'bad'] },
|
|
}),
|
|
);
|
|
|
|
foldersManager.getFolder.mockImplementation((id) => {
|
|
if (id === 'good') {
|
|
return createFolder({ id });
|
|
}
|
|
return null;
|
|
});
|
|
foldersManager.getDefaultQueryParameters.mockImplementation((folder) => {
|
|
return folder ? createFolderQueryParams(folder, [{ ha: { id: 'id' } }]) : null;
|
|
});
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildDefaultCameraQuery();
|
|
|
|
assert(query);
|
|
|
|
// Expected result: good included, bad skipped.
|
|
expect(query.getNodes()).toHaveLength(1);
|
|
expect(query.getFolderQueries('good')).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe('default parameters merging', () => {
|
|
it('should merge what/where from multiple cameras', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
cameraManager.getDefaultQueryParameters.mockImplementation((id) => {
|
|
if (id === 'camera.office') {
|
|
return { what: new Set(['person']) };
|
|
}
|
|
if (id === 'cam2') {
|
|
return { what: new Set(['car']), where: new Set(['driveway']) };
|
|
}
|
|
return {};
|
|
});
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildClipsQuery(new Set(['camera.office', 'cam2']));
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({
|
|
type: QueryType.Event,
|
|
what: new Set(['person', 'car']),
|
|
where: new Set(['driveway']),
|
|
});
|
|
});
|
|
|
|
it('should not include what/where when empty', () => {
|
|
const { cameraManager, foldersManager } = createMocks();
|
|
|
|
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
|
const query = builder.buildClipsQuery(new Set(['camera.office']));
|
|
|
|
assert(query);
|
|
expect(query.getNodes()[0]).toMatchObject({ type: QueryType.Event });
|
|
|
|
expect(query.getNodes()[0]).not.toHaveProperty('what');
|
|
expect(query.getNodes()[0]).not.toHaveProperty('where');
|
|
});
|
|
});
|
|
});
|