105 lines
4.9 KiB
TypeScript
105 lines
4.9 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { AmcrestClient } from '../../ts/integrations/amcrest/index.js';
|
|
|
|
tap.test('fetches live snapshots and only reports command success after HTTP response', async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
const requests: string[] = [];
|
|
globalThis.fetch = (async (inputArg: RequestInfo | URL) => {
|
|
const url = typeof inputArg === 'string' ? inputArg : inputArg instanceof URL ? inputArg.toString() : inputArg.url;
|
|
requests.push(url);
|
|
if (url.includes('/cgi-bin/magicBox.cgi?action=getDeviceType')) {
|
|
return new Response('IP2M-841');
|
|
}
|
|
if (url.includes('/cgi-bin/magicBox.cgi?action=getVendor')) {
|
|
return new Response('Amcrest');
|
|
}
|
|
if (url.includes('/cgi-bin/magicBox.cgi?action=getSerialNo')) {
|
|
return new Response('AMC123');
|
|
}
|
|
if (url.includes('/cgi-bin/configManager.cgi?action=getConfig&name=Encode')) {
|
|
return new Response('table.Encode[0].MainFormat[0].VideoEnable=true\ntable.Encode[0].MainFormat[0].AudioEnable=true');
|
|
}
|
|
if (url.includes('/cgi-bin/configManager.cgi?action=getConfig&name=RecordMode')) {
|
|
return new Response('table.RecordMode[0].Mode=Manual');
|
|
}
|
|
if (url.includes('/cgi-bin/configManager.cgi?action=getConfig&name=MotionDetect')) {
|
|
return new Response('table.MotionDetect[0].Enable=true\ntable.MotionDetect[0].EventHandler.RecordEnable=false');
|
|
}
|
|
if (url.includes('/cgi-bin/configManager.cgi?action=getConfig&name=LeLensMask')) {
|
|
return new Response('table.LeLensMask[0].Enable=false');
|
|
}
|
|
if (url.includes('/cgi-bin/eventManager.cgi?action=getEventIndexes&code=VideoMotion')) {
|
|
return new Response('channels[0]=0');
|
|
}
|
|
if (url.includes('/cgi-bin/ptz.cgi?action=getPresets')) {
|
|
return new Response('presets[0].Name=Home\npresets[1].Name=Driveway');
|
|
}
|
|
if (url.includes('/cgi-bin/configManager.cgi?action=setConfig&LeLensMask[0].Enable=true')) {
|
|
return new Response('OK');
|
|
}
|
|
if (url.includes('/cgi-bin/snapshot.cgi?channel=0')) {
|
|
return new Response(new Uint8Array([0xff, 0xd8, 0xff]), { headers: { 'content-type': 'image/jpeg' } });
|
|
}
|
|
return new Response('Not Found', { status: 404 });
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const client = new AmcrestClient({ host: '192.168.1.30', username: 'user', password: 'pass' });
|
|
const snapshot = await client.getSnapshot();
|
|
expect(snapshot.connected).toBeTrue();
|
|
expect(snapshot.deviceInfo.manufacturer).toEqual('Amcrest');
|
|
expect(snapshot.cameras[0].rtspUrl).toEqual('rtsp://user:pass@192.168.1.30:554/cam/realmonitor?channel=1&subtype=0');
|
|
expect(snapshot.binarySensors.find((sensorArg) => sensorArg.key === 'motion_detected')?.isOn).toBeTrue();
|
|
expect(snapshot.switches.find((switchArg) => switchArg.key === 'privacy_mode')?.isOn).toEqual(false);
|
|
expect(snapshot.sensors.find((sensorArg) => sensorArg.key === 'ptz_preset')?.value).toEqual(2);
|
|
|
|
const image = await client.execute({ type: 'snapshot_image', service: 'snapshot', channel: 0 });
|
|
expect((image as { contentType: string }).contentType).toEqual('image/jpeg');
|
|
|
|
const result = await client.execute({ type: 'set_privacy_mode', service: 'turn_on', enabled: true, channel: 0 });
|
|
expect((result as { ok: boolean }).ok).toBeTrue();
|
|
expect(requests.some((requestArg) => requestArg.includes('LeLensMask[0].Enable=true'))).toBeTrue();
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
tap.test('does not pretend live commands succeeded without a live endpoint or success body', async () => {
|
|
const clientWithoutHost = new AmcrestClient({});
|
|
let missingHostError = '';
|
|
try {
|
|
await clientWithoutHost.execute({ type: 'set_privacy_mode', service: 'turn_on', enabled: true });
|
|
} catch (errorArg) {
|
|
missingHostError = errorArg instanceof Error ? errorArg.message : String(errorArg);
|
|
}
|
|
expect(missingHostError.includes('requires config.host or config.url')).toBeTrue();
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
globalThis.fetch = (async (inputArg: RequestInfo | URL) => {
|
|
const url = typeof inputArg === 'string' ? inputArg : inputArg instanceof URL ? inputArg.toString() : inputArg.url;
|
|
if (url.includes('/cgi-bin/magicBox.cgi?action=getDeviceType')) {
|
|
return new Response('IP2M-841');
|
|
}
|
|
if (url.includes('/cgi-bin/configManager.cgi?action=setConfig&LeLensMask[0].Enable=true')) {
|
|
return new Response('Error');
|
|
}
|
|
return new Response('', { status: 404 });
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const client = new AmcrestClient({ host: '192.168.1.30' });
|
|
await client.getSnapshot();
|
|
let commandError = '';
|
|
try {
|
|
await client.execute({ type: 'set_privacy_mode', service: 'turn_on', enabled: true });
|
|
} catch (errorArg) {
|
|
commandError = errorArg instanceof Error ? errorArg.message : String(errorArg);
|
|
}
|
|
expect(commandError.includes('did not return a successful response')).toBeTrue();
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
export default tap.start();
|