Rename the entire workspace:
- Crate packages: quicnprotochat-{core,proto,server,client,gui,p2p,mobile} -> quicproquo-*
- Binary names: quicnprotochat -> qpq, quicnprotochat-server -> qpq-server,
quicnprotochat-gui -> qpq-gui
- Default files: *-state.bin -> qpq-state.bin, *-server.toml -> qpq-server.toml,
*.db -> qpq.db
- Environment variable prefix: QUICNPROTOCHAT_* -> QPQ_*
- App identifier: chat.quicnproto.gui -> chat.quicproquo.gui
- Proto package: quicnprotochat.bench -> quicproquo.bench
- All documentation, Docker, CI, and script references updated
HKDF domain-separation strings and P2P ALPN remain unchanged for
backward compatibility with existing encrypted state and wire protocol.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
153 lines
5.9 KiB
Bash
Executable File
153 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ── Interactive Chat Test ─────────────────────────────────────────────────────
|
|
#
|
|
# Spins up a server + two client containers (Alice & Bob), sets up an MLS
|
|
# group, and opens a tmux session with side-by-side interactive chat panes.
|
|
#
|
|
# Usage:
|
|
# ./scripts/chat-test.sh
|
|
#
|
|
# Requirements: docker, docker compose, tmux
|
|
# Exit: Ctrl+D in both panes, or: tmux kill-session -t qpc-chat
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
COMPOSE_FILE="$PROJECT_ROOT/docker/docker-compose.chat-test.yml"
|
|
COMPOSE="docker compose -f $COMPOSE_FILE -p qpc-chat-test"
|
|
|
|
# ── Colors ────────────────────────────────────────────────────────────────────
|
|
|
|
GREEN='\033[0;32m'
|
|
CYAN='\033[0;36m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m'
|
|
step() { echo -e "${GREEN}==> $1${NC}"; }
|
|
info() { echo -e " ${CYAN}$1${NC}"; }
|
|
error() { echo -e "${RED}ERROR: $1${NC}" >&2; }
|
|
|
|
# ── Cleanup on exit ──────────────────────────────────────────────────────────
|
|
|
|
cleanup() {
|
|
echo ""
|
|
step "Tearing down containers..."
|
|
$COMPOSE down -v --remove-orphans 2>/dev/null || true
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
# ── Preflight checks ─────────────────────────────────────────────────────────
|
|
|
|
if ! command -v docker &>/dev/null; then
|
|
error "docker is required but not installed."
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker compose version &>/dev/null; then
|
|
error "docker compose (v2 plugin) is required."
|
|
echo " See: https://docs.docker.com/compose/install/"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v tmux &>/dev/null; then
|
|
error "tmux is required but not installed."
|
|
echo " macOS: brew install tmux"
|
|
echo " Linux: sudo apt-get install tmux"
|
|
exit 1
|
|
fi
|
|
|
|
# ── Step 1: Build ─────────────────────────────────────────────────────────────
|
|
|
|
step "Building Docker image (server + client)..."
|
|
$COMPOSE build
|
|
|
|
# ── Step 2: Start services ────────────────────────────────────────────────────
|
|
|
|
step "Starting server, alice, bob..."
|
|
$COMPOSE up -d --wait
|
|
|
|
# ── Step 3: Verify server reachable via QUIC ──────────────────────────────────
|
|
|
|
step "Verifying QUIC connectivity from alice..."
|
|
$COMPOSE exec -T alice qpq health
|
|
|
|
# ── Step 4: Alice — register identity + upload KeyPackage ─────────────────────
|
|
|
|
step "Alice: register-state..."
|
|
$COMPOSE exec -T alice qpq register-state --state /chat/alice.bin
|
|
|
|
ALICE_KEY=$($COMPOSE exec -T alice qpq whoami --state /chat/alice.bin \
|
|
| grep 'identity_key' | awk '{print $3}' | tr -d '[:space:]')
|
|
info "Alice identity: ${ALICE_KEY}"
|
|
|
|
if [ -z "$ALICE_KEY" ]; then
|
|
error "Failed to extract Alice's identity key."
|
|
exit 1
|
|
fi
|
|
|
|
# ── Step 5: Bob — register identity + upload KeyPackage ───────────────────────
|
|
|
|
step "Bob: register-state..."
|
|
$COMPOSE exec -T bob qpq register-state --state /chat/bob.bin
|
|
|
|
BOB_KEY=$($COMPOSE exec -T bob qpq whoami --state /chat/bob.bin \
|
|
| grep 'identity_key' | awk '{print $3}' | tr -d '[:space:]')
|
|
info "Bob identity: ${BOB_KEY}"
|
|
|
|
if [ -z "$BOB_KEY" ]; then
|
|
error "Failed to extract Bob's identity key."
|
|
exit 1
|
|
fi
|
|
|
|
# ── Step 6: Alice creates group ───────────────────────────────────────────────
|
|
|
|
step "Alice: create-group 'docker-chat'..."
|
|
$COMPOSE exec -T alice qpq create-group \
|
|
--state /chat/alice.bin \
|
|
--group-id docker-chat
|
|
|
|
# ── Step 7: Alice invites Bob ─────────────────────────────────────────────────
|
|
|
|
step "Alice: invite Bob..."
|
|
$COMPOSE exec -T alice qpq invite \
|
|
--state /chat/alice.bin \
|
|
--peer-key "$BOB_KEY"
|
|
|
|
# ── Step 8: Bob joins ─────────────────────────────────────────────────────────
|
|
|
|
step "Bob: join group..."
|
|
$COMPOSE exec -T bob qpq join --state /chat/bob.bin
|
|
|
|
# ── Step 9: Launch tmux ──────────────────────────────────────────────────────
|
|
|
|
step "Launching interactive chat (tmux side-by-side)..."
|
|
echo ""
|
|
echo " Left pane: Alice"
|
|
echo " Right pane: Bob"
|
|
echo ""
|
|
echo " Type a message + Enter to send."
|
|
echo " Ctrl+D exits a pane."
|
|
echo " tmux kill-session -t qpc-chat to stop."
|
|
echo ""
|
|
|
|
ALICE_CMD="$COMPOSE exec alice qpq chat --state /chat/alice.bin"
|
|
BOB_CMD="$COMPOSE exec bob qpq chat --state /chat/bob.bin"
|
|
|
|
# Kill any stale tmux session with the same name.
|
|
tmux kill-session -t qpc-chat 2>/dev/null || true
|
|
|
|
# Create tmux session with Alice in the first pane, Bob in a horizontal split.
|
|
tmux new-session -d -s qpc-chat -x 200 -y 50 "$ALICE_CMD"
|
|
tmux split-window -h -t qpc-chat "$BOB_CMD"
|
|
|
|
# Label panes (tmux >= 2.6).
|
|
tmux select-pane -t qpc-chat:0.0 -T "Alice"
|
|
tmux select-pane -t qpc-chat:0.1 -T "Bob"
|
|
tmux set-option -t qpc-chat pane-border-status top 2>/dev/null || true
|
|
tmux set-option -t qpc-chat pane-border-format " #{pane_title} " 2>/dev/null || true
|
|
|
|
# Attach — blocks until the user exits tmux.
|
|
tmux attach-session -t qpc-chat
|
|
|
|
step "Chat session ended."
|