111 lines
6.0 KiB
JavaScript
111 lines
6.0 KiB
JavaScript
|
|
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import path from 'node:path'
|
|
import { createSimAdminClient, normalizeInstance, summarizeInstanceSnapshot, redactInstance, loadConfig, addInstanceToConfig, updateInstanceInConfig, deleteInstanceFromConfig } from '../server/core.js'
|
|
|
|
test('normalizeInstance supports passwordless and password-protected instances without exposing password', () => {
|
|
const open = normalizeInstance({ id: 'open-1', name: '开放设备', url: 'http://127.0.0.1:3000/' }, 0)
|
|
assert.equal(open.auth.mode, 'none')
|
|
assert.equal(open.url, 'http://127.0.0.1:3000')
|
|
|
|
const protectedOne = normalizeInstance({ id: 'locked', url: 'http://cpe.local', auth: { password: 'secret' } }, 1)
|
|
assert.equal(protectedOne.auth.mode, 'password')
|
|
assert.equal(protectedOne.auth.password, 'secret')
|
|
assert.equal(redactInstance(protectedOne).auth.hasPassword, true)
|
|
assert.equal(redactInstance(protectedOne).auth.password, undefined)
|
|
})
|
|
|
|
test('client stores simadmin_session from login and sends it on later proxied requests', async () => {
|
|
const calls = []
|
|
const fetchImpl = async (url, options = {}) => {
|
|
calls.push({ url: String(url), options })
|
|
if (String(url).endsWith('/api/auth/status') && calls.length === 1) {
|
|
return new Response(JSON.stringify({ data: { configured: true, authenticated: false, settings: { password_protection_enabled: true } } }), { status: 200, headers: { 'content-type': 'application/json' } })
|
|
}
|
|
if (String(url).endsWith('/api/auth/login')) {
|
|
return new Response(JSON.stringify({ success: true }), { status: 200, headers: { 'set-cookie': 'simadmin_session=abc123; HttpOnly; Path=/; Max-Age=86400' } })
|
|
}
|
|
if (String(url).endsWith('/api/device')) {
|
|
return new Response(JSON.stringify({ data: { model: 'RM500Q' } }), { status: 200, headers: { 'content-type': 'application/json' } })
|
|
}
|
|
return new Response('{}', { status: 404 })
|
|
}
|
|
const client = createSimAdminClient(normalizeInstance({ id: 'locked', url: 'http://sim.local', auth: { password: 'secret' } }, 0), { fetchImpl })
|
|
const auth = await client.ensureAuthenticated()
|
|
assert.equal(auth.authenticated, true)
|
|
const proxied = await client.fetchJson('/api/device')
|
|
assert.equal(proxied.status, 200)
|
|
assert.match(calls.at(-1).options.headers.cookie, /simadmin_session=abc123/)
|
|
})
|
|
|
|
test('snapshot summary exposes device-monitoring fields from stats, sms, data and hardware endpoints', () => {
|
|
const snapshot = summarizeInstanceSnapshot({
|
|
device: { data: { model: 'CPE X', imei: '123', manufacturer: 'Quectel', powered: true, revision: 'RG500QEAAAR13A01' } },
|
|
sim: { data: { iccid: '8986', imsi: '460001234', phone_numbers: ['13800138000'], operator: 'CMCC', present: true } },
|
|
network: { data: { operator_name: '中国移动', signal_strength: -73, registration_status: 'registered', technology_preference: 'LTE' } },
|
|
smsStats: { data: { total: 12, incoming: 9, outgoing: 3, pushed: 8 } },
|
|
data: { data: { active: true } },
|
|
ota: { data: { current_version: '1.2.3', current_commit: 'abc123', pending_update: false } },
|
|
stats: { data: {
|
|
cpu_load: [0.2, 0.4, 0.6],
|
|
memory: { total: 1024, used: 512, free: 512 },
|
|
disk: { total: 2048, used: 1024, free: 1024 },
|
|
network_speed: { rx: 1024, tx: 2048 },
|
|
system_info: { os: 'OpenWrt', kernel: '6.6.1' },
|
|
temperature: { cpu: 47.5, modem: 41 },
|
|
uptime: 3600,
|
|
} },
|
|
})
|
|
assert.equal(snapshot.device.model, 'CPE X')
|
|
assert.equal(snapshot.device.manufacturer, 'Quectel')
|
|
assert.equal(snapshot.device.powered, true)
|
|
assert.equal(snapshot.sim.iccid, '8986')
|
|
assert.equal(snapshot.sim.present, true)
|
|
assert.equal(snapshot.sim.phoneNumber, '13800138000')
|
|
assert.equal(snapshot.network.operator, '中国移动')
|
|
assert.equal(snapshot.network.signal, -73)
|
|
assert.equal(snapshot.network.accessTechnology, 'LTE')
|
|
assert.equal(snapshot.sms.total, 12)
|
|
assert.equal(snapshot.sms.incoming, 9)
|
|
assert.equal(snapshot.sms.outgoing, 3)
|
|
assert.equal(snapshot.sms.pushed, 8)
|
|
assert.equal(snapshot.data.active, true)
|
|
assert.equal(snapshot.ota.currentVersion, '1.2.3')
|
|
assert.equal(snapshot.ota.currentCommit, 'abc123')
|
|
assert.equal(snapshot.ota.pendingUpdate, false)
|
|
assert.deepEqual(snapshot.system.temperature, { cpu: 47.5, modem: 41 })
|
|
assert.deepEqual(snapshot.system.networkSpeed, { rx: 1024, tx: 2048 })
|
|
assert.equal(snapshot.system.memory.used, 512)
|
|
assert.equal(snapshot.system.disk.free, 1024)
|
|
assert.equal(snapshot.system.info.os, 'OpenWrt')
|
|
assert.equal(snapshot.system.uptime, 3600)
|
|
})
|
|
|
|
test('config store can add update and delete instances while persisting json', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'multi-simadmin-'))
|
|
const configPath = path.join(dir, 'config.json')
|
|
await writeFile(configPath, JSON.stringify({ server: { host: '127.0.0.1', port: 9999 }, instances: [] }, null, 2))
|
|
const config = await loadConfig({ configPath, defaultConfigPath: configPath })
|
|
|
|
const added = await addInstanceToConfig(config, { id: 'home-cpe', name: '家里 CPE', url: 'http://192.168.1.1/', auth: { password: 'pw' }, tags: ['home'] })
|
|
assert.equal(added.id, 'home-cpe')
|
|
assert.equal(added.url, 'http://192.168.1.1')
|
|
assert.equal(config.instances.length, 1)
|
|
|
|
const updated = await updateInstanceInConfig(config, 'home-cpe', { name: '客厅 CPE', url: 'http://192.168.1.2', auth: { mode: 'none' }, description: 'main router' })
|
|
assert.equal(updated.name, '客厅 CPE')
|
|
assert.equal(updated.auth.password, '')
|
|
|
|
const persisted = JSON.parse(await readFile(configPath, 'utf8'))
|
|
assert.equal(persisted.instances[0].name, '客厅 CPE')
|
|
assert.equal(persisted.instances[0].url, 'http://192.168.1.2')
|
|
|
|
const removed = await deleteInstanceFromConfig(config, 'home-cpe')
|
|
assert.equal(removed.id, 'home-cpe')
|
|
assert.equal(config.instances.length, 0)
|
|
assert.deepEqual(JSON.parse(await readFile(configPath, 'utf8')).instances, [])
|
|
})
|