179 lines
8.9 KiB
TypeScript
179 lines
8.9 KiB
TypeScript
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
|
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { HuaweiLteClient, HuaweiLteIntegration, HuaweiLteMapper, type IHuaweiLteRawData } from '../../ts/integrations/huawei_lte/index.js';
|
|
|
|
const xmlResponse = (itemsArg: Record<string, string | number | boolean>): string => {
|
|
return `<?xml version="1.0" encoding="UTF-8"?><response>${Object.entries(itemsArg).map(([key, value]) => `<${key}>${value}</${key}>`).join('')}</response>`;
|
|
};
|
|
|
|
const readBody = async (requestArg: IncomingMessage): Promise<string> => {
|
|
const chunks: Buffer[] = [];
|
|
for await (const chunk of requestArg) {
|
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
}
|
|
return Buffer.concat(chunks).toString('utf8');
|
|
};
|
|
|
|
tap.test('reads Huawei LTE snapshots over native local HTTP and writes real POST commands', async () => {
|
|
const requests: Array<{ method?: string; url?: string; body?: string }> = [];
|
|
const server = createServer((requestArg: IncomingMessage, responseArg: ServerResponse) => {
|
|
void (async () => {
|
|
const body = requestArg.method === 'POST' ? await readBody(requestArg) : undefined;
|
|
requests.push({ method: requestArg.method, url: requestArg.url, body });
|
|
responseArg.setHeader('content-type', 'application/xml');
|
|
responseArg.setHeader('__RequestVerificationToken', 'token-next');
|
|
const url = new URL(requestArg.url || '/', 'http://127.0.0.1');
|
|
|
|
if (url.pathname === '/') {
|
|
responseArg.setHeader('content-type', 'text/html');
|
|
responseArg.end('<html><head><meta name="csrf_token" content="token-1"></head></html>');
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/device/information') {
|
|
responseArg.end(xmlResponse({ DeviceName: 'B535-232', SerialNumber: 'LIVE123', SoftwareVersion: '12.0.5.1', MacAddress1: 'AABBCCDDEEFF' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/device/basic_information') {
|
|
responseArg.end(xmlResponse({ devicename: 'B535-232' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/device/signal') {
|
|
responseArg.end(xmlResponse({ rsrp: '-90dBm', rsrq: '-11dB', sinr: '20dB' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/dialup/mobile-dataswitch' && requestArg.method === 'GET') {
|
|
responseArg.end(xmlResponse({ dataswitch: '1' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/monitoring/status') {
|
|
responseArg.end(xmlResponse({ ConnectionStatus: '901', WifiStatus: '1', CurrentWifiUser: '2' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/monitoring/check-notifications') {
|
|
responseArg.end(xmlResponse({ UnreadMessage: '1', SmsStorageFull: '0' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/monitoring/traffic-statistics') {
|
|
responseArg.end(xmlResponse({ CurrentDownloadRate: '128', CurrentUploadRate: '64', TotalDownload: '4096', TotalUpload: '8192' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/monitoring/month_statistics') {
|
|
responseArg.end(xmlResponse({ CurrentMonthDownload: '1000', CurrentMonthUpload: '2000' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/net/current-plmn') {
|
|
responseArg.end(xmlResponse({ FullName: 'Example Mobile', Numeric: '00101', State: '0' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/net/net-mode' && requestArg.method === 'GET') {
|
|
responseArg.end(xmlResponse({ NetworkMode: '03', NetworkBand: '3fffffff', LTEBand: '7fffffffffffffff' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/sms/sms-count') {
|
|
responseArg.end(xmlResponse({ LocalUnread: '1', LocalInbox: '3', SimMax: '50' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/wlan/wifi-feature-switch') {
|
|
responseArg.end(xmlResponse({ wifi24g_switch_enable: '1', wifi5g_enabled: '1' }));
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/lan/HostInfo') {
|
|
responseArg.end('<?xml version="1.0" encoding="UTF-8"?><response><Hosts><Host><MacAddress>11:22:33:44:55:66</MacAddress><HostName>phone</HostName><IpAddress>192.168.8.10</IpAddress><Active>1</Active><InterfaceType>Wireless</InterfaceType></Host></Hosts></response>');
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/wlan/multi-basic-settings') {
|
|
if (requestArg.method === 'GET') {
|
|
responseArg.end('<?xml version="1.0" encoding="UTF-8"?><response><Ssids><Ssid><Index>0</Index><WifiEnable>1</WifiEnable><WifiSsid>Main</WifiSsid><WifiMac>AA:BB:CC:DD:EE:FF</WifiMac><wifiisguestnetwork>0</wifiisguestnetwork></Ssid><Ssid><Index>1</Index><WifiEnable>1</WifiEnable><WifiSsid>Guest</WifiSsid><WifiMac>AA:BB:CC:DD:EE:00</WifiMac><wifiisguestnetwork>1</wifiisguestnetwork></Ssid></Ssids></response>');
|
|
return;
|
|
}
|
|
expect(body).toContain('<WifiEnable>0</WifiEnable>');
|
|
responseArg.end('<response>OK</response>');
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/dialup/mobile-dataswitch' && requestArg.method === 'POST') {
|
|
expect(body).toContain('<dataswitch>0</dataswitch>');
|
|
responseArg.end('<response>OK</response>');
|
|
return;
|
|
}
|
|
if (url.pathname === '/api/sms/send-sms') {
|
|
expect(body).toContain('<Phone>+123</Phone>');
|
|
expect(body).toContain('<Content>hello</Content>');
|
|
responseArg.end('<response>OK</response>');
|
|
return;
|
|
}
|
|
responseArg.statusCode = 404;
|
|
responseArg.end('<error><code>100002</code><message>No support</message></error>');
|
|
})().catch((errorArg) => {
|
|
responseArg.statusCode = 500;
|
|
responseArg.end(String(errorArg));
|
|
});
|
|
});
|
|
|
|
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
try {
|
|
const address = server.address();
|
|
const port = typeof address === 'object' && address ? address.port : 0;
|
|
const client = new HuaweiLteClient({ url: `http://127.0.0.1:${port}/`, timeoutMs: 1000 });
|
|
const snapshot = await client.getSnapshot();
|
|
await client.execute({ action: 'set_mobile_dataswitch', enabled: false });
|
|
await client.execute({ action: 'set_wifi_guest_network', enabled: false });
|
|
await client.execute({ action: 'send_sms', phoneNumbers: ['+123'], message: 'hello' });
|
|
|
|
expect(snapshot.online).toBeTrue();
|
|
expect(snapshot.source).toEqual('http');
|
|
expect(snapshot.device.serialNumber).toEqual('LIVE123');
|
|
expect(snapshot.device.macAddresses[0]).toEqual('aa:bb:cc:dd:ee:00');
|
|
expect(snapshot.signal.rsrp).toEqual(-90);
|
|
expect(snapshot.connection.mobileDataEnabled).toBeTrue();
|
|
expect(snapshot.connection.guestWifiEnabled).toBeTrue();
|
|
expect(snapshot.hosts[0].hostname).toEqual('phone');
|
|
expect(requests.some((requestArg) => requestArg.url === '/api/device/information')).toBeTrue();
|
|
expect(requests.some((requestArg) => requestArg.url === '/api/dialup/mobile-dataswitch' && requestArg.method === 'POST')).toBeTrue();
|
|
expect(requests.some((requestArg) => requestArg.url === '/api/sms/send-sms' && requestArg.method === 'POST')).toBeTrue();
|
|
} finally {
|
|
await new Promise<void>((resolve, reject) => server.close((errorArg) => errorArg ? reject(errorArg) : resolve()));
|
|
}
|
|
});
|
|
|
|
tap.test('does not report live command success for static snapshots', async () => {
|
|
const rawData: Partial<IHuaweiLteRawData> = {
|
|
deviceInformation: { DeviceName: 'Static LTE', SerialNumber: 'STATIC123' },
|
|
dialupMobileDataswitch: { dataswitch: '1' },
|
|
monitoringStatus: { ConnectionStatus: '901' },
|
|
};
|
|
const snapshot = HuaweiLteMapper.toSnapshot({ config: { name: 'Static LTE' }, rawData, online: true, source: 'manual' });
|
|
const runtime = await new HuaweiLteIntegration().setup({ snapshot }, {});
|
|
const result = await runtime.callService?.({ domain: 'switch', service: 'turn_off', target: { entityId: 'switch.static_lte_mobile_data' }, data: {} });
|
|
|
|
expect(result?.success).toBeFalse();
|
|
expect(result?.error).toContain('Static snapshots/manual data are read-only');
|
|
await runtime.destroy();
|
|
});
|
|
|
|
tap.test('delegates Huawei LTE commands to an injected executor', async () => {
|
|
const commands: unknown[] = [];
|
|
const rawData: Partial<IHuaweiLteRawData> = {
|
|
deviceInformation: { DeviceName: 'Executor LTE', SerialNumber: 'EXEC123' },
|
|
dialupMobileDataswitch: { dataswitch: '1' },
|
|
monitoringStatus: { ConnectionStatus: '901' },
|
|
};
|
|
const snapshot = HuaweiLteMapper.toSnapshot({ config: { name: 'Executor LTE' }, rawData, online: true, source: 'manual' });
|
|
const runtime = await new HuaweiLteIntegration().setup({
|
|
snapshot,
|
|
commandExecutor: {
|
|
execute: async (requestArg) => {
|
|
commands.push(requestArg);
|
|
return { accepted: true };
|
|
},
|
|
},
|
|
}, {});
|
|
|
|
const result = await runtime.callService?.({ domain: 'huawei_lte', service: 'reboot', target: {}, data: {} });
|
|
|
|
expect(result?.success).toBeTrue();
|
|
expect((commands[0] as { action?: string }).action).toEqual('reboot');
|
|
await runtime.destroy();
|
|
});
|
|
|
|
export default tap.start();
|