feat(robot): connect provisioning hotspots in app

This commit is contained in:
2026-08-16 20:18:28 +08:00
parent abecd5f344
commit c1326a2980
17 changed files with 1950 additions and 11 deletions

View File

@@ -13,6 +13,8 @@ import {
createAiHardwareOperationId,
recoverAiHardwareCredential,
openAiHardwareProvisioningPortal,
scanAiHardwareProvisioningHotspots,
connectAiHardwareProvisioningHotspot,
} from '@/lib/ai-hardware';
const hostApiFetchMock = vi.hoisted(() => vi.fn());
@@ -80,6 +82,99 @@ describe('AI hardware renderer API', () => {
);
});
it('strictly projects Robot hotspot scan and connection DTOs', async () => {
hostApiFetchMock
.mockResolvedValueOnce({
success: true,
data: {
platform: 'macos',
hotspots: [{
candidate_id: 'candidate_123',
ssid: 'Xiaozhi-Desk',
signal_percent: 72,
connected: false,
}],
},
})
.mockResolvedValueOnce({
success: true,
data: { connected: true, candidate_id: 'candidate_123', ssid: 'Xiaozhi-Desk' },
});
await expect(scanAiHardwareProvisioningHotspots()).resolves.toEqual({
platform: 'macos',
hotspots: [{
candidateId: 'candidate_123',
ssid: 'Xiaozhi-Desk',
signalPercent: 72,
connected: false,
}],
});
await expect(connectAiHardwareProvisioningHotspot('candidate_123')).resolves.toEqual({
connected: true,
candidateId: 'candidate_123',
ssid: 'Xiaozhi-Desk',
});
expect(hostApiFetchMock).toHaveBeenNthCalledWith(
1,
'/api/works/ai-hardware/provisioning-hotspots/scan',
{ method: 'POST', body: '{}' },
);
expect(hostApiFetchMock).toHaveBeenNthCalledWith(
2,
'/api/works/ai-hardware/provisioning-hotspots/connect',
{ method: 'POST', body: JSON.stringify({ candidate_id: 'candidate_123' }) },
);
});
it.each([
{ platform: 'linux', hotspots: [] },
{ platform: 'windows', hotspots: [], native_interface: 'secret' },
{
platform: 'windows',
hotspots: [{
candidate_id: 'candidate_123', ssid: 'Xiaozhi-Desk', signal_percent: 101,
connected: false,
}],
},
{
platform: 'windows',
hotspots: [{
candidate_id: 'candidate_123', ssid: 'Xiaozhi-Desk', signal_percent: 72,
connected: false, bssid: 'secret',
}],
},
])('rejects malformed or expanded hotspot scan DTO %#', async (data) => {
hostApiFetchMock.mockResolvedValueOnce({ success: true, data });
await expect(scanAiHardwareProvisioningHotspots()).rejects.toMatchObject({
code: 'AI_HARDWARE_INVALID_RESPONSE',
});
});
it.each([
{ connected: false, candidate_id: 'candidate_123', ssid: 'Xiaozhi-Desk' },
{ connected: true, candidate_id: '../ssid', ssid: 'Xiaozhi-Desk' },
{
connected: true, candidate_id: 'candidate_123', ssid: 'Xiaozhi-Desk',
native_profile: 'secret',
},
])('rejects malformed or expanded hotspot connection DTO %#', async (data) => {
hostApiFetchMock.mockResolvedValueOnce({ success: true, data });
await expect(connectAiHardwareProvisioningHotspot('candidate_123')).rejects.toMatchObject({
code: 'AI_HARDWARE_INVALID_RESPONSE',
});
});
it('rejects invalid hotspot candidate IDs before calling Host API', async () => {
await expect(connectAiHardwareProvisioningHotspot('../ssid')).rejects.toThrow(
'candidateId is invalid',
);
await expect(connectAiHardwareProvisioningHotspot('a'.repeat(129))).rejects.toThrow(
'candidateId is invalid',
);
expect(hostApiFetchMock).not.toHaveBeenCalled();
});
it.each([
{ guided_hotspot_binding: 'true' },
{ guided_hotspot_binding: false, portal_url: 'http://unsafe.example' },