Files
OmniRoute/tests/unit/conductor-faro-chat.test.ts
Diego Rodrigues de Sa e Souza 3638adeeae feat(dashboard): Faro chat with push-to-talk voice on the Conductor panel (PRD RF4) (#8222)
* feat(a2a): conductor bridge core — event mapping with canceled->cancelled

* feat(a2a): incremental SSE parser for conductor bridge

* feat(a2a): conductor bridge connection loop with persisted cursor and backoff

* feat(a2a): start conductor bridge at boot behind CONDUCTOR_HUB_URL

* fix(a2a): conductor bridge parses the hub's real SSE wire format (id/type in frame, data={ts,payload})

* docs(reference): document CONDUCTOR_HUB_URL/TOKEN in ENVIRONMENT.md (env-doc-sync gate)

* feat(a2a): fleet skills derived from the Conductor hub for the agent card

* feat(a2a): agent card announces Conductor fleet skills

* feat(dashboard): server-side hub proxy for the Conductor panel (whitelisted shapes, fail-open)

* feat(dashboard): /api/conductor proxy routes (fleet, task detail, cancel) behind management auth

* feat(dashboard): Conductor panel — fleet live view, task detail and cancel over /api/conductor proxy

* feat(dashboard): /api/conductor/ask — server-side proxy to Faro (spokesperson) with whitelisted {text,pending}

* chore(env): CONDUCTOR_SPOKESPERSON_URL declared in schema, .env.example and ENVIRONMENT.md

* feat(dashboard): Faro chat with push-to-talk voice on the Conductor panel

---------

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 02:00:20 -03:00

40 lines
1.6 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
// Invariantes de segurança/UX do chat com voz (componentes React: vitest-ui advisory + dashboard-typecheck).
const CHAT = "src/app/(dashboard)/dashboard/conductor/FaroChat.tsx";
function src(): string {
return fs.readFileSync(path.join(process.cwd(), CHAT), "utf8");
}
test("client fala SÓ com o OmniRoute: /api/conductor/ask + /api/v1/audio/* (nunca Faro/hub direto)", () => {
const s = src();
assert.match(s, /"use client"/);
assert.match(s, /\/api\/conductor\/ask/);
assert.match(s, /\/api\/v1\/audio\/transcriptions/);
assert.match(s, /\/api\/v1\/audio\/speech/);
assert.ok(!s.includes(":7920"), "endereço do Faro nunca no client");
assert.ok(!s.includes("CONDUCTOR_"), "nenhuma env do Conductor no client");
});
test("pending do Faro → botões Sim/Não que enviam 'sim'/'não' (trava de confirmação é do motor do Faro)", () => {
const s = src();
assert.match(s, /pending/);
assert.match(s, /"sim"/);
assert.match(s, /"não"/);
});
test("voz: push-to-talk com MediaRecorder/getUserMedia; STT multipart sem Content-Type manual; TTS via Blob com revoke", () => {
const s = src();
assert.match(s, /navigator\.mediaDevices\.getUserMedia/);
assert.match(s, /MediaRecorder/);
assert.match(s, /FormData\(\)/);
assert.ok(!/audio\/transcriptions[\s\S]{0,300}content-type/i.test(s), "multipart deixa o browser definir o boundary");
assert.match(s, /URL\.createObjectURL/);
assert.match(s, /URL\.revokeObjectURL/);
assert.match(s, /useTranslations\("conductor"\)/);
});