Skip to content

WebSocket Test Harness

Eric Fitzgerald edited this page Apr 8, 2026 · 2 revisions

WebSocket Test Harness

A standalone Go application for testing the TMI WebSocket interface for collaborative threat modeling.

Source Location: wstest/ directory in the TMI repository

Features

  • OAuth authentication with TMI test provider using login hints (idp=test)
  • Ticket-based WebSocket authentication (obtains a short-lived ticket from /ws/ticket before connecting)
  • Host mode: Creates threat models, adds participants with random permissions, creates diagrams, and starts collaboration sessions
  • Participant mode: Polls /me/sessions every 5 seconds for available collaboration sessions and joins them; automatically reconnects after disconnection
  • Handles all AsyncAPI message types: participant updates, diagram operations, sync protocol, presenter controls, error/rejection messages, and authorization events
  • Comprehensive structured logging (slog) of all network interactions and WebSocket messages
  • Graceful shutdown on SIGINT/SIGTERM with proper WebSocket close handshake
  • Supports multiple concurrent instances
  • Uses the Gorilla WebSocket library

Building

Use the make target (preferred):

make build-wstest

Or build directly:

cd wstest
go mod tidy
go build -o wstest

Usage

Host Mode

Start as a host to create a new collaboration session:

# Basic host mode
./wstest --user alice --host

# Host mode with participants
./wstest --user alice --host --participants "bob,charlie,dave"

# Custom server
./wstest --server http://localhost:8080 --user alice --host

Participant Mode

Start as a participant to join existing collaboration sessions:

# Join any available session
./wstest --user bob

# Multiple participants
./wstest --user charlie &
./wstest --user dave &

Command Line Options

Option Description Default
--server <url> Server URL localhost:8080
--user <hint> User login hint Required
--host Run in host mode false
--participants <list> Comma-separated list of participant hints (host mode only) Empty

Test Scenarios

Basic Two-User Test

Terminal 1 (Host):

./wstest --user alice --host --participants "bob"

Terminal 2 (Participant):

./wstest --user bob

Multi-User Collaboration

Terminal 1 (Host):

./wstest --user alice --host --participants "bob,charlie,dave"

Terminals 2-4 (Participants):

./wstest --user bob
./wstest --user charlie
./wstest --user dave

Automated Multi-Terminal Test

Use the make target to automatically launch three terminal windows:

make wstest

This launches:

Each instance has a 30-second timeout to prevent runaway processes.

WebSocket Authentication

The harness uses ticket-based authentication for WebSocket connections:

  1. After OAuth login, the harness requests a short-lived ticket from GET /ws/ticket?session_id=<session_id> using the Bearer token.
  2. The WebSocket connection URL includes the ticket as a query parameter: /threat_models/<id>/diagrams/<id>/ws?session_id=<id>&ticket=<ticket>.

This approach avoids sending long-lived access tokens over the WebSocket upgrade request.

Expected WebSocket Messages

All WebSocket messages are logged with timestamps and pretty-printed JSON. The harness handles the following message types:

Server to Client Messages

Message Type Description
participants_update Full list of current participants, host, and current presenter
diagram_operation_event A diagram operation applied by another user (includes operation_id, sequence_number, update_vector, operation)
diagram_state Full diagram state with all cells (sent on sync)
sync_status_response Current update_vector for the diagram
error Server error with error, message, code, and timestamp fields
operation_rejected A submitted operation was rejected (includes reason, affected_cells, requires_resync)
presenter_cursor Presenter's cursor position (x, y coordinates)
presenter_selection Cells currently selected by the presenter
presenter_request_event Notification to host that a user is requesting presenter role
presenter_denied_event Notification that a presenter request was denied
authorization_denied An operation was denied due to insufficient permissions

Client to Server Messages

Message Type Description
diagram_operation_request Submit a diagram operation (includes operation_id, base_vector, operation)
sync_status_request Request current sync status
sync_request Request diagram state sync (optionally from a specific update_vector)
undo_request Request undo of the last operation
redo_request Request redo of the last undone operation
presenter_request Request to become the presenter
change_presenter_request Request to change the presenter to another user
presenter_denied_request Host denies a presenter request (includes denied_user)
remove_participant_request Remove a participant from the session (includes removed_user)

participants_update

Full list of current participants (includes presenter info if any).

Structure:

{
  "message_type": "participants_update",
  "initiating_user": { /* user who triggered the update, or null for system events */ },
  "participants": [
    {
      "user": {
        "principal_type": "user",
        "provider": "tmi",
        "provider_id": "alice@tmi.local",
        "email": "alice@tmi.local",
        "display_name": "Alice (TMI User)"
      },
      "permissions": "owner",
      "last_activity": "2025-01-24T12:00:00Z"
    }
  ],
  "host": { /* host user object */ },
  "current_presenter": { /* presenter user object, or null if no presenter */ }
}

OAuth Flow

The harness implements the OAuth authorization code flow:

  1. Starts a local HTTP server on a random port for the callback.
  2. Makes a GET request to /oauth2/authorize?idp=test&login_hint=<user>&client_callback=<url>&scope=openid+email+profile.
  3. Receives a redirect response from the server pointing to the local callback.
  4. Follows the redirect by making a GET request to the callback URL (headless -- no browser involved).
  5. The callback handler extracts the authorization code and exchanges it for tokens via POST to /oauth2/token (JSON body with grant_type, code, redirect_uri).
  6. Calls GET /oauth2/userinfo with the Bearer token to ensure the user is created in the database.
  7. Uses the access token for all subsequent API calls and WebSocket ticket requests.

Note: The harness uses idp=test, the TMI development OAuth provider that creates test users based on login hints. The callback handler also supports implicit flow (tokens returned directly as query parameters) as a fallback.

Logging

The harness uses Go's slog structured logging (via the TMI slogging package). All network interactions are logged to the console:

  • HTTP requests show method, URL, and request bodies (at DEBUG level)
  • HTTP responses show status codes and response bodies (at DEBUG level)
  • WebSocket messages are parsed by type and logged with relevant fields at INFO level; full JSON is logged at DEBUG level
  • OAuth callback parameters are logged in detail at INFO level
  • Errors and warnings use appropriate log levels (ERROR, WARN)

Exit

Use Ctrl+C to gracefully shutdown the application. The WebSocket connection will be properly closed.

Make Targets

Target Description
make build-wstest Build the WebSocket test harness binary
make wstest Launch 3-terminal test (alice as host, bob & charlie as participants)
make monitor-wstest Run WebSocket harness with user 'monitor' in foreground
make clean-wstest Stop all running WebSocket test harness instances

Troubleshooting

Connection Fails Immediately

Check:

  1. Verify the server is running (make start-dev).
  2. Verify the server URL is correct (default is localhost:8080).
  3. Confirm the OAuth flow completes successfully (check console output).

No Sessions Found (Participant Mode)

Check:

  1. Verify the host has started a session first.
  2. Verify the participant user has been added to the threat model authorization.
  3. Confirm the session is still active (not timed out).

WebSocket Disconnects

Check:

  1. Review server logs for errors.
  2. Confirm the session has not been terminated by the host.
  3. Verify network connectivity.

Related Pages

Home

Releases


Getting Started

Deployment

Operation

Troubleshooting

Development

Integrations

Tools

API Reference

Reference

Clone this wiki locally