Files
integrations/test/dlna_dms/test.dlna_dms.client.node.ts
T

96 lines
5.5 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import { DlnaDmsClient, DlnaDmsIntegration } from '../../ts/integrations/dlna_dms/index.js';
const descriptionXml = `<?xml version="1.0"?>
<root>
<device>
<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>
<friendlyName>Fixture Media Server</friendlyName>
<manufacturer>Example</manufacturer>
<modelName>DMS Fixture</modelName>
<UDN>uuid:fixture-server</UDN>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>
<serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId>
<controlURL>/ContentDirectory/control</controlURL>
</service>
<service>
<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>
<serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>
<controlURL>/ConnectionManager/control</controlURL>
</service>
</serviceList>
</device>
</root>`;
const rootDidl = `<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"><container id="0" parentID="-1" childCount="2"><dc:title>Fixture Media Server</dc:title><upnp:class>object.container</upnp:class></container></DIDL-Lite>`;
const childrenDidl = `<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"><container id="music" parentID="0" childCount="12"><dc:title>Music</dc:title><upnp:class>object.container.album.musicAlbum</upnp:class></container><item id="track-1" parentID="0"><dc:title>Track One</dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><res protocolInfo="http-get:*:audio/mpeg:*">/media/track.mp3</res></item></DIDL-Lite>`;
const soapEnvelope = (actionArg: string, bodyArg: string): string => `<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><u:${actionArg}Response xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">${bodyArg}</u:${actionArg}Response></s:Body></s:Envelope>`;
tap.test('refreshes a live DLNA DMS snapshot from native UPnP responses', async () => {
const originalFetch = globalThis.fetch;
const requests: string[] = [];
globalThis.fetch = (async (inputArg: RequestInfo | URL, initArg?: RequestInit) => {
const url = String(inputArg);
requests.push(`${initArg?.method || 'GET'} ${url}`);
if (url.endsWith('/rootDesc.xml')) {
return new Response(descriptionXml, { status: 200 });
}
const body = String(initArg?.body || '');
if (body.includes('GetSystemUpdateID')) {
return new Response(soapEnvelope('GetSystemUpdateID', '<SystemUpdateID>11</SystemUpdateID>'), { status: 200 });
}
if (body.includes('GetSearchCapabilities')) {
return new Response(soapEnvelope('GetSearchCapabilities', '<SearchCaps>dc:title</SearchCaps>'), { status: 200 });
}
if (body.includes('GetSortCapabilities')) {
return new Response(soapEnvelope('GetSortCapabilities', '<SortCaps>dc:title</SortCaps>'), { status: 200 });
}
if (body.includes('GetProtocolInfo')) {
return new Response(soapEnvelope('GetProtocolInfo', '<Source>http-get:*:audio/mpeg:*</Source><Sink></Sink>'), { status: 200 });
}
if (body.includes('GetCurrentConnectionIDs')) {
return new Response(soapEnvelope('GetCurrentConnectionIDs', '<ConnectionIDs>0</ConnectionIDs>'), { status: 200 });
}
if (body.includes('BrowseMetadata')) {
return new Response(soapEnvelope('Browse', `<Result>${escapeXml(rootDidl)}</Result><NumberReturned>1</NumberReturned><TotalMatches>1</TotalMatches><UpdateID>11</UpdateID>`), { status: 200 });
}
if (body.includes('BrowseDirectChildren')) {
return new Response(soapEnvelope('Browse', `<Result>${escapeXml(childrenDidl)}</Result><NumberReturned>2</NumberReturned><TotalMatches>2</TotalMatches><UpdateID>11</UpdateID>`), { status: 200 });
}
return new Response('', { status: 404 });
}) as typeof globalThis.fetch;
try {
const client = new DlnaDmsClient({ location: 'http://192.168.1.60:8200/rootDesc.xml' });
const snapshot = await client.refresh();
expect(snapshot.online).toBeTrue();
expect(snapshot.source).toEqual('upnp');
expect(snapshot.device.friendlyName).toEqual('Fixture Media Server');
expect(snapshot.status.systemUpdateId).toEqual(11);
expect(snapshot.libraries[0].title).toEqual('Music');
expect(snapshot.contentDirectory.children[1].canPlay).toBeTrue();
expect(snapshot.contentDirectory.children[1].resources[0].uri).toEqual('http://192.168.1.60:8200/media/track.mp3');
expect(requests.some((requestArg) => requestArg.includes('/ContentDirectory/control'))).toBeTrue();
} finally {
globalThis.fetch = originalFetch;
}
});
tap.test('does not report live UPnP success without endpoint, snapshot, or client', async () => {
const runtime = await new DlnaDmsIntegration().setup({}, {});
const result = await runtime.callService!({ domain: 'dlna_dms', service: 'refresh', target: {} });
expect(result.success).toBeFalse();
const snapshot = result.data as { online: boolean; source: string; error?: string };
expect(snapshot.online).toBeFalse();
expect(snapshot.source).toEqual('runtime');
expect(snapshot.error).toContain('requires a device description URL');
await runtime.destroy();
});
const escapeXml = (valueArg: string): string => valueArg.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
export default tap.start();