+test "$result" = '{"legal":false,"message":"Square is already occupied"}'
+TEST_END
+
+TEST_SECTION "Scribe game"
+
+TEST "Create Scribe game"
+scribe_game_id=$(new_game scribe)
+test "$scribe_game_id" != ""
+TEST_END $scribe_game_id
+
+scribe_game_path=scribe/$scribe_game_id
+
+# Usage: scribe_profile <name>
+scribe_profile()
+{
+ curl_put /profile "{ \"nickname\": \"$1\" }" "-c .cookie-scribe"
+}
+
+# Pulls a single named event out of the scribe event stream
+#
+# Usage: scribe_get_event <event_name>
+scribe_get_event()
+{
+ curl_get $scribe_game_path/events "-m 0.1 -b .cookie-scribe" 2>&1 \
+ | grep "^event: $1" -A 1 \
+ | grep ^data: \
+ | sed -e 's,^data: *,,'
+}
+
+# Usage: scribe_player_name
+scribe_get_player_name()
+{
+ scribe_get_event player-info | jq -r .name
+}
+
+TEST_SUBSECTION "Scribe player-info"
+
+TEST "Hit LMNO /profile to set name to 'test-suite'"
+scribe_profile test-suite
+TEST_END
+
+TEST "Verify player-info event reports 'test-suite' name"
+result=$(scribe_get_player_name)
+test "$result" = "test-suite"
+TEST_END
+
+scribe_player_info()
+{
+ scribe_get_event player-info
+}
+
+scribe_set_player_name()
+{
+ curl_put $scribe_game_path/player "{ \"name\": \"$1\" }" "-b .cookie-scribe"
+}
+
+scribe_set_player_team()
+{
+ curl_put $scribe_game_path/player "{ \"team\": \"$1\" }" "-b .cookie-scribe"
+}
+
+TEST_SUBSECTION "Scribe /player"
+
+TEST "Change name to 'testy'"
+scribe_set_player_name testy
+result=$(scribe_player_info)
+test "$result" = '{"id":1,"active":true,"name":"testy","team":""}'
+TEST_END
+
+TEST "Change team to '+'"
+scribe_set_player_team +
+result=$(scribe_player_info)
+test "$result" = '{"id":1,"active":true,"name":"testy","team":"+"}'
+TEST_END
+
+TEST "Change team to 'o'"
+scribe_set_player_team o
+result=$(scribe_player_info)
+test "$result" = '{"id":1,"active":true,"name":"testy","team":"o"}'
+TEST_END
+
+TEST "Verify cannot change team to 'X'"
+scribe_set_player_team X
+result=$(scribe_player_info)
+test "$result" = '{"id":1,"active":true,"name":"testy","team":"o"}'
+TEST_END
+
+TEST "Leave current team"
+scribe_set_player_team ""
+result=$(scribe_player_info)
+test "$result" = '{"id":1,"active":true,"name":"testy","team":""}'
+TEST_END
+
+scribe_move()
+{
+ curl_post $scribe_game_path/move "{ \"move\": $1 }" "-b .cookie-scribe"
+}
+
+TEST_SUBSECTION "Scribe /move"
+
+TEST "First move doesn't require a team"
+result=$(scribe_move '[4,0]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Second move does require a team"
+result=$(scribe_move '[0,3]')
+test "$result" = '{"legal":false,"message":"It'"'"'s not your turn to move"}'
+TEST_END
+
+TEST "Illegal to move when it's not your turn"
+scribe_set_player_team +
+result=$(scribe_move '[0,3]')
+test "$result" = '{"legal":false,"message":"It'"'"'s not your turn to move"}'
+TEST_END
+
+TEST "Legal move to an empty square"
+scribe_set_player_team o
+result=$(scribe_move '[0,3]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Move to same again is now illegal"
+scribe_set_player_team +
+result=$(scribe_move '[0,3]')
+test "$result" = '{"legal":false,"message":"Square is already occupied"}'
+TEST_END
+
+TEST "Move must be in correct mini-grid by last move"
+scribe_set_player_team +
+result=$(scribe_move '[1,8]')
+test "$result" = '{"legal":false,"message":"Move is inconsistent with your previous move"}'
+TEST_END
+
+TEST "Move in correct mini-grid is now legal"
+result=$(scribe_move '[0,8]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Several moves to fill up the mini-grid 0"
+scribe_set_player_team o
+scribe_move '[3,0]' >/dev/null
+scribe_set_player_team +
+scribe_move '[8,0]' >/dev/null
+scribe_set_player_team o
+scribe_move '[0,0]' >/dev/null
+scribe_set_player_team +
+scribe_move '[0,1]' >/dev/null
+scribe_set_player_team o
+scribe_move '[0,2]' >/dev/null
+scribe_set_player_team +
+scribe_move '[1,0]' >/dev/null
+scribe_set_player_team o
+scribe_move '[2,0]' >/dev/null
+scribe_set_player_team +
+scribe_move '[0,5]' >/dev/null
+scribe_set_player_team o
+scribe_move '[0,6]' >/dev/null
+scribe_set_player_team +
+scribe_move '[5,0]' >/dev/null
+scribe_set_player_team o
+scribe_move '[6,0]' >/dev/null
+scribe_set_player_team +
+scribe_move '[0,7]' >/dev/null
+scribe_set_player_team o
+scribe_move '[0,4]' >/dev/null
+scribe_set_player_team +
+result=$(scribe_move '[7,0]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Full mini grid allows a free move"
+scribe_set_player_team o
+scribe_move '[4,1]' >/dev/null
+scribe_set_player_team +
+result=$(scribe_move '[1,1]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player o forms Cross glyph"
+scribe_set_player_team o
+scribe_move '[1,4]' >/dev/null
+scribe_set_player_team +
+scribe_move '[1,2]' >/dev/null
+scribe_set_player_team o
+scribe_move '[4,4]' >/dev/null
+scribe_set_player_team +
+scribe_move '[2,1]' >/dev/null
+scribe_set_player_team o
+scribe_move '[4,7]' >/dev/null
+scribe_set_player_team +
+scribe_move '[1,5]' >/dev/null
+scribe_set_player_team o
+scribe_move '[7,4]' >/dev/null
+scribe_set_player_team +
+scribe_move '[5,1]' >/dev/null
+scribe_set_player_team o
+scribe_move '[4,3]' >/dev/null
+scribe_set_player_team +
+scribe_move '[1,8]' >/dev/null
+scribe_set_player_team o
+scribe_move '[3,4]' >/dev/null
+scribe_set_player_team +
+scribe_move '[8,1]' >/dev/null
+scribe_set_player_team o
+result=$(scribe_move '[4,5]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player + forms J glyph"
+scribe_set_player_team +
+result=$(scribe_move '[1,7]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player + forms Earring glyph"
+scribe_set_player_team o
+scribe_move '[5,4]' >/dev/null
+scribe_set_player_team +
+scribe_move '[7,1]' >/dev/null
+scribe_set_player_team o
+scribe_move '[4,6]' >/dev/null
+scribe_set_player_team +
+result=$(scribe_move '[1,3]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player o forms House glyph"
+scribe_set_player_team o
+scribe_move '[6,4]' >/dev/null
+scribe_set_player_team +
+scribe_move '[3,1]' >/dev/null
+scribe_set_player_team o
+result=$(scribe_move '[4,8]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player + forms O glyph"
+scribe_set_player_team +
+result=$(scribe_move '[1,6]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player o forms T glyph"
+scribe_set_player_team o
+scribe_move '[8,6]' >/dev/null
+scribe_set_player_team +
+scribe_move '[6,7]' >/dev/null
+scribe_set_player_team o
+scribe_move '[6,3]' >/dev/null
+scribe_set_player_team +
+scribe_move '[7,8]' >/dev/null
+scribe_set_player_team o
+scribe_move '[3,6]' >/dev/null
+scribe_set_player_team +
+scribe_move '[8,2]' >/dev/null
+scribe_set_player_team o
+scribe_move '[6,6]' >/dev/null
+scribe_set_player_team +
+scribe_move '[2,8]' >/dev/null
+scribe_set_player_team o
+result=$(scribe_move '[6,5]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player o forms Chair glyph"
+scribe_set_player_team +
+scribe_move '[8,5]' >/dev/null
+scribe_set_player_team o
+scribe_move '[5,6]' >/dev/null
+scribe_set_player_team +
+scribe_move '[5,8]' >/dev/null
+scribe_set_player_team o
+result=$(scribe_move '[6,2]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player + forms Bomber glyph"
+scribe_set_player_team +
+scribe_move '[8,8]' >/dev/null
+scribe_set_player_team o
+scribe_move '[2,6]' >/dev/null
+scribe_set_player_team +
+result=$(scribe_move '[8,4]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player o forms H glyph"
+scribe_set_player_team o
+result=$(scribe_move '[6,8]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player + forms 6-Block glyph"
+scribe_set_player_team +
+scribe_move '[4,2]' >/dev/null
+scribe_set_player_team o
+scribe_move '[8,3]' >/dev/null
+scribe_set_player_team +
+scribe_move '[2,4]' >/dev/null
+scribe_set_player_team o
+scribe_move '[3,3]' >/dev/null
+scribe_set_player_team +
+scribe_move '[2,2]' >/dev/null
+scribe_set_player_team o
+scribe_move '[3,7]' >/dev/null
+scribe_set_player_team +
+scribe_move '[2,5]' >/dev/null
+scribe_set_player_team o
+scribe_move '[7,3]' >/dev/null
+scribe_set_player_team +
+scribe_move '[5,2]' >/dev/null
+scribe_set_player_team o
+scribe_move '[3,2]' >/dev/null
+scribe_set_player_team +
+result=$(scribe_move '[2,7]')
+test "$result" = '{"legal":true}'
+TEST_END
+
+TEST "Player o forms Ottoman glyph"
+scribe_set_player_team o
+scribe_move '[2,3]' >/dev/null
+scribe_set_player_team +
+scribe_move '[7,7]' >/dev/null
+scribe_set_player_team o
+scribe_move '[3,5]' >/dev/null
+scribe_set_player_team +
+scribe_move '[7,2]' >/dev/null
+scribe_set_player_team o
+scribe_move '[5,3]' >/dev/null
+scribe_set_player_team +
+scribe_move '[7,6]' >/dev/null
+scribe_set_player_team o
+result=$(scribe_move '[3,8]')
+test "$result"='{"legal":true}'
+TEST_END
+
+TEST "Player + forms J glyph"
+scribe_set_player_team +
+scribe_move '[6,1]' >/dev/null
+scribe_set_player_team o
+scribe_move '[8,7]' >/dev/null
+scribe_set_player_team +
+scribe_move '[5,5]' >/dev/null
+scribe_set_player_team o
+scribe_move '[7,5]' >/dev/null
+scribe_set_player_team +
+result=$(scribe_move '[5,7]')
+test "$result"='{"legal":true}'
+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
+
+# Usage: empathy_get <player_name> <endpoint> [curl_options]
+empathy_get()
+{
+ curl_get_cookie $1 $empathy_game_path/$2 "${3:-}"
+}
+
+# Usage: empathy_post <player_name> <endpoint> [data]
+empathy_post()
+{
+ curl_post_cookie $1 $empathy_game_path/$2 "${3:-}"
+}
+
+# Given a player name as $1 (eg. "empathy_player_activate alice") set both
+# $1_cookie and $1_pid (that is $alice_cookie and $alice_pid) to
+# a filename containing a cookie and the PID of a running event-streaming
+# process.
+empathy_player_activate()
+{
+ player="$1"
+ player_cookie=${player}_cookie
+ player_pid=${player}_pid
+
+ eval ${player_cookie}=".cookie-empathy-$player"
+ curl_put /profile "{ \"nickname\": \"$player\" }" "-c ${!player_cookie}"
+ empathy_get $player events >/dev/null 2>&1 &
+ eval ${player_pid}=$!
+ empathy_players+=($player)
+}
+
+empathy_player_reactivate()
+{
+ player="$1"
+ player_pid=${player}_pid
+
+ empathy_get $player events >/dev/null 2>&1 &
+ eval ${player_pid}=$!
+}
+
+# Usage: empathy_player_deactivate <player_name>
+empathy_player_deactivate()
+{
+ player="$1"
+ player_pid=${player}_pid
+ if [ "${!player_pid}" != "" ]; then
+ pkill -P ${!player_pid}
+ fi
+ eval ${player_pid}=""
+}
+
+empathy_deactivate_all()
+{
+ for player in ${empathy_players[*]}; do
+ empathy_player_deactivate $player
+ done
+}
+
+# Pulls a single named event out of the empathy event stream
+#
+# Usage: empathy_get_event <player_name> <event_name>
+empathy_get_event()
+{
+ empathy_get $1 events "-m 0.1" 2>&1 \
+ | grep "^event: $2" -A 1 \
+ | grep ^data: \
+ | sed -e 's,^data: *,,'
+}
+
+# Usage: empathy_player_name <player_name>
+empathy_player_name()
+{
+ empathy_get_event $1 player-info | jq -r .name
+}
+
+TEST "Set 'alice' in session"
+empathy_player_activate alice
+test "$alice_cookie" = ".cookie-empathy-alice"
+TEST_END
+
+TEST "Register alice and verify name"
+result=$(empathy_player_name alice)
+test "$result" = "alice"
+TEST_END
+
+TEST "Register bob"
+empathy_player_activate bob
+result=$(empathy_player_name bob)
+test "$result" = "bob"