@@ -4,7 +4,6 @@ import { Camera } from '../../src/camera-manager/camera.js';
|
||||
import { GenericCameraManagerEngine } from '../../src/camera-manager/generic/engine-generic.js';
|
||||
import { CameraProxyConfig } from '../../src/camera-manager/types.js';
|
||||
import { StateWatcherSubscriptionInterface } from '../../src/card-controller/hass/state-watcher.js';
|
||||
import { ProxyConfig } from '../../src/config/schema/cameras.js';
|
||||
import {
|
||||
callStateWatcherCallback,
|
||||
createCameraConfig,
|
||||
@@ -179,6 +178,7 @@ describe('Camera', () => {
|
||||
{},
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
@@ -186,9 +186,10 @@ describe('Camera', () => {
|
||||
],
|
||||
[
|
||||
'when media set to true',
|
||||
{ media: true },
|
||||
{ proxy: { media: true } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
@@ -196,9 +197,10 @@ describe('Camera', () => {
|
||||
],
|
||||
[
|
||||
'when media set to false',
|
||||
{ media: false as const },
|
||||
{ proxy: { media: false as const } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
@@ -206,9 +208,10 @@ describe('Camera', () => {
|
||||
],
|
||||
[
|
||||
'when media set to auto',
|
||||
{ media: 'auto' as const },
|
||||
{ proxy: { media: 'auto' as const } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
@@ -216,9 +219,10 @@ describe('Camera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_verification is set to auto',
|
||||
{ ssl_verification: 'auto' as const },
|
||||
{ proxy: { ssl_verification: 'auto' as const } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
@@ -226,9 +230,10 @@ describe('Camera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_verification is set to true',
|
||||
{ ssl_verification: true },
|
||||
{ proxy: { ssl_verification: true } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
@@ -236,9 +241,10 @@ describe('Camera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_verification is set to false',
|
||||
{ ssl_verification: false },
|
||||
{ proxy: { ssl_verification: false } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'default' as const,
|
||||
@@ -246,9 +252,10 @@ describe('Camera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_ciphers is set to auto',
|
||||
{ ssl_ciphers: 'auto' as const },
|
||||
{ proxy: { ssl_ciphers: 'auto' as const } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
@@ -256,9 +263,10 @@ describe('Camera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_ciphers is set to modern',
|
||||
{ ssl_ciphers: 'modern' as const },
|
||||
{ proxy: { ssl_ciphers: 'modern' as const } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'modern' as const,
|
||||
@@ -266,9 +274,47 @@ describe('Camera', () => {
|
||||
],
|
||||
[
|
||||
'when dynamic is set to false',
|
||||
{ dynamic: false },
|
||||
{ proxy: { dynamic: false } },
|
||||
{
|
||||
dynamic: false,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
},
|
||||
],
|
||||
[
|
||||
'when go2rtc has no url',
|
||||
{ live_provider: 'go2rtc' },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
},
|
||||
],
|
||||
[
|
||||
'when go2rtc has a url',
|
||||
{ live_provider: 'go2rtc', go2rtc: { url: 'http://localhost:1984' } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: true,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
},
|
||||
],
|
||||
[
|
||||
'when go2rtc has a url but live is set to false',
|
||||
{
|
||||
live_provider: 'go2rtc',
|
||||
go2rtc: { url: 'http://localhost:1984' },
|
||||
proxy: { live: false },
|
||||
},
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default' as const,
|
||||
@@ -276,15 +322,9 @@ describe('Camera', () => {
|
||||
],
|
||||
])(
|
||||
'%s',
|
||||
(
|
||||
_name: string,
|
||||
proxyConfig: Partial<ProxyConfig>,
|
||||
expectedResult: CameraProxyConfig,
|
||||
) => {
|
||||
(_name: string, cameraConfig: unknown, expectedResult: CameraProxyConfig) => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
proxy: proxyConfig,
|
||||
}),
|
||||
createCameraConfig(cameraConfig),
|
||||
new GenericCameraManagerEngine(mock<StateWatcherSubscriptionInterface>()),
|
||||
);
|
||||
expect(camera.getProxyConfig()).toEqual(expectedResult);
|
||||
|
||||
@@ -5,7 +5,6 @@ import { ReolinkCamera } from '../../../src/camera-manager/reolink/camera';
|
||||
import { CameraProxyConfig } from '../../../src/camera-manager/types';
|
||||
import { ActionsExecutor } from '../../../src/card-controller/actions/types';
|
||||
import { StateWatcher } from '../../../src/card-controller/hass/state-watcher';
|
||||
import { ProxyConfig } from '../../../src/config/schema/cameras';
|
||||
import { EntityRegistryManagerLive } from '../../../src/ha/registry/entity';
|
||||
import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock';
|
||||
import {
|
||||
@@ -341,6 +340,7 @@ describe('ReolinkCamera', () => {
|
||||
{},
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
@@ -348,9 +348,10 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
[
|
||||
'when media set to on',
|
||||
{ media: true },
|
||||
{ proxy: { media: true } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
@@ -358,9 +359,10 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
[
|
||||
'when media set to off',
|
||||
{ media: false },
|
||||
{ proxy: { media: false } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: false,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
@@ -368,9 +370,10 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
[
|
||||
'when media set to auto',
|
||||
{ media: 'auto' as const },
|
||||
{ proxy: { media: 'auto' as const } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
@@ -378,9 +381,10 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_verification is set to auto',
|
||||
{ ssl_verification: 'auto' as const },
|
||||
{ proxy: { ssl_verification: 'auto' as const } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
@@ -388,9 +392,10 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_verification is set to true',
|
||||
{ ssl_verification: true },
|
||||
{ proxy: { ssl_verification: true } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
@@ -398,9 +403,10 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_verification is set to false',
|
||||
{ ssl_verification: false },
|
||||
{ proxy: { ssl_verification: false } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
@@ -408,9 +414,10 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_ciphers is set to auto',
|
||||
{ ssl_ciphers: 'auto' as const },
|
||||
{ proxy: { ssl_ciphers: 'auto' as const } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
@@ -418,9 +425,10 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
[
|
||||
'when ssl_ciphers is set to modern',
|
||||
{ ssl_ciphers: 'modern' as const },
|
||||
{ proxy: { ssl_ciphers: 'modern' as const } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'modern' as const,
|
||||
@@ -428,9 +436,47 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
[
|
||||
'when dynamic is set to false',
|
||||
{ dynamic: false },
|
||||
{ proxy: { dynamic: false } },
|
||||
{
|
||||
dynamic: false,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
},
|
||||
],
|
||||
[
|
||||
'when go2rtc has no url',
|
||||
{ live_provider: 'go2rtc' },
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
},
|
||||
],
|
||||
[
|
||||
'when go2rtc has a url',
|
||||
{ live_provider: 'go2rtc', go2rtc: { url: 'http://localhost:1984' } },
|
||||
{
|
||||
dynamic: true,
|
||||
live: true,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
},
|
||||
],
|
||||
[
|
||||
'when go2rtc has a url but live is set to false',
|
||||
{
|
||||
live_provider: 'go2rtc',
|
||||
go2rtc: { url: 'http://localhost:1984' },
|
||||
proxy: { live: false },
|
||||
},
|
||||
{
|
||||
dynamic: true,
|
||||
live: false,
|
||||
media: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'intermediate' as const,
|
||||
@@ -438,15 +484,9 @@ describe('ReolinkCamera', () => {
|
||||
],
|
||||
])(
|
||||
'%s',
|
||||
(
|
||||
_name: string,
|
||||
proxyConfig: Partial<ProxyConfig>,
|
||||
expectedResult: CameraProxyConfig,
|
||||
) => {
|
||||
(_name: string, cameraConfig: unknown, expectedResult: CameraProxyConfig) => {
|
||||
const camera = new ReolinkCamera(
|
||||
createCameraConfig({
|
||||
proxy: proxyConfig,
|
||||
}),
|
||||
createCameraConfig(cameraConfig),
|
||||
mock<CameraManagerEngine>(),
|
||||
);
|
||||
expect(camera.getProxyConfig()).toEqual(expectedResult);
|
||||
|
||||
@@ -40,6 +40,7 @@ describe('config defaults', () => {
|
||||
},
|
||||
proxy: {
|
||||
dynamic: true,
|
||||
live: 'auto',
|
||||
media: 'auto',
|
||||
ssl_verification: 'auto',
|
||||
ssl_ciphers: 'auto',
|
||||
|
||||
@@ -8,15 +8,21 @@ import {
|
||||
import { createHASS } from '../test-utils.js';
|
||||
|
||||
describe('getWebProxiedURL', () => {
|
||||
it('should return proxied URL with v != 0', () => {
|
||||
expect(getWebProxiedURL('http://example.com', 2)).toBe(
|
||||
it('should return proxied URL with default version', () => {
|
||||
expect(getWebProxiedURL('http://example.com')).toBe(
|
||||
'/api/hass_web_proxy/v0/?url=http%3A%2F%2Fexample.com',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return proxied URL with non-default version', () => {
|
||||
expect(getWebProxiedURL('http://example.com', { version: 2 })).toBe(
|
||||
'/api/hass_web_proxy/v2/?url=http%3A%2F%2Fexample.com',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return proxied URL with default v', () => {
|
||||
expect(getWebProxiedURL('http://example.com')).toBe(
|
||||
'/api/hass_web_proxy/v0/?url=http%3A%2F%2Fexample.com',
|
||||
it('should return proxied URL with websocket', () => {
|
||||
expect(getWebProxiedURL('http://example.com', { websocket: true })).toBe(
|
||||
'/api/hass_web_proxy/v0/ws?url=http%3A%2F%2Fexample.com',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -26,6 +32,7 @@ describe('shouldUseWebProxy', () => {
|
||||
config: Partial<CameraProxyConfig> = {},
|
||||
): CameraProxyConfig => ({
|
||||
media: true,
|
||||
live: true,
|
||||
ssl_verification: true,
|
||||
ssl_ciphers: 'default',
|
||||
dynamic: true,
|
||||
@@ -83,4 +90,28 @@ describe('addDynamicProxyURL', () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should add dynamic proxy URL using config defaults', async () => {
|
||||
const hass = createHASS();
|
||||
const proxyConfig: CameraProxyConfig = {
|
||||
media: true,
|
||||
live: true,
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'insecure',
|
||||
dynamic: true,
|
||||
};
|
||||
|
||||
await addDynamicProxyURL(hass, 'http://example.com', {
|
||||
proxyConfig: proxyConfig,
|
||||
});
|
||||
|
||||
expect(hass.callService).toHaveBeenCalledWith(
|
||||
'hass_web_proxy',
|
||||
'create_proxied_url',
|
||||
expect.objectContaining({
|
||||
ssl_verification: false,
|
||||
ssl_ciphers: 'insecure',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
arrayMove,
|
||||
aspectRatioToStyle,
|
||||
contentsChanged,
|
||||
convertHTTPAdressToWebsocket,
|
||||
dayToDate,
|
||||
desparsifyArrays,
|
||||
errorToConsole,
|
||||
@@ -514,3 +515,23 @@ describe('generateFloatApproximatelyEqualsCustomizer', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertHTTPAdressToWebsocket', () => {
|
||||
it('should convert http to ws', () => {
|
||||
expect(convertHTTPAdressToWebsocket('http://example.com')).toBe('ws://example.com');
|
||||
});
|
||||
it('should convert https to ws', () => {
|
||||
expect(convertHTTPAdressToWebsocket('https://example.com')).toBe(
|
||||
'wss://example.com',
|
||||
);
|
||||
});
|
||||
it('should not change ws url', () => {
|
||||
expect(convertHTTPAdressToWebsocket('ws://example.com')).toBe('ws://example.com');
|
||||
});
|
||||
it('should not change wss url', () => {
|
||||
expect(convertHTTPAdressToWebsocket('wss://example.com')).toBe('wss://example.com');
|
||||
});
|
||||
it('should handle mixed case', () => {
|
||||
expect(convertHTTPAdressToWebsocket('HtTp://example.com')).toBe('ws://example.com');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { homeAssistantSignPath } from '../../src/ha/sign-path.js';
|
||||
import { convertEndpointAddressToSignedWebsocket } from '../../src/utils/endpoint';
|
||||
import { createHASS } from '../test-utils';
|
||||
|
||||
vi.mock('../../src/ha/sign-path.js');
|
||||
|
||||
describe('convertEndpointAddressToSignedWebsocket', () => {
|
||||
it('without signing', async () => {
|
||||
expect(
|
||||
await convertEndpointAddressToSignedWebsocket(createHASS(), {
|
||||
endpoint: 'http://example.com',
|
||||
sign: false,
|
||||
}),
|
||||
).toBe('http://example.com');
|
||||
});
|
||||
|
||||
describe('with signing', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('successful', async () => {
|
||||
vi.mocked(homeAssistantSignPath).mockResolvedValue('http://signed.com');
|
||||
|
||||
expect(
|
||||
await convertEndpointAddressToSignedWebsocket(createHASS(), {
|
||||
endpoint: 'http://example.com',
|
||||
sign: true,
|
||||
}),
|
||||
).toBe('ws://signed.com');
|
||||
});
|
||||
|
||||
it('with null response', async () => {
|
||||
vi.mocked(homeAssistantSignPath).mockResolvedValue(null);
|
||||
|
||||
expect(
|
||||
await convertEndpointAddressToSignedWebsocket(createHASS(), {
|
||||
endpoint: 'http://example.com',
|
||||
sign: true,
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('with exception on signing', async () => {
|
||||
const consoleSpy = vi.spyOn(global.console, 'warn').mockReturnValue(undefined);
|
||||
|
||||
vi.mocked(homeAssistantSignPath).mockRejectedValue(new Error());
|
||||
|
||||
expect(
|
||||
await convertEndpointAddressToSignedWebsocket(createHASS(), {
|
||||
endpoint: 'http://example.com',
|
||||
sign: true,
|
||||
}),
|
||||
).toBeNull();
|
||||
|
||||
expect(consoleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user