diff --git a/go-socket b/go-socket index 80bc2b8..c3928b6 100755 Binary files a/go-socket and b/go-socket differ diff --git a/postgres/docker-compose.yml b/postgres/docker-compose.yml index 2201298..f4a5a19 100644 --- a/postgres/docker-compose.yml +++ b/postgres/docker-compose.yml @@ -7,4 +7,4 @@ services: ports: - "5432:5432" volumes: - - ./data:/var/lib/postgresql/data \ No newline at end of file + - ./data:/var/lib/postgresql/data diff --git a/tests/.state b/tests/.state new file mode 100644 index 0000000..c9eb8ef --- /dev/null +++ b/tests/.state @@ -0,0 +1,5 @@ +TOKEN1=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZXhwIjoxNzc0ODYzMTYxLCJpYXQiOjE3NzQ4NTk1NjF9.Xc2AdvB-OppMH_Xbudlf7roy81mLJW_OkZwriVP6tck +USER1_ID=1 +TOKEN2=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyIiwiZXhwIjoxNzc0ODYzMTYxLCJpYXQiOjE3NzQ4NTk1NjF9.EOY84pt8UUrIsGWAVgVUDUJdGxdll6Db-lFFKw4HeA4 +USER2_ID=2 +GROUP_ID=1 diff --git a/tests/01_create_accounts.sh b/tests/01_create_accounts.sh new file mode 100755 index 0000000..92ed3d0 --- /dev/null +++ b/tests/01_create_accounts.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Create two user accounts +source "$(dirname "$0")/config.sh" + +echo "=== Creating account: $USER1_NAME ===" +RESP1=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/new/client" \ + -d "username=$USER1_NAME" \ + -d "password=$USER1_PASS" \ + -d "color=$USER1_COLOR") +BODY1=$(echo "$RESP1" | head -1) +CODE1=$(echo "$RESP1" | tail -1) +echo "Response: $BODY1 (HTTP $CODE1)" + +echo "" +echo "=== Creating account: $USER2_NAME ===" +RESP2=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/new/client" \ + -d "username=$USER2_NAME" \ + -d "password=$USER2_PASS" \ + -d "color=$USER2_COLOR") +BODY2=$(echo "$RESP2" | head -1) +CODE2=$(echo "$RESP2" | tail -1) +echo "Response: $BODY2 (HTTP $CODE2)" diff --git a/tests/02_login.sh b/tests/02_login.sh new file mode 100755 index 0000000..57d57aa --- /dev/null +++ b/tests/02_login.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Login both users and save tokens +source "$(dirname "$0")/config.sh" + +echo "=== Logging in as $USER1_NAME ===" +TOKEN1=$(curl -s -X POST "$BASE_URL/new/token" \ + -d "username=$USER1_NAME" \ + -d "password=$USER1_PASS") +echo "Token1: $TOKEN1" +save_state "TOKEN1" "$TOKEN1" + +USER1_ID=$(decode_jwt_sub "$TOKEN1") +echo "User1 ID: $USER1_ID" +save_state "USER1_ID" "$USER1_ID" + +echo "" +echo "=== Logging in as $USER2_NAME ===" +TOKEN2=$(curl -s -X POST "$BASE_URL/new/token" \ + -d "username=$USER2_NAME" \ + -d "password=$USER2_PASS") +echo "Token2: $TOKEN2" +save_state "TOKEN2" "$TOKEN2" + +USER2_ID=$(decode_jwt_sub "$TOKEN2") +echo "User2 ID: $USER2_ID" +save_state "USER2_ID" "$USER2_ID" diff --git a/tests/03_create_group.sh b/tests/03_create_group.sh new file mode 100755 index 0000000..ae1d5cd --- /dev/null +++ b/tests/03_create_group.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Create a group as user1 +source "$(dirname "$0")/config.sh" + +TOKEN1=$(load_state "TOKEN1") +if [[ -z "$TOKEN1" ]]; then + echo "ERROR: No token found. Run 02_login.sh first." + exit 1 +fi + +echo "=== Creating group: $GROUP_NAME ===" +# Pipe curl directly to od to avoid null bytes being lost in bash variables +GROUP_ID=$(curl -s -X POST "$BASE_URL/new/group" \ + -d "token=$TOKEN1" \ + -d "name=$GROUP_NAME" \ + -d "color=$GROUP_COLOR" \ + | od -An -tu4 -N4 --endian=big | tr -d ' ') +echo "Group ID: $GROUP_ID" +save_state "GROUP_ID" "$GROUP_ID" diff --git a/tests/04_add_user_to_group.sh b/tests/04_add_user_to_group.sh new file mode 100755 index 0000000..e2036a6 --- /dev/null +++ b/tests/04_add_user_to_group.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Add user2 to the group (user1 is owner) +source "$(dirname "$0")/config.sh" + +TOKEN1=$(load_state "TOKEN1") +GROUP_ID=$(load_state "GROUP_ID") +USER2_ID=$(load_state "USER2_ID") + +if [[ -z "$TOKEN1" || -z "$GROUP_ID" || -z "$USER2_ID" ]]; then + echo "ERROR: Missing state. Run previous scripts first." + echo " TOKEN1=$TOKEN1" + echo " GROUP_ID=$GROUP_ID" + echo " USER2_ID=$USER2_ID" + exit 1 +fi + +echo "=== Adding user2 (ID: $USER2_ID) to group $GROUP_ID ===" +RESP=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/mod/group/addclients" \ + -d "token=$TOKEN1" \ + -d "groupid=$GROUP_ID" \ + -d "users=$USER2_ID") +BODY=$(echo "$RESP" | head -1) +CODE=$(echo "$RESP" | tail -1) +echo "Response: $BODY (HTTP $CODE)" diff --git a/tests/05_send_message.sh b/tests/05_send_message.sh new file mode 100755 index 0000000..283ce3e --- /dev/null +++ b/tests/05_send_message.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Send a message on the group via WebSocket (requires websocat) +source "$(dirname "$0")/config.sh" + +TOKEN1=$(load_state "TOKEN1") +GROUP_ID=$(load_state "GROUP_ID") + +echo "=== Debug: loaded state ===" +echo " TOKEN1: ${TOKEN1:0:20}..." +echo " GROUP_ID: $GROUP_ID" + +if [[ -z "$TOKEN1" || -z "$GROUP_ID" ]]; then + echo "ERROR: Missing state. Run previous scripts first." + exit 1 +fi + +if ! command -v websocat &>/dev/null; then + echo "ERROR: websocat is not installed." + echo "Install it with: cargo install websocat" + echo " or: pacman -S websocat" + exit 1 +fi + +AUTH_MSG="{\"token\":\"$TOKEN1\"}" +CHAT_MSG="{\"subject\":$GROUP_ID,\"content\":\"Hello from user1!\"}" + +echo "" +echo "=== Sending message to group $GROUP_ID as user1 ===" +echo " Auth payload: $AUTH_MSG" +echo " Chat payload: $CHAT_MSG" +echo "" + +echo "[*] Connecting to ws://localhost:8080/ws ..." + +# Use a temp file to capture all ws output +WS_OUT=$(mktemp) +trap "rm -f $WS_OUT" EXIT + +( + echo "[>] Sending auth message..." >&2 + echo "$AUTH_MSG" + sleep 2 + echo "[>] Auth sent. Sending chat message..." >&2 + echo "$CHAT_MSG" + sleep 2 + echo "[>] Chat message sent. Closing..." >&2 +) | websocat -v ws://localhost:8080/ws 2>&1 | tee "$WS_OUT" & + +WS_PID=$! + +# Wait for websocat to finish or timeout after 10s +WAITED=0 +while kill -0 $WS_PID 2>/dev/null && [[ $WAITED -lt 10 ]]; do + sleep 1 + WAITED=$((WAITED + 1)) +done + +if kill -0 $WS_PID 2>/dev/null; then + echo "[!] websocat still running after ${WAITED}s, killing..." + kill $WS_PID 2>/dev/null + wait $WS_PID 2>/dev/null +fi + +echo "" +echo "=== WebSocket output ===" +cat "$WS_OUT" +echo "" +echo "=== Done ===" diff --git a/tests/cleanup.sh b/tests/cleanup.sh new file mode 100755 index 0000000..efbc338 --- /dev/null +++ b/tests/cleanup.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Clean up state file left by test scripts +DIR="$(dirname "$0")" +STATE_FILE="$DIR/.state" + +if [[ -f "$STATE_FILE" ]]; then + echo "Removing $STATE_FILE" + rm "$STATE_FILE" + echo "Done." +else + echo "Nothing to clean up." +fi diff --git a/tests/config.sh b/tests/config.sh new file mode 100755 index 0000000..f9f7f94 --- /dev/null +++ b/tests/config.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Shared config for all test scripts + +BASE_URL="http://localhost:8080" + +USER1_NAME="user_one" +USER1_PASS="password1234" +USER1_COLOR="255,0,0" + +USER2_NAME="user_two" +USER2_PASS="password5678" +USER2_COLOR="0,0,255" + +GROUP_NAME="TestGroup" +GROUP_COLOR="0,255,0" + +# File to persist state between scripts +STATE_FILE="$(dirname "$0")/.state" + +save_state() { + local key="$1" value="$2" + touch "$STATE_FILE" + # Remove existing key if present, then append + sed -i "/^${key}=/d" "$STATE_FILE" + echo "${key}=${value}" >> "$STATE_FILE" +} + +load_state() { + local key="$1" + if [[ -f "$STATE_FILE" ]]; then + grep "^${key}=" "$STATE_FILE" | cut -d'=' -f2- + fi +} + +decode_jwt_sub() { + local token="$1" + echo "$token" | cut -d'.' -f2 | base64 -d 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['sub'])" +} diff --git a/tests/run_all.sh b/tests/run_all.sh new file mode 100755 index 0000000..cddf3dd --- /dev/null +++ b/tests/run_all.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Run the full test flow end-to-end +set -e + +DIR="$(dirname "$0")" + +# Clean previous state +rm -f "$DIR/.state" + +echo "=============================" +echo " Step 1: Create accounts" +echo "=============================" +bash "$DIR/01_create_accounts.sh" +echo "" + +echo "=============================" +echo " Step 2: Login" +echo "=============================" +bash "$DIR/02_login.sh" +echo "" + +echo "=============================" +echo " Step 3: Create group" +echo "=============================" +bash "$DIR/03_create_group.sh" +echo "" + +echo "=============================" +echo " Step 4: Add user2 to group" +echo "=============================" +bash "$DIR/04_add_user_to_group.sh" +echo "" + +echo "=============================" +echo " Step 5: Send message" +echo "=============================" +bash "$DIR/05_send_message.sh" +echo "" + +echo "=============================" +echo " Cleanup" +echo "=============================" +bash "$DIR/cleanup.sh" diff --git a/wsServer.go b/wsServer.go index 3e322b7..72dd064 100644 --- a/wsServer.go +++ b/wsServer.go @@ -146,12 +146,29 @@ func handleUnauthenticatedMessage(ctx context.Context, client *Client, clientMes clientFromCache, err := CacheGetClientById(clientId) if err != nil { - var msg = map[string]any{ - "from": "server", - "error": "invalid token", + // Not in cache — load from database + dbClient := &Client{Id: clientId} + err = DbSetClientByIdWithoutGroups(ctx, dbClient) + if err != nil { + var msg = map[string]any{ + "from": "server", + "error": "invalid client data", + } + sendMessageCloseIfTimeout(client, &msg) + return false } - sendMessageCloseIfTimeout(client, &msg) - return false + err = DbSetClientGroups(ctx, dbClient) + if err != nil { + var msg = map[string]any{ + "from": "server", + "error": "invalid client data", + } + sendMessageCloseIfTimeout(client, &msg) + return false + } + dbClient.WsConn = client.WsConn + CacheSaveClient(dbClient) + clientFromCache = dbClient } *client = *clientFromCache