up
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { once } from 'node:events'
|
||||
import { mkdtemp, writeFile, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { test } from 'node:test'
|
||||
import { WebSocket } from 'ws'
|
||||
import { Controller } from './controller.js'
|
||||
import { LanServer } from './lan.js'
|
||||
|
||||
test('LAN: код, подтверждение, cookie, WebSocket и восстановление без повтора', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'screen-test-'))
|
||||
await writeFile(join(root, 'index.html'), '<html>local</html>')
|
||||
let count = 0
|
||||
const controller = new Controller((state) => {
|
||||
count++
|
||||
server.broadcast(state)
|
||||
})
|
||||
const server = new LanServer(
|
||||
root,
|
||||
() => controller.state,
|
||||
(input) => controller.command(input),
|
||||
)
|
||||
const sockets: WebSocket[] = []
|
||||
try {
|
||||
await server.start('127.0.0.1')
|
||||
const origin = server.url
|
||||
assert.equal((await fetch(origin)).status, 200)
|
||||
assert.equal((await fetch(origin + '/package.json')).status, 404)
|
||||
assert.equal(
|
||||
(
|
||||
await fetch(origin + '/pair', {
|
||||
method: 'POST',
|
||||
headers: { Origin: origin },
|
||||
body: JSON.stringify({ code: 'wrong' }),
|
||||
})
|
||||
).status,
|
||||
403,
|
||||
)
|
||||
assert.equal(
|
||||
(
|
||||
await fetch(origin + '/pair', {
|
||||
method: 'POST',
|
||||
headers: { Origin: 'http://evil.example' },
|
||||
body: '{}',
|
||||
})
|
||||
).status,
|
||||
403,
|
||||
)
|
||||
const denied = new WebSocket(origin.replace('http', 'ws') + '/ws', { origin })
|
||||
sockets.push(denied)
|
||||
await new Promise<void>((resolve) => denied.on('error', () => resolve()))
|
||||
const paired = await fetch(origin + '/pair', {
|
||||
method: 'POST',
|
||||
headers: { Origin: origin },
|
||||
body: JSON.stringify({ code: server.code }),
|
||||
})
|
||||
const { id } = (await paired.json()) as { id: string }
|
||||
assert.equal((await fetch(origin + '/pair/' + id)).status, 202)
|
||||
server.approve(id)
|
||||
const approved = await fetch(origin + '/pair/' + id)
|
||||
assert.equal(approved.status, 200)
|
||||
const cookie = approved.headers.get('set-cookie')!.split(';')[0]
|
||||
assert.equal((await fetch(origin + '/pair/' + id)).status, 404)
|
||||
async function connect() {
|
||||
const ws = new WebSocket(origin.replace('http', 'ws') + '/ws', {
|
||||
origin,
|
||||
headers: { Cookie: cookie },
|
||||
})
|
||||
sockets.push(ws)
|
||||
const [data] = await once(ws, 'message')
|
||||
return { ws, state: JSON.parse(data.toString()) }
|
||||
}
|
||||
const first = await connect()
|
||||
assert.equal(first.state.revision, 0)
|
||||
let received = once(first.ws, 'message')
|
||||
const command = { id: 'phone-1', action: 'play', media: 'audio', position: 0 }
|
||||
first.ws.send(JSON.stringify(command))
|
||||
await received
|
||||
first.ws.send(JSON.stringify(command))
|
||||
// A fresh command is a barrier proving that the duplicate was processed first.
|
||||
received = once(first.ws, 'message')
|
||||
first.ws.send(JSON.stringify({ ...command, id: 'phone-2', action: 'pause' }))
|
||||
await received
|
||||
assert.equal(count, 2)
|
||||
first.ws.close()
|
||||
await once(first.ws, 'close')
|
||||
const restored = await connect()
|
||||
assert.equal(restored.state.revision, 2)
|
||||
assert.equal(count, 2)
|
||||
const closed = once(restored.ws, 'close')
|
||||
restored.ws.send(JSON.stringify({ action: 'delete' }))
|
||||
await closed
|
||||
assert.equal(count, 2)
|
||||
const oversized = await connect()
|
||||
const oversizedClosed = once(oversized.ws, 'close')
|
||||
oversized.ws.send('x'.repeat(3000))
|
||||
await oversizedClosed
|
||||
assert.equal(count, 2)
|
||||
assert.equal((await fetch(origin)).status, 200)
|
||||
} finally {
|
||||
for (const ws of sockets) ws.terminate()
|
||||
await server.close()
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user