#!/bin/bash usage () { echo "Usage:$0 " } if [ $# -lt 1 ]; then echo "Error: No test URL given." >&2 echo "" >&2 usage >&2 exit 1 fi URL=$1 CURL="curl --silent --show-error" _TEST_SECTION() { echo "" echo $1 echo $1 | sed -e "s/./$2/g" } TEST_SECTION() { _TEST_SECTION "$1" = } TEST_SUBSECTION() { _TEST_SECTION "$1" - } TEST() { printf " $1" printf "%*s" $(( 52 - ${#1} )) | tr ' ' '.' (( tests_total++ )) || true } # Result of test depends on the exit status of last command TEST_END() { if [ $? -eq 0 ]; then echo -n "OK" else (( tests_failed++ )) || true echo -n "FAIL" fi # If we got an argument, append it after test result if [ -n "$1" ]; then echo " $1" else echo "" fi } # Print report of all previous test results TEST_REPORT() { echo "" echo "Test Report" echo "===========" if [ "$tests_failed" == "" ]; then echo "All $tests_total tests passed." echo "" return 0 else echo "$tests_failed of $tests_total tests failed." echo "" return 1 fi } # Does a string contain a regular expression pattern # # Example: # # contains "All's well that ends well" "s.well" contains() { grep -q "$2" <<< $1 } # POST to a URL endpoint with optional JSON data # # Usage: # # curl_post [data] [CURL_OPTIONS] curl_post() { $CURL ${3:-} -X POST ${2:+-H 'Content-Type: application/json' -d "$2"} $URL/$1 } # PUT to a URL endpoint with optional JSON data # # Usage: # # curl_post [data] [CURL_OPTIONS] curl_put() { $CURL ${3:-} -X PUT ${2:+-H 'Content-Type: application/json' -d "$2"} $URL/$1 } # GET from a URL endpoint # # Usage: # # curl_get [CURL_OPTIONS] curl_get() { $CURL ${2:-} $URL/$1 } # Create a new game of the specified engine type # # Usage: # # new_game new_game() { curl_post new/$1 | jq -r . } TEST_SECTION "LMNO (super-site for games)" TEST_SUBSECTION "Testing home page" home_page=$($CURL $URL) TEST "Contains 'Join Game'" contains "$home_page" "Join Game" TEST_END TEST "Contains 'Host a new game'" contains "$home_page" "Host a new game" TEST_END TEST_SUBSECTION "Creating some new games" TEST "Empires" empires_game_id=$(new_game empires) test "$empires_game_id" != "" TEST_END $empires_game_id TEST "Tic Tac Toe" tictactoe_game_id=$(new_game tictactoe) test "$tictactoe_game_id" != "" TEST_END $tictactoe_game_id TEST_SUBSECTION "Test redirects" TEST "Redirect of /GAMEID at top level" redirect=$(curl_get $empires_game_id) test "$redirect" = "Moved Permanently. Redirecting to /empires/$empires_game_id/" TEST_END TEST "Redirect of lowercase /gameid at top level" empires_game_id_lower=$(tr '[:upper:]' '[:lower:]' <<< $empires_game_id) redirect=$(curl_get $empires_game_id_lower) test "$redirect" = "Moved Permanently. Redirecting to /$empires_game_id/" TEST_END TEST "Redirect of lowercase /empires/gameid" redirect=$(curl_get empires/$empires_game_id_lower) test "$redirect" = "Moved Permanently. Redirecting to /empires/$empires_game_id/" TEST_END TEST_SECTION "Empires game" empires_game_path=empires/$empires_game_id TEST_SUBSECTION "Empires game /register" empires_register() { curl_post $empires_game_path/register "{\"name\": \"$1\", \"character\": \"$2\"}" } empires_players_string() { curl_get $empires_game_path/players | jq -r .[].name | tr '\n' ',' } empires_characters_string() { curl_get $empires_game_path/characters | jq -r .[] | tr '\n' ',' } TEST "Registering a player returns an ID" carl_id=$(empires_register Carl "Bugs Bunny" | jq -r .) test "$carl_id" = "1" TEST_END TEST "Registering several more players" empires_register Richard "Bob Hope" > /dev/null empires_register Kevin "Elvis Presley" > /dev/null empires_register Stacy Phineas > /dev/null empires_register David "Red Power Ranger" > /dev/null empires_register Nancy "Audrey Hepburn" > /dev/null bogus_id=$(empires_register Bogus "Mr. Bogus") TEST_END TEST 'Verify complete players list (with "Bogus")' players=$(empires_players_string) test "$players" = "Carl,Richard,Kevin,Stacy,David,Nancy,Bogus," TEST_END TEST 'Verify complete players list (with "Mr. Bogus")' characters=$(empires_characters_string) test "$characters" = "Bugs Bunny,Bob Hope,Elvis Presley,Phineas,Red Power Ranger,Audrey Hepburn,Mr. Bogus," TEST_END TEST_SUBSECTION "Empires game /deregister" empires_deregister() { curl_post $empires_game_path/deregister/$1 } TEST "Removing the bogus player" empires_deregister $bogus_id TEST_END TEST 'Verify modified players list (w/o "Bogus")"' players=$(empires_players_string) test "$players" = "Carl,Richard,Kevin,Stacy,David,Nancy," TEST_END TEST 'Verify modified characters list (w/o "Mr. Bogus")' characters=$(empires_characters_string) test "$characters" = "Bugs Bunny,Bob Hope,Elvis Presley,Phineas,Red Power Ranger,Audrey Hepburn," TEST_END TEST_SUBSECTION "Empires game /capture" empires_capture() { curl_post $empires_game_path/capture/$1/$2 } empires_empires_string() { # Get empires as a compact string (much more compact than JSON) curl_get $empires_game_path/empires | jq -c '.[] | [.id,.captures]' | tr '\n' ',' } TEST "Verify empires before any captures" empires=$(empires_empires_string) test "$empires" = "[1,[]],[2,[]],[3,[]],[4,[]],[5,[]],[6,[]]," TEST_END TEST "Perform some captures" empires_capture 1 2 empires_capture 3 5 empires_capture 4 6 empires_capture 3 4 TEST_END TEST "Verify empires after captures" empires=$(empires_empires_string) test "$empires" = "[1,[2]],[2,[]],[3,[5,4]],[4,[6]],[5,[]],[6,[]]," TEST_END TEST_SUBSECTION "Empires game /liberate" empires_liberate() { curl_post $empires_game_path/liberate/$1 } TEST "Liberate a player" empires_liberate 2 TEST_END TEST "Verify empires after liberate" empires=$(empires_empires_string) test "$empires" = "[1,[]],[2,[]],[3,[5,4]],[4,[6]],[5,[]],[6,[]]," TEST_END TEST_SUBSECTION "Empires game /reset" empires_reset() { curl_post $empires_game_path/reset } TEST "Reset the game" empires_reset TEST_END TEST "Verify players is now empty" players=$(empires_players_string) test "$players" = "" TEST_END TEST_SECTION "Tic Tac Toe game" tictactoe_game_path=tictactoe/$tictactoe_game_id tictactoe_profile() { curl_put /profile "{ \"nickname\": \"$1\" }" "-c .cookie-tictactoe" } tictactoe_move() { curl_post $tictactoe_game_path/move "{ \"move\": $1 }" "-b .cookie-tictactoe" } tictactoe_player_info() { curl_get $tictactoe_game_path/events "-m 0.1 -b .cookie-tictactoe" 2>&1 \ | grep player-info -A 1 \ | grep ^data } tictactoe_player_name() { curl_put $tictactoe_game_path/player "{ \"name\": \"$1\" }" "-b .cookie-tictactoe" } tictactoe_player_team() { curl_put $tictactoe_game_path/player "{ \"team\": \"$1\" }" "-b .cookie-tictactoe" } TEST_SUBSECTION "Tic Tac Toe player-info" TEST "Hit LMNO /profile to set name to 'curl'" tictactoe_profile curl TEST_END TEST "Verify player-info event reports 'curl' name" result=$(tictactoe_player_info) test "$result" = 'data: {"id":1,"name":"curl","team":""}' TEST_END TEST_SUBSECTION "Tic Tac Toe /player" TEST "Change name to 'newname'" tictactoe_player_name newname result=$(tictactoe_player_info) test "$result" = 'data: {"id":1,"name":"newname","team":""}' TEST_END TEST "Change team to 'X'" tictactoe_player_team X result=$(tictactoe_player_info) test "$result" = 'data: {"id":1,"name":"newname","team":"X"}' TEST_END TEST "Change team to 'O'" tictactoe_player_team O result=$(tictactoe_player_info) test "$result" = 'data: {"id":1,"name":"newname","team":"O"}' TEST_END TEST "Verify cannot change team to 'Z'" tictactoe_player_team Z result=$(tictactoe_player_info) test "$result" = 'data: {"id":1,"name":"newname","team":"O"}' TEST_END TEST "Leave current team" tictactoe_player_team "" result=$(tictactoe_player_info) test "$result" = 'data: {"id":1,"name":"newname","team":""}' TEST_END TEST_SUBSECTION "Tic Tac Toe /move" TEST "First move doesn't require a team" result=$(tictactoe_move 0) test "$result" = '{"legal":true}' TEST_END TEST "Second move does require a team" result=$(tictactoe_move 4) test "$result" = '{"legal":false,"message":"It'"'"'s not your turn to move"}' TEST_END TEST "Illegal to move when it's not your turn" tictactoe_player_team X result=$(tictactoe_move 4) test "$result" = '{"legal":false,"message":"It'"'"'s not your turn to move"}' TEST_END TEST "Legal move to center square" tictactoe_player_team O result=$(tictactoe_move 4) test "$result" = '{"legal":true}' TEST_END TEST "Move to center square again is now illegal" tictactoe_player_team X result=$(tictactoe_move 4) test "$result" = '{"legal":false,"message":"Square is already occupied"}' TEST_END TEST_SECTION "Empathy game" TEST_SUBSECTION "Create a game and register 3 players" TEST "Create the game" empathy_game_id=$(new_game empathy) test "$empathy_game_id" != "" TEST_END $empathy_game_id empathy_game_path=empathy/$empathy_game_id empathy_profile() { cookie_file=".cookie-empathy-$1" curl_put /profile "{ \"nickname\": \"$1\" }" "-c $cookie_file" echo $cookie_file } curl_get_event() { curl_get $1 "-m 0.1 $3" 2>&1 \ | grep "^event: $2" -A 1 \ | grep ^data: \ | sed -e 's,^data: *,,' } empathy_player_name() { curl_get_event $empathy_game_path/events player-info "-b $1" | jq -r .name } TEST "Set 'alice' in session" alice=$(empathy_profile alice) test "$alice" = ".cookie-empathy-alice" TEST_END TEST "Register alice and verify name" result=$(empathy_player_name $alice) test "$result" = "alice" TEST_END TEST "Register bob" bob=$(empathy_profile bob) result=$(empathy_player_name $bob) test "$result" = "bob" TEST_END TEST "Register charlie" charlie=$(empathy_profile charlie) result=$(empathy_player_name $charlie) test "$result" = "charlie" TEST_END TEST_SUBSECTION "Category selection" empathy_submit_prompt() { curl_post $empathy_game_path/prompts "{ \"items\": $2, \"prompt\": \"$3\"}" "-b $1" } TEST "Huge numbers are rejected" result=$(empathy_submit_prompt $alice 10000 "10,000 Maniacs") test "$result" = '{"valid":false,"message":"Maximum number of items is 20"}' TEST_END TEST "Submit a category" prompt_id=$(empathy_submit_prompt $alice 4 "4 things on a beach" | jq .id) test "$prompt_id" = "1" TEST_END empathy_vote() { curl_post $empathy_game_path/vote/$2 "" "-b $1" } TEST "Vote on this category" empathy_vote $alice $prompt_id test "$?" = "0" TEST_END empathy_start() { curl_post $empathy_game_path/start/$2 "" "-b $1" } TEST "Start the game with this category" empathy_start $alice $prompt_id test "$?" = "0" TEST_END empathy_answer() { curl_post $empathy_game_path/answer/$2 "{ \"answers\": [$3]}" "-b $1" } TEST_SUBSECTION "Submitting answers" TEST "Submit from a non-player fails" result=$(empathy_answer bogus $prompt_id '"Sun", "Sand", "Water", "People"') test "$result" = '{"valid":false,"message":"Player not found"}' TEST_END TEST "Submit from alice succeeds" result=$(empathy_answer $alice $prompt_id '"sun", "sand", "water", "people"') test "$result" = '{"valid":true}' TEST_END TEST "Submit from bob succeeds" result=$(empathy_answer $bob $prompt_id '"sand", "sands", "SunLight", "towels"') test "$result" = '{"valid":true}' TEST_END TEST "Submit from charlie succeeds" result=$(empathy_answer $charlie $prompt_id '"SunShine", "Grains of Sand", "wafer", "people"') test "$result" = '{"valid":true}' TEST_END TEST_SUBSECTION "Transition from answering to judging" empathy_ambiguities() { curl_get_event $empathy_game_path/events game-state "-b $1" \ | jq .ambiguities } TEST "Judging has not yet started" result=$(echo $(empathy_ambiguities $alice)) test "$result" = "null" TEST_END empathy_end_answers() { curl_post $empathy_game_path/end-answers/$2 "" "-b $1" } TEST "Players vote to start judging" empathy_end_answers $alice $prompt_id empathy_end_answers $bob $prompt_id empathy_end_answers $charlie $prompt_id test "$?" = "0" TEST_END TEST "Judging has now started" result=$(echo $(empathy_ambiguities $alice)) test "$result" != "null" TEST_END TEST_SUBSECTION "Judging answers" empathy_ambiguities_list() { curl_get_event $empathy_game_path/events game-state "-b $1" \ | jq .ambiguities[] } TEST "Received all unique words" # echo here is to strip newlines result=$(echo $(empathy_ambiguities_list $alice)) test "$result" = '"Grains of Sand" "people" "sand" "sands" "sun" "SunLight" "SunShine" "towels" "wafer" "water"' TEST_END empathy_judging() { curl_post $empathy_game_path/judging/$2 "{ \"word_groups\": $3}" "-b $1" } TEST "Submit word groups from alice" result=$(empathy_judging $alice $prompt_id '[["sun","SunLight","SunShine"],["sand","sands","Grains of Sand"],["water","wafer"]]') test "$result" = '{"valid":true}' TEST_END TEST "Submit word groups from bob" result=$(empathy_judging $bob $prompt_id '[["sands","grains of sand"],["water","wafer"]]') test "$result" = '{"valid":true}' TEST_END TEST "Submit word groups from charlie" result=$(empathy_judging $charlie $prompt_id '[["SunLight","SunShine"],["sand","Grains of Sand"]]') test "$result" = '{"valid":true}' TEST_END TEST_SUBSECTION "Transition from judging to scoring" empathy_scores() { curl_get_event $empathy_game_path/events game-state "-b $1" \ | jq .scores } TEST "Scoring has not yet started" result=$(echo $(empathy_scores $alice)) test "$result" = "null" TEST_END empathy_end_judging() { curl_post $empathy_game_path/end-judging/$2 "" "-b $1" } TEST "Players vote to start scoring" empathy_end_judging $alice $prompt_id empathy_end_judging $bob $prompt_id empathy_end_judging $charlie $prompt_id test "$?" = "0" TEST_END TEST "Scoring has now started" result=$(echo $(empathy_scores $alice)) test "$result" != "null" TEST_END empathy_scores_names_numbers() { curl_get_event $empathy_game_path/events game-state "-b $1" \ | jq '.scores.scores[]|.player,.score' } TEST_SUBSECTION "Scoring" TEST "Verify final scores as expected" # echo here is to strip newlines result=$(echo $(empathy_scores_names_numbers $alice)) test "$result" = '"charlie" 9 "alice" 8 "bob" 6' TEST_END empathy_words_submitted() { curl_get_event $empathy_game_path/events game-state "-b $1" \ | jq '.scores.words[].word' } TEST "Verify final list of words submitted" # echo here is to strip newlines result=$(echo $(empathy_words_submitted $alice)) test "$result" = '"Grains of Sand/sand/sands" "SunLight/SunShine" "wafer/water" "people" "sun" "towels"' TEST_END TEST_REPORT