assets/test/voice_state_test.mjs

58e6347eeb72 · 2 KB

import assert from "node:assert/strict"
import {readFileSync} from "node:fs"
import test from "node:test"

import {
  ACTIVE_SERVER_STATES,
  admissionErrorMessage,
  closeVoiceResources,
  microphoneErrorMessage,
  microphoneMayTransmit,
} from "../js/voice_state.mjs"

test("the browser admission request does not negotiate through Phoenix's HTML accept gate", () => {
  const source = readFileSync(new URL("../js/voice_controller.js", import.meta.url), "utf8")

  assert.doesNotMatch(source, /["']accept["']\s*:\s*["']application\/sdp["']/)
  assert.match(source, /["']content-type["']\s*:\s*["']application\/sdp["']/)
})

test("microphone transmits only after browser and fenced server readiness", () => {
  const ready = {
    localPhase: "ready",
    serverStatus: "listening",
    peerConnectionState: "connected",
    channelState: "open",
    userMuted: false,
    playbackBlocked: false,
  }

  assert.equal(microphoneMayTransmit(ready), true)

  for (const unsafe of [
    {...ready, serverStatus: "connecting"},
    {...ready, serverStatus: "reconnecting"},
    {...ready, peerConnectionState: "disconnected"},
    {...ready, channelState: "closed"},
    {...ready, userMuted: true},
    {...ready, playbackBlocked: true},
  ]) {
    assert.equal(microphoneMayTransmit(unsafe), false)
  }

  assert.equal(ACTIVE_SERVER_STATES.has("reconnecting"), true)
  assert.equal(ACTIVE_SERVER_STATES.has("ended"), false)
})

test("permission, device, rate, and text conflict failures stay actionable", () => {
  assert.match(microphoneErrorMessage({name: "NotAllowedError"}), /ACCESS DENIED/)
  assert.match(microphoneErrorMessage({name: "NotFoundError"}), /NO MICROPHONE/)
  assert.match(admissionErrorMessage(429, "voice_rate_limited"), /WAIT A MINUTE/)
  assert.match(admissionErrorMessage(409, "text_turn_in_progress"), /TEXT RESPONSE/)
  assert.match(admissionErrorMessage(503, "voice_unavailable"), /TYPED CHAT/)
})

test("cleanup closes every media resource and detaches remote audio", () => {
  const calls = []
  const channel = {close: () => calls.push("channel")}
  const peer = {close: () => calls.push("peer")}
  const media = {
    getTracks: () => [
      {stop: () => calls.push("track-one")},
      {stop: () => calls.push("track-two")},
    ],
  }
  const audio = {srcObject: {id: "remote-stream"}}

  closeVoiceResources({channel, peer, media, audio})

  assert.deepEqual(calls, ["channel", "peer", "track-one", "track-two"])
  assert.equal(audio.srcObject, null)
})