Enter the flags you expect to find in this scenario (comma-separated). These will be validated when you submit them at the flag station during gameplay.
<%= form_with url: break_escape.games_path, method: :post, local: true, class: 'flags-form' do |f| %>
<%= f.hidden_field :mission_id, value: @mission.id %>
@@ -191,6 +203,11 @@
box-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
}
+ .flags-textarea.xml-input {
+ font-size: 12px;
+ line-height: 1.4;
+ }
+
.form-hint {
color: #888;
font-size: 12px;
diff --git a/planning_notes/vms_and_flags/IMPLEMENTATION_PLAN.md b/planning_notes/vms_and_flags/IMPLEMENTATION_PLAN.md
index e376569..af01bc7 100644
--- a/planning_notes/vms_and_flags/IMPLEMENTATION_PLAN.md
+++ b/planning_notes/vms_and_flags/IMPLEMENTATION_PLAN.md
@@ -837,8 +837,8 @@ def create
# Validate VM set belongs to user and matches mission
if BreakEscape::Mission.hacktivity_mode?
- unless @mission.valid_vm_sets_for_user(current_user).include?(vm_set)
- return render json: { error: 'Invalid VM set for this mission' }, status: :forbidden
+ unless @mission.valid_vm_sets_for_user(current_user).include?(vm_set)
+ return render json: { error: 'Invalid VM set for this mission' }, status: :forbidden
end
else
# Standalone mode - vm_set_id shouldn't be used
diff --git a/public/break_escape/assets/objects/vm-launcher-desktop.png b/public/break_escape/assets/objects/vm-launcher-desktop.png
index 2d3c6d2..bf0da56 100644
Binary files a/public/break_escape/assets/objects/vm-launcher-desktop.png and b/public/break_escape/assets/objects/vm-launcher-desktop.png differ
diff --git a/public/break_escape/assets/objects/vm-launcher-kali.png b/public/break_escape/assets/objects/vm-launcher-kali.png
index 44b9ede..9dba279 100644
Binary files a/public/break_escape/assets/objects/vm-launcher-kali.png and b/public/break_escape/assets/objects/vm-launcher-kali.png differ
diff --git a/public/break_escape/js/minigames/flag-station/flag-station-minigame.js b/public/break_escape/js/minigames/flag-station/flag-station-minigame.js
index ac8c573..8c9bd4a 100644
--- a/public/break_escape/js/minigames/flag-station/flag-station-minigame.js
+++ b/public/break_escape/js/minigames/flag-station/flag-station-minigame.js
@@ -13,6 +13,7 @@ export class FlagStationMinigame extends MinigameScene {
this.stationId = params.stationId || 'flag-station';
this.stationName = params.stationName || 'Flag Submission Terminal';
this.expectedFlags = params.flags || [];
+ this.acceptsVms = params.acceptsVms || []; // List of VM names whose flags are accepted
this.submittedFlags = params.submittedFlags || window.gameState?.submittedFlags || [];
this.gameId = params.gameId || window.breakEscapeConfig?.gameId || window.gameConfig?.gameId;
this.isSubmitting = false;
@@ -208,6 +209,29 @@ export class FlagStationMinigame extends MinigameScene {
font-style: italic;
font-size: 13px;
}
+
+ .accepts-vms {
+ margin-top: 15px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+
+ .accepts-label {
+ color: #888;
+ font-size: 12px;
+ }
+
+ .vm-badge {
+ background: #00aa00;
+ color: #000;
+ padding: 4px 12px;
+ font-size: 14px;
+ font-weight: bold;
+ font-family: 'Courier New', monospace;
+ }
`;
this.gameContainer.appendChild(style);
@@ -227,6 +251,14 @@ export class FlagStationMinigame extends MinigameScene {
? `${submittedCount}/${totalCount} flags submitted`
: '';
+ // Show which VMs' flags are accepted at this station
+ const vmBadges = this.acceptsVms.length > 0
+ ? `
`
+ : '';
+
return `
@@ -413,16 +446,38 @@ export class FlagStationMinigame extends MinigameScene {
processRewardEvents(rewards) {
for (const reward of rewards) {
if (reward.type === 'give_item' && reward.item) {
- // Emit item received event
- if (window.eventDispatcher) {
- window.eventDispatcher.emit('item_received', {
- item: reward.item,
- source: 'flag_reward'
- });
+ // Use the standard inventory system to add the item
+ // Server validates flag-station itemsHeld as a valid source
+ if (window.addToInventory) {
+ const itemSprite = {
+ name: reward.item.type,
+ objectId: `flag_reward_${reward.item.name}_${Date.now()}`,
+ scenarioData: reward.item,
+ texture: {
+ key: reward.item.type
+ },
+ // Copy critical properties for keys
+ keyPins: reward.item.keyPins,
+ key_id: reward.item.key_id || reward.item.keyId,
+ setVisible: function() { return this; }
+ };
+
+ console.log('[FlagStation] Adding reward item to inventory:', reward.item.name);
+ window.addToInventory(itemSprite);
+ } else {
+ console.warn('[FlagStation] addToInventory not available');
}
}
if (reward.type === 'unlock_door' && reward.room_id) {
+ // Unlock the room in client-side state
+ if (window.gameState && window.gameState.unlockedRooms) {
+ if (!window.gameState.unlockedRooms.includes(reward.room_id)) {
+ window.gameState.unlockedRooms.push(reward.room_id);
+ console.log('[FlagStation] Unlocked room:', reward.room_id);
+ }
+ }
+
// Emit door unlocked event
if (window.eventDispatcher) {
window.eventDispatcher.emit('door_unlocked', {
diff --git a/public/break_escape/js/systems/interactions.js b/public/break_escape/js/systems/interactions.js
index 467b855..8fa5f06 100644
--- a/public/break_escape/js/systems/interactions.js
+++ b/public/break_escape/js/systems/interactions.js
@@ -656,8 +656,9 @@ export function handleObjectInteraction(sprite) {
stationId: sprite.scenarioData.id || sprite.objectId,
stationName: sprite.scenarioData.name,
flags: sprite.scenarioData.flags || [],
+ acceptsVms: sprite.scenarioData.acceptsVms || [],
submittedFlags: window.gameState?.submittedFlags || [],
- gameId: window.gameConfig?.gameId
+ gameId: window.breakEscapeConfig?.gameId || window.gameConfig?.gameId
});
return;
}
diff --git a/scenarios/secgen_vm_lab/scenario.json.erb b/scenarios/secgen_vm_lab/scenario.json.erb
index 524a008..f8648d2 100644
--- a/scenarios/secgen_vm_lab/scenario.json.erb
+++ b/scenarios/secgen_vm_lab/scenario.json.erb
@@ -68,23 +68,40 @@
"flag_room": {
"type": "room_office",
"connections": {
- "west": "lab_workstation"
+ "west": "lab_workstation",
+ "north": "server_room"
},
"locked": false,
"objects": [
{
"type": "flag-station",
- "id": "flag_station_1",
- "name": "Flag Submission Terminal",
+ "id": "flag_station_desktop",
+ "name": "Desktop Flag Terminal",
"takeable": false,
- "observations": "A secure terminal for submitting captured flags. All submissions are verified against the lab database.",
- "flags": <%= vm_context && vm_context['flags'] ? vm_context['flags'].map { |f| f['value'] }.to_json : '[]' %>,
- "flagRewards": {
- "default_reward": {
- "type": "emit_event",
- "event_name": "flag_captured"
+ "observations": "Submit flags captured from the Desktop VM here.",
+ "acceptsVms": ["desktop"],
+ "flags": <%= flags_for_vm('desktop', ['flag{test_desktop_1}', 'flag{test_desktop_2}']) %>,
+ "flagRewards": [
+ {
+ "type": "give_item",
+ "item_name": "Server Room Keycard",
+ "description": "First flag unlocks access to the server room"
+ },
+ {
+ "type": "unlock_door",
+ "target_room": "server_room",
+ "description": "Second flag unlocks the server room door"
}
- }
+ ],
+ "itemsHeld": [
+ {
+ "type": "keycard",
+ "name": "Server Room Keycard",
+ "keyId": "server_keycard",
+ "takeable": true,
+ "observations": "A keycard with 'SERVER ROOM' printed on it"
+ }
+ ]
},
{
"type": "notes",
@@ -103,6 +120,41 @@
"observations": "Real-time progress tracking for lab completion"
}
]
+ },
+ "server_room": {
+ "type": "room_server",
+ "connections": {
+ "south": "flag_room"
+ },
+ "locked": true,
+ "lockType": "rfid",
+ "requires": ["server_keycard"],
+ "objects": [
+ {
+ "type": "flag-station",
+ "id": "flag_station_kali",
+ "name": "Kali Flag Terminal",
+ "takeable": false,
+ "observations": "Submit flags captured from the Kali VM here. Advanced challenges only.",
+ "acceptsVms": ["kali"],
+ "flags": <%= flags_for_vm('kali', []) %>,
+ "flagRewards": [
+ {
+ "type": "emit_event",
+ "event_name": "kali_flag_captured",
+ "description": "Triggers a custom game event"
+ }
+ ]
+ },
+ {
+ "type": "notes",
+ "name": "Reward Types Guide",
+ "takeable": true,
+ "readable": true,
+ "text": "FLAG REWARD TYPES:\n\n1. give_item - Adds an item to your inventory\n Example: Keycard, tool, document\n\n2. unlock_door - Opens a locked door\n Example: Access to new areas\n\n3. emit_event - Triggers a game event\n Example: NPC dialogue, cutscene, objective completion\n\n4. reveal_secret - Shows hidden information\n Example: Password hints, map locations\n\nRewards are given in order:\n- First flag = first reward\n- Second flag = second reward\n- etc.\n\nSome stations accept flags from specific VMs only!",
+ "observations": "Documentation on the flag reward system"
+ }
+ ]
}
}
}
diff --git a/test/dummy/log/test.log b/test/dummy/log/test.log
index a38670b..cba6fad 100644
--- a/test/dummy/log/test.log
+++ b/test/dummy/log/test.log
@@ -54424,3 +54424,1506 @@ Processing by BreakEscape::MissionsController#index as HTML
Completed 200 OK in 1ms (Views: 0.9ms | ActiveRecord: 0.1ms (5 queries, 1 cached) | GC: 0.0ms)
[1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
[1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mActiveRecord::InternalMetadata Load (0.1ms)[0m [1m[34mSELECT * FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? ORDER BY "ar_internal_metadata"."key" ASC LIMIT 1[0m [[nil, "schema_sha1"]]
+ [1m[36mActiveRecord::SchemaMigration Load (0.0ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+ [1m[35m (0.1ms)[0m [1m[35mPRAGMA foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = ON[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = OFF[0m
+ [1m[36mFixtures Load (0.1ms)[0m [1m[31mDELETE FROM "break_escape_demo_users";
+DELETE FROM "break_escape_missions";
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (149617800, 'test_user', '2025-11-29 01:39:30', '2025-11-29 01:39:30');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-29 01:39:30', '2025-11-29 01:39:30');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (418560898, 'ceo_exfil', 'CEO Exfiltration', 'Test scenario', 1, 3, '2025-11-29 01:39:30', '2025-11-29 01:39:30');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (636030761, 'test_unpublished', 'Unpublished Test', 'Not visible', 0, 1, '2025-11-29 01:39:30', '2025-11-29 01:39:30');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at", "secgen_scenario", "collection") VALUES (899573729, 'secgen_vm_lab', 'SecGen VM Lab - Linux Introduction', 'Test VM and flag integration with SecGen scenario', 1, 2, '2025-11-29 01:39:30', '2025-11-29 01:39:30', 'labs/introducing_attacks/1_intro_linux.xml', 'vm_labs')[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = 0[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = 1[0m
+ [1m[36mTRANSACTION (1.1ms)[0m [1m[36mcommit transaction[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_key_check[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_inventory_endpoint_should_add_items
+--------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.2ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.937304"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.937195"], ["updated_at", "2025-11-29 01:39:30.937195"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Update (0.1ms)[0m [1m[33mUPDATE "break_escape_games" SET "scenario_data" = ?, "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[{\"id\":\"note_1\",\"type\":\"note\",\"name\":\"Test Note\",\"takeable\":true}]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 01:39:30.938409"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/inventory" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#inventory as HTML
+ Parameters: {"action_type"=>"add", "item"=>{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}, "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.1ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] inventory endpoint: action=add, item=#
"note", "name"=>"Test Note", "id"=>"note_1"} permitted: false>
+[BreakEscape] validate_item_collectible: type=note, id=note_1, name=Test Note
+[BreakEscape] Item collection valid: note
+[BreakEscape] Adding item to inventory: note / Test Note
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"note\",\"name\":\"Test Note\",\"id\":\"note_1\"}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 01:39:30.952691"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+[BreakEscape] Item added successfully. Current inventory: [{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}]
+Completed 200 OK in 5ms (Views: 0.1ms | ActiveRecord: 0.3ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_reject_invalid_attempts
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.955442"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.955393"], ["updated_at", "2025-11-29 01:39:30.955393"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"room", "targetId"=>"office", "attempt"=>"wrong_code", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=room, id=office, attempt=wrong_code, method=pin
+[BreakEscape] Object not found: office
+Completed 422 Unprocessable Content in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_inject_game_configuration
+----------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.957922"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.957876"], ["updated_at", "2025-11-29 01:39:30.957876"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 1.6ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.8ms | GC: 0.0ms)
+Completed 200 OK in 7ms (Views: 3.7ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_game_setup_has_correct_scenario_data
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.967352"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.967285"], ["updated_at", "2025-11-29 01:39:30.967285"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_NPC_without_story_file
+------------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.968496"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.968454"], ["updated_at", "2025-11-29 01:39:30.968454"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=missing-npc" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"missing-npc", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: missing-npc
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_accept_correct_pin_code
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.970968"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.970930"], ["updated_at", "2025-11-29 01:39:30.970930"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"door", "targetId"=>"office", "attempt"=>"1234", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=door, id=office, attempt=1234, method=pin
+[BreakEscape] Room data: locked=true, lockType=pin, requires=1234
+[BreakEscape] Room is LOCKED, method must be valid: pin
+[BreakEscape] pin validation result: true
+[BreakEscape] validate_unlock returning: true
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\",\"office\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 01:39:30.972731"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.2ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_sync_state_should_update_player_state_for_current_room
+---------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.974095"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.974056"], ["updated_at", "2025-11-29 01:39:30.974056"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started PUT "/break_escape/games/1/sync_state" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#sync_state as HTML
+ Parameters: {"currentRoom"=>"reception", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------
+BreakEscape::GamesControllerTest: test_should_show_game
+-------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.976807"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.976768"], ["updated_at", "2025-11-29 01:39:30.976768"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.2ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_require_npc_parameter
+--------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.979399"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.979360"], ["updated_at", "2025-11-29 01:39:30.979360"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 400 Bad Request in 0ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_non-existent_NPC
+------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.981473"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.981434"], ["updated_at", "2025-11-29 01:39:30.981434"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=non-existent" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"non-existent", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: non-existent
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_scenario_endpoint_should_return_JSON
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.983539"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.983499"], ["updated_at", "2025-11-29 01:39:30.983499"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/scenario" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#scenario as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_return_HTML_with_game_container
+----------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:30.985712"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:30.985671"], ["updated_at", "2025-11-29 01:39:30.985671"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.2ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_should_get_index
+----------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-29 01:39:30 +0000
+Processing by BreakEscape::MissionsController#index as HTML
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application
+ [1m[36mBreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mCACHE BreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."published" = ?[0m [["published", 1]]
+ [1m[36mBreakEscape::Cybok Load (0.1ms)[0m [1m[34mSELECT "break_escape_cyboks".* FROM "break_escape_cyboks" WHERE "break_escape_cyboks"."cybokable_type" = ? AND "break_escape_cyboks"."cybokable_id" IN (?, ?)[0m [["cybokable_type", "BreakEscape::Mission"], ["cybokable_id", 418560898], ["cybokable_id", 899573729]]
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.2ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application (Duration: 10.4ms | GC: 3.1ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 10.5ms | GC: 3.1ms)
+Completed 200 OK in 11ms (Views: 10.4ms | ActiveRecord: 0.3ms (5 queries, 1 cached) | GC: 3.1ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_should_create_game_and_redirect_when_showing_mission
+----------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Count (0.0ms)[0m [1m[34mSELECT COUNT(*) FROM "break_escape_games"[0m
+Started GET "/break_escape/missions/418560898" for 127.0.0.1 at 2025-11-29 01:39:31 +0000
+Processing by BreakEscape::MissionsController#show as HTML
+ Parameters: {"id"=>"418560898"}
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Load (0.1ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."player_type" = ? AND "break_escape_games"."player_id" = ? AND "break_escape_games"."mission_id" = ? LIMIT ?[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"scenario_brief\":\"Hi! You are a cyber investigator tasked with uncovering evidence of corporate espionage. Anonymous tips suggest the CEO has been selling company secrets, but you need proof.\",\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"phone\",\"name\":\"Your Phone\",\"takeable\":true,\"phoneId\":\"player_phone\",\"npcIds\":[\"neye_eve\",\"gossip_girl\",\"helper_npc\"],\"observations\":\"Your personal phone with some interesting contacts\"},{\"type\":\"workstation\",\"name\":\"Crypto Analysis Station\",\"takeable\":true,\"observations\":\"A powerful workstation for cryptographic analysis\"},{\"type\":\"lockpick\",\"name\":\"Lock Pick Kit\",\"takeable\":true,\"observations\":\"A professional lock picking kit with various picks and tension wrenches\"}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office1\"},\"npcs\":[{\"id\":\"neye_eve\",\"displayName\":\"Neye Eve\",\"storyPath\":\"scenarios/ink/neye-eve.json\",\"avatar\":\"assets/npc/avatars/npc_adversary.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\"},{\"id\":\"gossip_girl\",\"displayName\":\"Gossip Girl\",\"storyPath\":\"scenarios/ink/gossip-girl.json\",\"avatar\":\"assets/npc/avatars/npc_neutral.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\",\"timedMessages\":[{\"delay\":5000,\"message\":\"Hey! 👋 Got any juicy gossip for me today?\",\"type\":\"text\"}]},{\"id\":\"helper_npc\",\"displayName\":\"Helpful Contact\",\"storyPath\":\"scenarios/ink/helper-npc.json\",\"avatar\":\"assets/npc/avatars/npc_helper.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\",\"unlockable\":[\"secure_vault\",\"ceo\"],\"eventMappings\":[{\"eventPattern\":\"item_picked_up:lockpick\",\"targetKnot\":\"on_lockpick_pickup\",\"onceOnly\":true,\"cooldown\":0},{\"eventPattern\":\"minigame_completed\",\"targetKnot\":\"on_lockpick_success\",\"condition\":\"data.minigameName \\u0026\\u0026 data.minigameName.includes('Lockpick')\",\"cooldown\":10000},{\"eventPattern\":\"minigame_failed\",\"targetKnot\":\"on_lockpick_failed\",\"condition\":\"data.minigameName \\u0026\\u0026 data.minigameName.includes('Lockpick')\",\"cooldown\":15000},{\"eventPattern\":\"door_unlocked\",\"targetKnot\":\"on_door_unlocked\",\"cooldown\":8000},{\"eventPattern\":\"door_unlock_attempt\",\"targetKnot\":\"on_door_attempt\",\"cooldown\":12000},{\"eventPattern\":\"object_interacted\",\"targetKnot\":\"on_ceo_desk_interact\",\"condition\":\"data.objectType === 'desk_ceo'\",\"cooldown\":10000},{\"eventPattern\":\"item_picked_up:*\",\"targetKnot\":\"on_item_found\",\"cooldown\":20000},{\"eventPattern\":\"room_entered\",\"targetKnot\":\"on_room_entered\",\"cooldown\":45000,\"maxTriggers\":3},{\"eventPattern\":\"room_discovered\",\"targetKnot\":\"on_room_discovered\",\"cooldown\":15000,\"maxTriggers\":5},{\"eventPattern\":\"room_entered:ceo\",\"targetKnot\":\"on_ceo_office_entered\",\"onceOnly\":true}]}],\"objects\":[{\"type\":\"phone\",\"name\":\"Reception Phone\",\"takeable\":false,\"readable\":true,\"voice\":\"Hi, this is the IT Team. Security breach detected in server room. Changed access code to 4829.\",\"sender\":\"IT Team\",\"timestamp\":\"2:15 AM\",\"observations\":\"The reception phone's message light is blinking urgently\"},{\"type\":\"notes\",\"name\":\"Security Log\",\"takeable\":true,\"readable\":true,\"text\":\"Unusual after-hours access detected:\\n- CEO office: 11:30 PM\\n- Server room: 2:15 AM\\n- CEO office again: 3:45 AM\",\"observations\":\"A concerning security log from last night\"},{\"type\":\"pc\",\"name\":\"Reception Computer\",\"takeable\":false,\"lockType\":\"password\",\"passwordHint\":\"Optional hint text\",\"showHint\":true,\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"requires\":\"secret123\",\"observations\":\"The reception's computer, currently locked\",\"postitNote\":\"Password: secret123\",\"showPostit\":true,\"contents\":[{\"type\":\"text_file\",\"name\":\"Private\",\"takeable\":false,\"readable\":true,\"text\":\"Closet keypad code: 7391 - Must move evidence to safe before audit\"}]},{\"type\":\"tablet\",\"name\":\"Tablet Device\",\"takeable\":true,\"locked\":true,\"lockType\":\"bluetooth\",\"requires\":\"bluetooth\",\"mac\":\"00:11:22:33:44:55\",\"observations\":\"A locked tablet device that requires Bluetooth pairing\"},{\"type\":\"bluetooth_scanner\",\"name\":\"Bluetooth Scanner\",\"takeable\":true,\"observations\":\"A device for detecting nearby Bluetooth signals\",\"canScanBluetooth\":true},{\"type\":\"key\",\"name\":\"Office Key\",\"takeable\":true,\"key_id\":\"office1_key\",\"keyPins\":[65,25,65,25],\"observations\":\"A key to access the office areas\"},{\"type\":\"pin-cracker\",\"name\":\"PIN Cracker\",\"takeable\":true,\"observations\":\"A sophisticated device that can analyze PIN entry patterns and provide feedback on attempts\"},{\"type\":\"safe\",\"name\":\"Reception Safe\",\"takeable\":false,\"locked\":true,\"lockType\":\"pin\",\"requires\":\"9573\",\"observations\":\"A small wall safe behind the reception desk. Looks like it needs a 4-digit code.\",\"contents\":[{\"type\":\"notes\",\"name\":\"IT Access Credentials\",\"takeable\":true,\"readable\":true,\"text\":\"Emergency IT Admin Credentials:\\nUsername: admin\\nPassword: ITsecure2024\\n\\nServer Room Backup Code: 4829\\nCEO Office Alarm Override: 1337\",\"observations\":\"Sensitive IT credentials that could be very useful\"}]}]},\"office1\":{\"type\":\"room_office\",\"locked\":true,\"lockType\":\"key\",\"requires\":\"office1_key\",\"keyPins\":[65,25,65,25],\"difficulty\":\"easy\",\"door_sign\":\"4A Hot Desks\",\"connections\":{\"north\":[\"office2\",\"office3\"],\"south\":\"reception\"},\"objects\":[{\"type\":\"pc\",\"name\":\"Office Computer\",\"takeable\":false,\"requires\":\"password\",\"hasFingerprint\":true,\"fingerprintOwner\":\"ceo\",\"fingerprintDifficulty\":\"medium\",\"observations\":\"A computer with a cybersecurity alert on screen. There might be fingerprints on the keyboard.\"},{\"type\":\"notes\",\"name\":\"IT Memo\",\"takeable\":true,\"readable\":true,\"text\":\"URGENT: Multiple unauthorized access attempts detected from CEO's office IP address\",\"observations\":\"A concerning IT department memo\"},{\"type\":\"fingerprint_kit\",\"name\":\"Fingerprint Kit\",\"takeable\":true,\"observations\":\"A kit used for collecting fingerprints from surfaces\"}]},\"office2\":{\"type\":\"room_office\",\"connections\":{\"north\":\"ceo\",\"south\":\"office1\"},\"objects\":[{\"type\":\"pc\",\"name\":\"Office Computer\",\"takeable\":false,\"lockType\":\"password\",\"requires\":\"office2024\",\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"postitNote\":\"Password: office2024\",\"showPostit\":true,\"observations\":\"A standard office computer with a sticky note on the monitor\"},{\"type\":\"notes\",\"name\":\"Shredded Document\",\"takeable\":true,\"readable\":true,\"text\":\"Partially readable: '...offshore account...transfer complete...delete all traces...'\",\"observations\":\"A partially shredded document that someone failed to dispose of properly\"},{\"type\":\"key\",\"name\":\"CEO Office Key\",\"takeable\":true,\"key_id\":\"ceo_office_key\",\"keyPins\":[25,45,65,75],\"observations\":\"A spare key to the CEO's office, carelessly left behind\"}]},\"office3\":{\"type\":\"room_office\",\"connections\":{\"north\":\"server1\",\"south\":\"office1\"},\"objects\":[{\"type\":\"pc\",\"name\":\"IT Staff Computer\",\"takeable\":false,\"requires\":\"bluetooth\",\"lockType\":\"bluetooth\",\"mac\":\"00:11:22:33:44:55\",\"observations\":\"An IT staff computer showing network security logs\"},{\"type\":\"notes\",\"name\":\"Network Logs\",\"takeable\":true,\"readable\":true,\"text\":\"Large data transfers detected to unknown external IPs - All originating from CEO's office\",\"observations\":\"Suspicious network activity logs\"}]},\"ceo\":{\"type\":\"room_ceo\",\"connections\":{\"north\":\"closet\",\"south\":\"office2\"},\"locked\":true,\"lockType\":\"key\",\"requires\":\"ceo_office_key\",\"keyPins\":[25,45,65,75],\"difficulty\":\"easy\",\"objects\":[{\"type\":\"pc\",\"name\":\"CEO Computer\",\"takeable\":false,\"lockType\":\"password\",\"requires\":\"ceo2024\",\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"postitNote\":\"Password: ceo2024\",\"showPostit\":true,\"observations\":\"The CEO's laptop, still warm - recently used. A sticky note is attached to the screen.\"},{\"type\":\"suitcase\",\"name\":\"CEO Briefcase\",\"takeable\":false,\"locked\":true,\"lockType\":\"key\",\"requires\":\"briefcase_key\",\"keyPins\":[45,35,25,55],\"difficulty\":\"medium\",\"observations\":\"An expensive leather briefcase with a sturdy lock\",\"contents\":[{\"type\":\"notes\",\"name\":\"Private Note\",\"takeable\":true,\"readable\":true,\"text\":\"Closet keypad code: 7391 - Must move evidence to safe before audit\",\"observations\":\"A hastily written note on expensive paper\"},{\"type\":\"key\",\"name\":\"Safe Key\",\"takeable\":true,\"key_id\":\"safe_key\",\"keyPins\":[52,29,44,37],\"observations\":\"A heavy-duty safe key hidden behind server equipment\"}]},{\"type\":\"phone\",\"name\":\"CEO Phone\",\"takeable\":false,\"readable\":true,\"text\":\"Recent calls: 'Offshore Bank', 'Unknown', 'Data Buyer'\",\"sender\":\"Call Log\",\"timestamp\":\"Last 24 hours\",\"observations\":\"The CEO's phone shows suspicious recent calls\"}]},\"closet\":{\"type\":\"room_closet\",\"connections\":{\"south\":\"ceo\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"7391\",\"objects\":[{\"type\":\"safe\",\"name\":\"Hidden Safe\",\"takeable\":false,\"locked\":true,\"lockType\":\"key\",\"requires\":\"safe_key\",\"keyPins\":[52,29,44,37],\"difficulty\":\"hard\",\"observations\":\"A well-hidden wall safe behind a painting\",\"contents\":[{\"type\":\"notes\",\"name\":\"Incriminating Documents\",\"takeable\":true,\"readable\":true,\"text\":\"Contract for sale of proprietary technology\\nBank transfers from competing companies\\nDetails of upcoming corporate espionage operations\",\"observations\":\"A folder containing damning evidence of corporate espionage. Congratulations! You've recovered the incriminating documents. flag{ceo_exfil_flag}\"}]}]},\"server1\":{\"type\":\"room_servers\",\"connections\":{\"south\":\"office3\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"4829\",\"objects\":[{\"type\":\"pc\",\"name\":\"Server Terminal\",\"takeable\":false,\"observations\":\"The main server terminal showing massive data exfiltration\"},{\"type\":\"key\",\"name\":\"Briefcase Key\",\"takeable\":true,\"key_id\":\"briefcase_key\",\"keyPins\":[45,35,25,55],\"observations\":\"A small key labeled 'Personal - Do Not Copy'\"}]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"phone\",\"name\":\"Your Phone\",\"takeable\":true,\"phoneId\":\"player_phone\",\"npcIds\":[\"neye_eve\",\"gossip_girl\",\"helper_npc\"],\"observations\":\"Your personal phone with some interesting contacts\"},{\"type\":\"workstation\",\"name\":\"Crypto Analysis Station\",\"takeable\":true,\"observations\":\"A powerful workstation for cryptographic analysis\"},{\"type\":\"lockpick\",\"name\":\"Lock Pick Kit\",\"takeable\":true,\"observations\":\"A professional lock picking kit with various picks and tension wrenches\"}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:31.003939"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:31.003054"], ["updated_at", "2025-11-29 01:39:31.003054"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Redirected to http://www.example.com/break_escape/games/1
+Completed 302 Found in 3ms (ActiveRecord: 0.3ms (4 queries, 0 cached) | GC: 0.2ms)
+ [1m[36mBreakEscape::Game Count (0.0ms)[0m [1m[34mSELECT COUNT(*) FROM "break_escape_games"[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_index_should_return_HTML_with_mission_list
+------------------------------------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-29 01:39:31 +0000
+Processing by BreakEscape::MissionsController#index as HTML
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application
+ [1m[36mBreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mCACHE BreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."published" = ?[0m [["published", 1]]
+ [1m[36mBreakEscape::Cybok Load (0.1ms)[0m [1m[34mSELECT "break_escape_cyboks".* FROM "break_escape_cyboks" WHERE "break_escape_cyboks"."cybokable_type" = ? AND "break_escape_cyboks"."cybokable_id" IN (?, ?)[0m [["cybokable_type", "BreakEscape::Mission"], ["cybokable_id", 418560898], ["cybokable_id", 899573729]]
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application (Duration: 1.0ms | GC: 0.1ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.0ms | GC: 0.1ms)
+Completed 200 OK in 1ms (Views: 1.0ms | ActiveRecord: 0.1ms (5 queries, 1 cached) | GC: 0.1ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_index_should_display_published_missions
+---------------------------------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-29 01:39:31 +0000
+Processing by BreakEscape::MissionsController#index as HTML
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application
+ [1m[36mBreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mCACHE BreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."published" = ?[0m [["published", 1]]
+ [1m[36mBreakEscape::Cybok Load (0.0ms)[0m [1m[34mSELECT "break_escape_cyboks".* FROM "break_escape_cyboks" WHERE "break_escape_cyboks"."cybokable_type" = ? AND "break_escape_cyboks"."cybokable_id" IN (?, ?)[0m [["cybokable_type", "BreakEscape::Mission"], ["cybokable_id", 418560898], ["cybokable_id", 899573729]]
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application (Duration: 0.8ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.9ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 1.0ms | ActiveRecord: 0.1ms (5 queries, 1 cached) | GC: 0.1ms)
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-----------------------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_should_show_published_mission
+-----------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+Started GET "/break_escape/missions/418560898" for 127.0.0.1 at 2025-11-29 01:39:31 +0000
+Processing by BreakEscape::MissionsController#show as HTML
+ Parameters: {"id"=>"418560898"}
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."player_type" = ? AND "break_escape_games"."player_id" = ? AND "break_escape_games"."mission_id" = ? LIMIT ?[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"scenario_brief\":\"Hi! You are a cyber investigator tasked with uncovering evidence of corporate espionage. Anonymous tips suggest the CEO has been selling company secrets, but you need proof.\",\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"phone\",\"name\":\"Your Phone\",\"takeable\":true,\"phoneId\":\"player_phone\",\"npcIds\":[\"neye_eve\",\"gossip_girl\",\"helper_npc\"],\"observations\":\"Your personal phone with some interesting contacts\"},{\"type\":\"workstation\",\"name\":\"Crypto Analysis Station\",\"takeable\":true,\"observations\":\"A powerful workstation for cryptographic analysis\"},{\"type\":\"lockpick\",\"name\":\"Lock Pick Kit\",\"takeable\":true,\"observations\":\"A professional lock picking kit with various picks and tension wrenches\"}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office1\"},\"npcs\":[{\"id\":\"neye_eve\",\"displayName\":\"Neye Eve\",\"storyPath\":\"scenarios/ink/neye-eve.json\",\"avatar\":\"assets/npc/avatars/npc_adversary.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\"},{\"id\":\"gossip_girl\",\"displayName\":\"Gossip Girl\",\"storyPath\":\"scenarios/ink/gossip-girl.json\",\"avatar\":\"assets/npc/avatars/npc_neutral.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\",\"timedMessages\":[{\"delay\":5000,\"message\":\"Hey! 👋 Got any juicy gossip for me today?\",\"type\":\"text\"}]},{\"id\":\"helper_npc\",\"displayName\":\"Helpful Contact\",\"storyPath\":\"scenarios/ink/helper-npc.json\",\"avatar\":\"assets/npc/avatars/npc_helper.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\",\"unlockable\":[\"secure_vault\",\"ceo\"],\"eventMappings\":[{\"eventPattern\":\"item_picked_up:lockpick\",\"targetKnot\":\"on_lockpick_pickup\",\"onceOnly\":true,\"cooldown\":0},{\"eventPattern\":\"minigame_completed\",\"targetKnot\":\"on_lockpick_success\",\"condition\":\"data.minigameName \\u0026\\u0026 data.minigameName.includes('Lockpick')\",\"cooldown\":10000},{\"eventPattern\":\"minigame_failed\",\"targetKnot\":\"on_lockpick_failed\",\"condition\":\"data.minigameName \\u0026\\u0026 data.minigameName.includes('Lockpick')\",\"cooldown\":15000},{\"eventPattern\":\"door_unlocked\",\"targetKnot\":\"on_door_unlocked\",\"cooldown\":8000},{\"eventPattern\":\"door_unlock_attempt\",\"targetKnot\":\"on_door_attempt\",\"cooldown\":12000},{\"eventPattern\":\"object_interacted\",\"targetKnot\":\"on_ceo_desk_interact\",\"condition\":\"data.objectType === 'desk_ceo'\",\"cooldown\":10000},{\"eventPattern\":\"item_picked_up:*\",\"targetKnot\":\"on_item_found\",\"cooldown\":20000},{\"eventPattern\":\"room_entered\",\"targetKnot\":\"on_room_entered\",\"cooldown\":45000,\"maxTriggers\":3},{\"eventPattern\":\"room_discovered\",\"targetKnot\":\"on_room_discovered\",\"cooldown\":15000,\"maxTriggers\":5},{\"eventPattern\":\"room_entered:ceo\",\"targetKnot\":\"on_ceo_office_entered\",\"onceOnly\":true}]}],\"objects\":[{\"type\":\"phone\",\"name\":\"Reception Phone\",\"takeable\":false,\"readable\":true,\"voice\":\"Hi, this is the IT Team. Security breach detected in server room. Changed access code to 4829.\",\"sender\":\"IT Team\",\"timestamp\":\"2:15 AM\",\"observations\":\"The reception phone's message light is blinking urgently\"},{\"type\":\"notes\",\"name\":\"Security Log\",\"takeable\":true,\"readable\":true,\"text\":\"Unusual after-hours access detected:\\n- CEO office: 11:30 PM\\n- Server room: 2:15 AM\\n- CEO office again: 3:45 AM\",\"observations\":\"A concerning security log from last night\"},{\"type\":\"pc\",\"name\":\"Reception Computer\",\"takeable\":false,\"lockType\":\"password\",\"passwordHint\":\"Optional hint text\",\"showHint\":true,\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"requires\":\"secret123\",\"observations\":\"The reception's computer, currently locked\",\"postitNote\":\"Password: secret123\",\"showPostit\":true,\"contents\":[{\"type\":\"text_file\",\"name\":\"Private\",\"takeable\":false,\"readable\":true,\"text\":\"Closet keypad code: 7391 - Must move evidence to safe before audit\"}]},{\"type\":\"tablet\",\"name\":\"Tablet Device\",\"takeable\":true,\"locked\":true,\"lockType\":\"bluetooth\",\"requires\":\"bluetooth\",\"mac\":\"00:11:22:33:44:55\",\"observations\":\"A locked tablet device that requires Bluetooth pairing\"},{\"type\":\"bluetooth_scanner\",\"name\":\"Bluetooth Scanner\",\"takeable\":true,\"observations\":\"A device for detecting nearby Bluetooth signals\",\"canScanBluetooth\":true},{\"type\":\"key\",\"name\":\"Office Key\",\"takeable\":true,\"key_id\":\"office1_key\",\"keyPins\":[65,25,65,25],\"observations\":\"A key to access the office areas\"},{\"type\":\"pin-cracker\",\"name\":\"PIN Cracker\",\"takeable\":true,\"observations\":\"A sophisticated device that can analyze PIN entry patterns and provide feedback on attempts\"},{\"type\":\"safe\",\"name\":\"Reception Safe\",\"takeable\":false,\"locked\":true,\"lockType\":\"pin\",\"requires\":\"9573\",\"observations\":\"A small wall safe behind the reception desk. Looks like it needs a 4-digit code.\",\"contents\":[{\"type\":\"notes\",\"name\":\"IT Access Credentials\",\"takeable\":true,\"readable\":true,\"text\":\"Emergency IT Admin Credentials:\\nUsername: admin\\nPassword: ITsecure2024\\n\\nServer Room Backup Code: 4829\\nCEO Office Alarm Override: 1337\",\"observations\":\"Sensitive IT credentials that could be very useful\"}]}]},\"office1\":{\"type\":\"room_office\",\"locked\":true,\"lockType\":\"key\",\"requires\":\"office1_key\",\"keyPins\":[65,25,65,25],\"difficulty\":\"easy\",\"door_sign\":\"4A Hot Desks\",\"connections\":{\"north\":[\"office2\",\"office3\"],\"south\":\"reception\"},\"objects\":[{\"type\":\"pc\",\"name\":\"Office Computer\",\"takeable\":false,\"requires\":\"password\",\"hasFingerprint\":true,\"fingerprintOwner\":\"ceo\",\"fingerprintDifficulty\":\"medium\",\"observations\":\"A computer with a cybersecurity alert on screen. There might be fingerprints on the keyboard.\"},{\"type\":\"notes\",\"name\":\"IT Memo\",\"takeable\":true,\"readable\":true,\"text\":\"URGENT: Multiple unauthorized access attempts detected from CEO's office IP address\",\"observations\":\"A concerning IT department memo\"},{\"type\":\"fingerprint_kit\",\"name\":\"Fingerprint Kit\",\"takeable\":true,\"observations\":\"A kit used for collecting fingerprints from surfaces\"}]},\"office2\":{\"type\":\"room_office\",\"connections\":{\"north\":\"ceo\",\"south\":\"office1\"},\"objects\":[{\"type\":\"pc\",\"name\":\"Office Computer\",\"takeable\":false,\"lockType\":\"password\",\"requires\":\"office2024\",\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"postitNote\":\"Password: office2024\",\"showPostit\":true,\"observations\":\"A standard office computer with a sticky note on the monitor\"},{\"type\":\"notes\",\"name\":\"Shredded Document\",\"takeable\":true,\"readable\":true,\"text\":\"Partially readable: '...offshore account...transfer complete...delete all traces...'\",\"observations\":\"A partially shredded document that someone failed to dispose of properly\"},{\"type\":\"key\",\"name\":\"CEO Office Key\",\"takeable\":true,\"key_id\":\"ceo_office_key\",\"keyPins\":[25,45,65,75],\"observations\":\"A spare key to the CEO's office, carelessly left behind\"}]},\"office3\":{\"type\":\"room_office\",\"connections\":{\"north\":\"server1\",\"south\":\"office1\"},\"objects\":[{\"type\":\"pc\",\"name\":\"IT Staff Computer\",\"takeable\":false,\"requires\":\"bluetooth\",\"lockType\":\"bluetooth\",\"mac\":\"00:11:22:33:44:55\",\"observations\":\"An IT staff computer showing network security logs\"},{\"type\":\"notes\",\"name\":\"Network Logs\",\"takeable\":true,\"readable\":true,\"text\":\"Large data transfers detected to unknown external IPs - All originating from CEO's office\",\"observations\":\"Suspicious network activity logs\"}]},\"ceo\":{\"type\":\"room_ceo\",\"connections\":{\"north\":\"closet\",\"south\":\"office2\"},\"locked\":true,\"lockType\":\"key\",\"requires\":\"ceo_office_key\",\"keyPins\":[25,45,65,75],\"difficulty\":\"easy\",\"objects\":[{\"type\":\"pc\",\"name\":\"CEO Computer\",\"takeable\":false,\"lockType\":\"password\",\"requires\":\"ceo2024\",\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"postitNote\":\"Password: ceo2024\",\"showPostit\":true,\"observations\":\"The CEO's laptop, still warm - recently used. A sticky note is attached to the screen.\"},{\"type\":\"suitcase\",\"name\":\"CEO Briefcase\",\"takeable\":false,\"locked\":true,\"lockType\":\"key\",\"requires\":\"briefcase_key\",\"keyPins\":[45,35,25,55],\"difficulty\":\"medium\",\"observations\":\"An expensive leather briefcase with a sturdy lock\",\"contents\":[{\"type\":\"notes\",\"name\":\"Private Note\",\"takeable\":true,\"readable\":true,\"text\":\"Closet keypad code: 7391 - Must move evidence to safe before audit\",\"observations\":\"A hastily written note on expensive paper\"},{\"type\":\"key\",\"name\":\"Safe Key\",\"takeable\":true,\"key_id\":\"safe_key\",\"keyPins\":[52,29,44,37],\"observations\":\"A heavy-duty safe key hidden behind server equipment\"}]},{\"type\":\"phone\",\"name\":\"CEO Phone\",\"takeable\":false,\"readable\":true,\"text\":\"Recent calls: 'Offshore Bank', 'Unknown', 'Data Buyer'\",\"sender\":\"Call Log\",\"timestamp\":\"Last 24 hours\",\"observations\":\"The CEO's phone shows suspicious recent calls\"}]},\"closet\":{\"type\":\"room_closet\",\"connections\":{\"south\":\"ceo\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"7391\",\"objects\":[{\"type\":\"safe\",\"name\":\"Hidden Safe\",\"takeable\":false,\"locked\":true,\"lockType\":\"key\",\"requires\":\"safe_key\",\"keyPins\":[52,29,44,37],\"difficulty\":\"hard\",\"observations\":\"A well-hidden wall safe behind a painting\",\"contents\":[{\"type\":\"notes\",\"name\":\"Incriminating Documents\",\"takeable\":true,\"readable\":true,\"text\":\"Contract for sale of proprietary technology\\nBank transfers from competing companies\\nDetails of upcoming corporate espionage operations\",\"observations\":\"A folder containing damning evidence of corporate espionage. Congratulations! You've recovered the incriminating documents. flag{ceo_exfil_flag}\"}]}]},\"server1\":{\"type\":\"room_servers\",\"connections\":{\"south\":\"office3\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"4829\",\"objects\":[{\"type\":\"pc\",\"name\":\"Server Terminal\",\"takeable\":false,\"observations\":\"The main server terminal showing massive data exfiltration\"},{\"type\":\"key\",\"name\":\"Briefcase Key\",\"takeable\":true,\"key_id\":\"briefcase_key\",\"keyPins\":[45,35,25,55],\"observations\":\"A small key labeled 'Personal - Do Not Copy'\"}]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"phone\",\"name\":\"Your Phone\",\"takeable\":true,\"phoneId\":\"player_phone\",\"npcIds\":[\"neye_eve\",\"gossip_girl\",\"helper_npc\"],\"observations\":\"Your personal phone with some interesting contacts\"},{\"type\":\"workstation\",\"name\":\"Crypto Analysis Station\",\"takeable\":true,\"observations\":\"A powerful workstation for cryptographic analysis\"},{\"type\":\"lockpick\",\"name\":\"Lock Pick Kit\",\"takeable\":true,\"observations\":\"A professional lock picking kit with various picks and tension wrenches\"}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:39:31.017693"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:39:31.017018"], ["updated_at", "2025-11-29 01:39:31.017018"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Redirected to http://www.example.com/break_escape/games/1
+Completed 302 Found in 2ms (ActiveRecord: 0.3ms (4 queries, 0 cached) | GC: 0.1ms)
+ [1m[36mTRANSACTION (0.1ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mActiveRecord::InternalMetadata Load (0.1ms)[0m [1m[34mSELECT * FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? ORDER BY "ar_internal_metadata"."key" ASC LIMIT 1[0m [[nil, "schema_sha1"]]
+ [1m[36mActiveRecord::SchemaMigration Load (0.0ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+ [1m[35m (0.1ms)[0m [1m[35mPRAGMA foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = ON[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = OFF[0m
+ [1m[36mFixtures Load (0.1ms)[0m [1m[31mDELETE FROM "break_escape_demo_users";
+DELETE FROM "break_escape_missions";
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (149617800, 'test_user', '2025-11-29 01:43:57', '2025-11-29 01:43:57');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-29 01:43:57', '2025-11-29 01:43:57');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (418560898, 'ceo_exfil', 'CEO Exfiltration', 'Test scenario', 1, 3, '2025-11-29 01:43:57', '2025-11-29 01:43:57');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (636030761, 'test_unpublished', 'Unpublished Test', 'Not visible', 0, 1, '2025-11-29 01:43:57', '2025-11-29 01:43:57');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at", "secgen_scenario", "collection") VALUES (899573729, 'secgen_vm_lab', 'SecGen VM Lab - Linux Introduction', 'Test VM and flag integration with SecGen scenario', 1, 2, '2025-11-29 01:43:57', '2025-11-29 01:43:57', 'labs/introducing_attacks/1_intro_linux.xml', 'vm_labs')[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = 0[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = 1[0m
+ [1m[36mTRANSACTION (0.9ms)[0m [1m[36mcommit transaction[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_key_check[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------
+BreakEscape::GamesControllerTest: test_should_show_game
+-------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.2ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.674256"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.674165"], ["updated_at", "2025-11-29 01:43:57.674165"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.1ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 1.5ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.7ms | GC: 0.0ms)
+Completed 200 OK in 8ms (Views: 3.0ms | ActiveRecord: 0.2ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_return_HTML_with_game_container
+----------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.691459"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.691404"], ["updated_at", "2025-11-29 01:43:57.691404"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.1ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_accept_correct_pin_code
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.694948"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.694904"], ["updated_at", "2025-11-29 01:43:57.694904"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"door", "targetId"=>"office", "attempt"=>"1234", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=door, id=office, attempt=1234, method=pin
+[BreakEscape] Room data: locked=true, lockType=pin, requires=1234
+[BreakEscape] Room is LOCKED, method must be valid: pin
+[BreakEscape] pin validation result: true
+[BreakEscape] validate_unlock returning: true
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\",\"office\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 01:43:57.698500"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.2ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_require_npc_parameter
+--------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.699991"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.699950"], ["updated_at", "2025-11-29 01:43:57.699950"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 400 Bad Request in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_game_setup_has_correct_scenario_data
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.702297"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.702253"], ["updated_at", "2025-11-29 01:43:57.702253"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_non-existent_NPC
+------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.703468"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.703429"], ["updated_at", "2025-11-29 01:43:57.703429"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=non-existent" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"non-existent", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: non-existent
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.1ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_sync_state_should_update_player_state_for_current_room
+---------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.705660"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.705623"], ["updated_at", "2025-11-29 01:43:57.705623"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started PUT "/break_escape/games/1/sync_state" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#sync_state as HTML
+ Parameters: {"currentRoom"=>"reception", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_NPC_without_story_file
+------------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.708263"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.708224"], ["updated_at", "2025-11-29 01:43:57.708224"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=missing-npc" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"missing-npc", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: missing-npc
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_inventory_endpoint_should_add_items
+--------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.710331"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.710293"], ["updated_at", "2025-11-29 01:43:57.710293"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Update (0.1ms)[0m [1m[33mUPDATE "break_escape_games" SET "scenario_data" = ?, "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[{\"id\":\"note_1\",\"type\":\"note\",\"name\":\"Test Note\",\"takeable\":true}]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 01:43:57.710818"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/inventory" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#inventory as HTML
+ Parameters: {"action_type"=>"add", "item"=>{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}, "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] inventory endpoint: action=add, item=#"note", "name"=>"Test Note", "id"=>"note_1"} permitted: false>
+[BreakEscape] validate_item_collectible: type=note, id=note_1, name=Test Note
+[BreakEscape] Item collection valid: note
+[BreakEscape] Adding item to inventory: note / Test Note
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"note\",\"name\":\"Test Note\",\"id\":\"note_1\"}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 01:43:57.712349"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+[BreakEscape] Item added successfully. Current inventory: [{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}]
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.2ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_scenario_endpoint_should_return_JSON
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.713640"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.713600"], ["updated_at", "2025-11-29 01:43:57.713600"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/scenario" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#scenario as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_inject_game_configuration
+----------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.716147"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.716107"], ["updated_at", "2025-11-29 01:43:57.716107"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.2ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_reject_invalid_attempts
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.718872"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.718826"], ["updated_at", "2025-11-29 01:43:57.718826"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"room", "targetId"=>"office", "attempt"=>"wrong_code", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=room, id=office, attempt=wrong_code, method=pin
+[BreakEscape] Object not found: office
+Completed 422 Unprocessable Content in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_index_should_display_published_missions
+---------------------------------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::MissionsController#index as HTML
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application
+ [1m[36mBreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mCACHE BreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."published" = ?[0m [["published", 1]]
+ [1m[36mBreakEscape::Cybok Load (0.1ms)[0m [1m[34mSELECT "break_escape_cyboks".* FROM "break_escape_cyboks" WHERE "break_escape_cyboks"."cybokable_type" = ? AND "break_escape_cyboks"."cybokable_id" IN (?, ?)[0m [["cybokable_type", "BreakEscape::Mission"], ["cybokable_id", 418560898], ["cybokable_id", 899573729]]
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.2ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application (Duration: 8.9ms | GC: 2.8ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 8.9ms | GC: 2.8ms)
+Completed 200 OK in 10ms (Views: 8.6ms | ActiveRecord: 0.5ms (5 queries, 1 cached) | GC: 2.8ms)
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_index_should_return_HTML_with_mission_list
+------------------------------------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::MissionsController#index as HTML
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application
+ [1m[36mBreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mCACHE BreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."published" = ?[0m [["published", 1]]
+ [1m[36mBreakEscape::Cybok Load (0.1ms)[0m [1m[34mSELECT "break_escape_cyboks".* FROM "break_escape_cyboks" WHERE "break_escape_cyboks"."cybokable_type" = ? AND "break_escape_cyboks"."cybokable_id" IN (?, ?)[0m [["cybokable_type", "BreakEscape::Mission"], ["cybokable_id", 418560898], ["cybokable_id", 899573729]]
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application (Duration: 1.0ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.0ms | GC: 0.0ms)
+Completed 200 OK in 2ms (Views: 1.0ms | ActiveRecord: 0.1ms (5 queries, 1 cached) | GC: 0.1ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_should_create_game_and_redirect_when_showing_mission
+----------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Count (0.0ms)[0m [1m[34mSELECT COUNT(*) FROM "break_escape_games"[0m
+Started GET "/break_escape/missions/418560898" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::MissionsController#show as HTML
+ Parameters: {"id"=>"418560898"}
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Load (0.1ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."player_type" = ? AND "break_escape_games"."player_id" = ? AND "break_escape_games"."mission_id" = ? LIMIT ?[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"scenario_brief\":\"Hi! You are a cyber investigator tasked with uncovering evidence of corporate espionage. Anonymous tips suggest the CEO has been selling company secrets, but you need proof.\",\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"phone\",\"name\":\"Your Phone\",\"takeable\":true,\"phoneId\":\"player_phone\",\"npcIds\":[\"neye_eve\",\"gossip_girl\",\"helper_npc\"],\"observations\":\"Your personal phone with some interesting contacts\"},{\"type\":\"workstation\",\"name\":\"Crypto Analysis Station\",\"takeable\":true,\"observations\":\"A powerful workstation for cryptographic analysis\"},{\"type\":\"lockpick\",\"name\":\"Lock Pick Kit\",\"takeable\":true,\"observations\":\"A professional lock picking kit with various picks and tension wrenches\"}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office1\"},\"npcs\":[{\"id\":\"neye_eve\",\"displayName\":\"Neye Eve\",\"storyPath\":\"scenarios/ink/neye-eve.json\",\"avatar\":\"assets/npc/avatars/npc_adversary.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\"},{\"id\":\"gossip_girl\",\"displayName\":\"Gossip Girl\",\"storyPath\":\"scenarios/ink/gossip-girl.json\",\"avatar\":\"assets/npc/avatars/npc_neutral.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\",\"timedMessages\":[{\"delay\":5000,\"message\":\"Hey! 👋 Got any juicy gossip for me today?\",\"type\":\"text\"}]},{\"id\":\"helper_npc\",\"displayName\":\"Helpful Contact\",\"storyPath\":\"scenarios/ink/helper-npc.json\",\"avatar\":\"assets/npc/avatars/npc_helper.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\",\"unlockable\":[\"secure_vault\",\"ceo\"],\"eventMappings\":[{\"eventPattern\":\"item_picked_up:lockpick\",\"targetKnot\":\"on_lockpick_pickup\",\"onceOnly\":true,\"cooldown\":0},{\"eventPattern\":\"minigame_completed\",\"targetKnot\":\"on_lockpick_success\",\"condition\":\"data.minigameName \\u0026\\u0026 data.minigameName.includes('Lockpick')\",\"cooldown\":10000},{\"eventPattern\":\"minigame_failed\",\"targetKnot\":\"on_lockpick_failed\",\"condition\":\"data.minigameName \\u0026\\u0026 data.minigameName.includes('Lockpick')\",\"cooldown\":15000},{\"eventPattern\":\"door_unlocked\",\"targetKnot\":\"on_door_unlocked\",\"cooldown\":8000},{\"eventPattern\":\"door_unlock_attempt\",\"targetKnot\":\"on_door_attempt\",\"cooldown\":12000},{\"eventPattern\":\"object_interacted\",\"targetKnot\":\"on_ceo_desk_interact\",\"condition\":\"data.objectType === 'desk_ceo'\",\"cooldown\":10000},{\"eventPattern\":\"item_picked_up:*\",\"targetKnot\":\"on_item_found\",\"cooldown\":20000},{\"eventPattern\":\"room_entered\",\"targetKnot\":\"on_room_entered\",\"cooldown\":45000,\"maxTriggers\":3},{\"eventPattern\":\"room_discovered\",\"targetKnot\":\"on_room_discovered\",\"cooldown\":15000,\"maxTriggers\":5},{\"eventPattern\":\"room_entered:ceo\",\"targetKnot\":\"on_ceo_office_entered\",\"onceOnly\":true}]}],\"objects\":[{\"type\":\"phone\",\"name\":\"Reception Phone\",\"takeable\":false,\"readable\":true,\"voice\":\"Hi, this is the IT Team. Security breach detected in server room. Changed access code to 4829.\",\"sender\":\"IT Team\",\"timestamp\":\"2:15 AM\",\"observations\":\"The reception phone's message light is blinking urgently\"},{\"type\":\"notes\",\"name\":\"Security Log\",\"takeable\":true,\"readable\":true,\"text\":\"Unusual after-hours access detected:\\n- CEO office: 11:30 PM\\n- Server room: 2:15 AM\\n- CEO office again: 3:45 AM\",\"observations\":\"A concerning security log from last night\"},{\"type\":\"pc\",\"name\":\"Reception Computer\",\"takeable\":false,\"lockType\":\"password\",\"passwordHint\":\"Optional hint text\",\"showHint\":true,\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"requires\":\"secret123\",\"observations\":\"The reception's computer, currently locked\",\"postitNote\":\"Password: secret123\",\"showPostit\":true,\"contents\":[{\"type\":\"text_file\",\"name\":\"Private\",\"takeable\":false,\"readable\":true,\"text\":\"Closet keypad code: 7391 - Must move evidence to safe before audit\"}]},{\"type\":\"tablet\",\"name\":\"Tablet Device\",\"takeable\":true,\"locked\":true,\"lockType\":\"bluetooth\",\"requires\":\"bluetooth\",\"mac\":\"00:11:22:33:44:55\",\"observations\":\"A locked tablet device that requires Bluetooth pairing\"},{\"type\":\"bluetooth_scanner\",\"name\":\"Bluetooth Scanner\",\"takeable\":true,\"observations\":\"A device for detecting nearby Bluetooth signals\",\"canScanBluetooth\":true},{\"type\":\"key\",\"name\":\"Office Key\",\"takeable\":true,\"key_id\":\"office1_key\",\"keyPins\":[65,25,65,25],\"observations\":\"A key to access the office areas\"},{\"type\":\"pin-cracker\",\"name\":\"PIN Cracker\",\"takeable\":true,\"observations\":\"A sophisticated device that can analyze PIN entry patterns and provide feedback on attempts\"},{\"type\":\"safe\",\"name\":\"Reception Safe\",\"takeable\":false,\"locked\":true,\"lockType\":\"pin\",\"requires\":\"9573\",\"observations\":\"A small wall safe behind the reception desk. Looks like it needs a 4-digit code.\",\"contents\":[{\"type\":\"notes\",\"name\":\"IT Access Credentials\",\"takeable\":true,\"readable\":true,\"text\":\"Emergency IT Admin Credentials:\\nUsername: admin\\nPassword: ITsecure2024\\n\\nServer Room Backup Code: 4829\\nCEO Office Alarm Override: 1337\",\"observations\":\"Sensitive IT credentials that could be very useful\"}]}]},\"office1\":{\"type\":\"room_office\",\"locked\":true,\"lockType\":\"key\",\"requires\":\"office1_key\",\"keyPins\":[65,25,65,25],\"difficulty\":\"easy\",\"door_sign\":\"4A Hot Desks\",\"connections\":{\"north\":[\"office2\",\"office3\"],\"south\":\"reception\"},\"objects\":[{\"type\":\"pc\",\"name\":\"Office Computer\",\"takeable\":false,\"requires\":\"password\",\"hasFingerprint\":true,\"fingerprintOwner\":\"ceo\",\"fingerprintDifficulty\":\"medium\",\"observations\":\"A computer with a cybersecurity alert on screen. There might be fingerprints on the keyboard.\"},{\"type\":\"notes\",\"name\":\"IT Memo\",\"takeable\":true,\"readable\":true,\"text\":\"URGENT: Multiple unauthorized access attempts detected from CEO's office IP address\",\"observations\":\"A concerning IT department memo\"},{\"type\":\"fingerprint_kit\",\"name\":\"Fingerprint Kit\",\"takeable\":true,\"observations\":\"A kit used for collecting fingerprints from surfaces\"}]},\"office2\":{\"type\":\"room_office\",\"connections\":{\"north\":\"ceo\",\"south\":\"office1\"},\"objects\":[{\"type\":\"pc\",\"name\":\"Office Computer\",\"takeable\":false,\"lockType\":\"password\",\"requires\":\"office2024\",\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"postitNote\":\"Password: office2024\",\"showPostit\":true,\"observations\":\"A standard office computer with a sticky note on the monitor\"},{\"type\":\"notes\",\"name\":\"Shredded Document\",\"takeable\":true,\"readable\":true,\"text\":\"Partially readable: '...offshore account...transfer complete...delete all traces...'\",\"observations\":\"A partially shredded document that someone failed to dispose of properly\"},{\"type\":\"key\",\"name\":\"CEO Office Key\",\"takeable\":true,\"key_id\":\"ceo_office_key\",\"keyPins\":[25,45,65,75],\"observations\":\"A spare key to the CEO's office, carelessly left behind\"}]},\"office3\":{\"type\":\"room_office\",\"connections\":{\"north\":\"server1\",\"south\":\"office1\"},\"objects\":[{\"type\":\"pc\",\"name\":\"IT Staff Computer\",\"takeable\":false,\"requires\":\"bluetooth\",\"lockType\":\"bluetooth\",\"mac\":\"00:11:22:33:44:55\",\"observations\":\"An IT staff computer showing network security logs\"},{\"type\":\"notes\",\"name\":\"Network Logs\",\"takeable\":true,\"readable\":true,\"text\":\"Large data transfers detected to unknown external IPs - All originating from CEO's office\",\"observations\":\"Suspicious network activity logs\"}]},\"ceo\":{\"type\":\"room_ceo\",\"connections\":{\"north\":\"closet\",\"south\":\"office2\"},\"locked\":true,\"lockType\":\"key\",\"requires\":\"ceo_office_key\",\"keyPins\":[25,45,65,75],\"difficulty\":\"easy\",\"objects\":[{\"type\":\"pc\",\"name\":\"CEO Computer\",\"takeable\":false,\"lockType\":\"password\",\"requires\":\"ceo2024\",\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"postitNote\":\"Password: ceo2024\",\"showPostit\":true,\"observations\":\"The CEO's laptop, still warm - recently used. A sticky note is attached to the screen.\"},{\"type\":\"suitcase\",\"name\":\"CEO Briefcase\",\"takeable\":false,\"locked\":true,\"lockType\":\"key\",\"requires\":\"briefcase_key\",\"keyPins\":[45,35,25,55],\"difficulty\":\"medium\",\"observations\":\"An expensive leather briefcase with a sturdy lock\",\"contents\":[{\"type\":\"notes\",\"name\":\"Private Note\",\"takeable\":true,\"readable\":true,\"text\":\"Closet keypad code: 7391 - Must move evidence to safe before audit\",\"observations\":\"A hastily written note on expensive paper\"},{\"type\":\"key\",\"name\":\"Safe Key\",\"takeable\":true,\"key_id\":\"safe_key\",\"keyPins\":[52,29,44,37],\"observations\":\"A heavy-duty safe key hidden behind server equipment\"}]},{\"type\":\"phone\",\"name\":\"CEO Phone\",\"takeable\":false,\"readable\":true,\"text\":\"Recent calls: 'Offshore Bank', 'Unknown', 'Data Buyer'\",\"sender\":\"Call Log\",\"timestamp\":\"Last 24 hours\",\"observations\":\"The CEO's phone shows suspicious recent calls\"}]},\"closet\":{\"type\":\"room_closet\",\"connections\":{\"south\":\"ceo\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"7391\",\"objects\":[{\"type\":\"safe\",\"name\":\"Hidden Safe\",\"takeable\":false,\"locked\":true,\"lockType\":\"key\",\"requires\":\"safe_key\",\"keyPins\":[52,29,44,37],\"difficulty\":\"hard\",\"observations\":\"A well-hidden wall safe behind a painting\",\"contents\":[{\"type\":\"notes\",\"name\":\"Incriminating Documents\",\"takeable\":true,\"readable\":true,\"text\":\"Contract for sale of proprietary technology\\nBank transfers from competing companies\\nDetails of upcoming corporate espionage operations\",\"observations\":\"A folder containing damning evidence of corporate espionage. Congratulations! You've recovered the incriminating documents. flag{ceo_exfil_flag}\"}]}]},\"server1\":{\"type\":\"room_servers\",\"connections\":{\"south\":\"office3\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"4829\",\"objects\":[{\"type\":\"pc\",\"name\":\"Server Terminal\",\"takeable\":false,\"observations\":\"The main server terminal showing massive data exfiltration\"},{\"type\":\"key\",\"name\":\"Briefcase Key\",\"takeable\":true,\"key_id\":\"briefcase_key\",\"keyPins\":[45,35,25,55],\"observations\":\"A small key labeled 'Personal - Do Not Copy'\"}]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"phone\",\"name\":\"Your Phone\",\"takeable\":true,\"phoneId\":\"player_phone\",\"npcIds\":[\"neye_eve\",\"gossip_girl\",\"helper_npc\"],\"observations\":\"Your personal phone with some interesting contacts\"},{\"type\":\"workstation\",\"name\":\"Crypto Analysis Station\",\"takeable\":true,\"observations\":\"A powerful workstation for cryptographic analysis\"},{\"type\":\"lockpick\",\"name\":\"Lock Pick Kit\",\"takeable\":true,\"observations\":\"A professional lock picking kit with various picks and tension wrenches\"}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.739651"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.738891"], ["updated_at", "2025-11-29 01:43:57.738891"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Redirected to http://www.example.com/break_escape/games/1
+Completed 302 Found in 2ms (ActiveRecord: 0.2ms (4 queries, 0 cached) | GC: 0.1ms)
+ [1m[36mBreakEscape::Game Count (0.0ms)[0m [1m[34mSELECT COUNT(*) FROM "break_escape_games"[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_should_get_index
+----------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::MissionsController#index as HTML
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application
+ [1m[36mBreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mCACHE BreakEscape::Mission Pluck (0.0ms)[0m [1m[34mSELECT DISTINCT "break_escape_missions"."collection" FROM "break_escape_missions"[0m
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."published" = ?[0m [["published", 1]]
+ [1m[36mBreakEscape::Cybok Load (0.1ms)[0m [1m[34mSELECT "break_escape_cyboks".* FROM "break_escape_cyboks" WHERE "break_escape_cyboks"."cybokable_type" = ? AND "break_escape_cyboks"."cybokable_id" IN (?, ?)[0m [["cybokable_type", "BreakEscape::Mission"], ["cybokable_id", 418560898], ["cybokable_id", 899573729]]
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/shared/_cybok_label.html.erb (Duration: 0.0ms | GC: 0.0ms)
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/missions/index.html.erb within layouts/break_escape/application (Duration: 0.8ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.9ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 1.0ms | ActiveRecord: 0.1ms (5 queries, 1 cached) | GC: 0.1ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-----------------------------------------------------------------------
+BreakEscape::MissionsControllerTest: test_should_show_published_mission
+-----------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+Started GET "/break_escape/missions/418560898" for 127.0.0.1 at 2025-11-29 01:43:57 +0000
+Processing by BreakEscape::MissionsController#show as HTML
+ Parameters: {"id"=>"418560898"}
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."player_type" = ? AND "break_escape_games"."player_id" = ? AND "break_escape_games"."mission_id" = ? LIMIT ?[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"scenario_brief\":\"Hi! You are a cyber investigator tasked with uncovering evidence of corporate espionage. Anonymous tips suggest the CEO has been selling company secrets, but you need proof.\",\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"phone\",\"name\":\"Your Phone\",\"takeable\":true,\"phoneId\":\"player_phone\",\"npcIds\":[\"neye_eve\",\"gossip_girl\",\"helper_npc\"],\"observations\":\"Your personal phone with some interesting contacts\"},{\"type\":\"workstation\",\"name\":\"Crypto Analysis Station\",\"takeable\":true,\"observations\":\"A powerful workstation for cryptographic analysis\"},{\"type\":\"lockpick\",\"name\":\"Lock Pick Kit\",\"takeable\":true,\"observations\":\"A professional lock picking kit with various picks and tension wrenches\"}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office1\"},\"npcs\":[{\"id\":\"neye_eve\",\"displayName\":\"Neye Eve\",\"storyPath\":\"scenarios/ink/neye-eve.json\",\"avatar\":\"assets/npc/avatars/npc_adversary.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\"},{\"id\":\"gossip_girl\",\"displayName\":\"Gossip Girl\",\"storyPath\":\"scenarios/ink/gossip-girl.json\",\"avatar\":\"assets/npc/avatars/npc_neutral.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\",\"timedMessages\":[{\"delay\":5000,\"message\":\"Hey! 👋 Got any juicy gossip for me today?\",\"type\":\"text\"}]},{\"id\":\"helper_npc\",\"displayName\":\"Helpful Contact\",\"storyPath\":\"scenarios/ink/helper-npc.json\",\"avatar\":\"assets/npc/avatars/npc_helper.png\",\"phoneId\":\"player_phone\",\"currentKnot\":\"start\",\"npcType\":\"phone\",\"unlockable\":[\"secure_vault\",\"ceo\"],\"eventMappings\":[{\"eventPattern\":\"item_picked_up:lockpick\",\"targetKnot\":\"on_lockpick_pickup\",\"onceOnly\":true,\"cooldown\":0},{\"eventPattern\":\"minigame_completed\",\"targetKnot\":\"on_lockpick_success\",\"condition\":\"data.minigameName \\u0026\\u0026 data.minigameName.includes('Lockpick')\",\"cooldown\":10000},{\"eventPattern\":\"minigame_failed\",\"targetKnot\":\"on_lockpick_failed\",\"condition\":\"data.minigameName \\u0026\\u0026 data.minigameName.includes('Lockpick')\",\"cooldown\":15000},{\"eventPattern\":\"door_unlocked\",\"targetKnot\":\"on_door_unlocked\",\"cooldown\":8000},{\"eventPattern\":\"door_unlock_attempt\",\"targetKnot\":\"on_door_attempt\",\"cooldown\":12000},{\"eventPattern\":\"object_interacted\",\"targetKnot\":\"on_ceo_desk_interact\",\"condition\":\"data.objectType === 'desk_ceo'\",\"cooldown\":10000},{\"eventPattern\":\"item_picked_up:*\",\"targetKnot\":\"on_item_found\",\"cooldown\":20000},{\"eventPattern\":\"room_entered\",\"targetKnot\":\"on_room_entered\",\"cooldown\":45000,\"maxTriggers\":3},{\"eventPattern\":\"room_discovered\",\"targetKnot\":\"on_room_discovered\",\"cooldown\":15000,\"maxTriggers\":5},{\"eventPattern\":\"room_entered:ceo\",\"targetKnot\":\"on_ceo_office_entered\",\"onceOnly\":true}]}],\"objects\":[{\"type\":\"phone\",\"name\":\"Reception Phone\",\"takeable\":false,\"readable\":true,\"voice\":\"Hi, this is the IT Team. Security breach detected in server room. Changed access code to 4829.\",\"sender\":\"IT Team\",\"timestamp\":\"2:15 AM\",\"observations\":\"The reception phone's message light is blinking urgently\"},{\"type\":\"notes\",\"name\":\"Security Log\",\"takeable\":true,\"readable\":true,\"text\":\"Unusual after-hours access detected:\\n- CEO office: 11:30 PM\\n- Server room: 2:15 AM\\n- CEO office again: 3:45 AM\",\"observations\":\"A concerning security log from last night\"},{\"type\":\"pc\",\"name\":\"Reception Computer\",\"takeable\":false,\"lockType\":\"password\",\"passwordHint\":\"Optional hint text\",\"showHint\":true,\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"requires\":\"secret123\",\"observations\":\"The reception's computer, currently locked\",\"postitNote\":\"Password: secret123\",\"showPostit\":true,\"contents\":[{\"type\":\"text_file\",\"name\":\"Private\",\"takeable\":false,\"readable\":true,\"text\":\"Closet keypad code: 7391 - Must move evidence to safe before audit\"}]},{\"type\":\"tablet\",\"name\":\"Tablet Device\",\"takeable\":true,\"locked\":true,\"lockType\":\"bluetooth\",\"requires\":\"bluetooth\",\"mac\":\"00:11:22:33:44:55\",\"observations\":\"A locked tablet device that requires Bluetooth pairing\"},{\"type\":\"bluetooth_scanner\",\"name\":\"Bluetooth Scanner\",\"takeable\":true,\"observations\":\"A device for detecting nearby Bluetooth signals\",\"canScanBluetooth\":true},{\"type\":\"key\",\"name\":\"Office Key\",\"takeable\":true,\"key_id\":\"office1_key\",\"keyPins\":[65,25,65,25],\"observations\":\"A key to access the office areas\"},{\"type\":\"pin-cracker\",\"name\":\"PIN Cracker\",\"takeable\":true,\"observations\":\"A sophisticated device that can analyze PIN entry patterns and provide feedback on attempts\"},{\"type\":\"safe\",\"name\":\"Reception Safe\",\"takeable\":false,\"locked\":true,\"lockType\":\"pin\",\"requires\":\"9573\",\"observations\":\"A small wall safe behind the reception desk. Looks like it needs a 4-digit code.\",\"contents\":[{\"type\":\"notes\",\"name\":\"IT Access Credentials\",\"takeable\":true,\"readable\":true,\"text\":\"Emergency IT Admin Credentials:\\nUsername: admin\\nPassword: ITsecure2024\\n\\nServer Room Backup Code: 4829\\nCEO Office Alarm Override: 1337\",\"observations\":\"Sensitive IT credentials that could be very useful\"}]}]},\"office1\":{\"type\":\"room_office\",\"locked\":true,\"lockType\":\"key\",\"requires\":\"office1_key\",\"keyPins\":[65,25,65,25],\"difficulty\":\"easy\",\"door_sign\":\"4A Hot Desks\",\"connections\":{\"north\":[\"office2\",\"office3\"],\"south\":\"reception\"},\"objects\":[{\"type\":\"pc\",\"name\":\"Office Computer\",\"takeable\":false,\"requires\":\"password\",\"hasFingerprint\":true,\"fingerprintOwner\":\"ceo\",\"fingerprintDifficulty\":\"medium\",\"observations\":\"A computer with a cybersecurity alert on screen. There might be fingerprints on the keyboard.\"},{\"type\":\"notes\",\"name\":\"IT Memo\",\"takeable\":true,\"readable\":true,\"text\":\"URGENT: Multiple unauthorized access attempts detected from CEO's office IP address\",\"observations\":\"A concerning IT department memo\"},{\"type\":\"fingerprint_kit\",\"name\":\"Fingerprint Kit\",\"takeable\":true,\"observations\":\"A kit used for collecting fingerprints from surfaces\"}]},\"office2\":{\"type\":\"room_office\",\"connections\":{\"north\":\"ceo\",\"south\":\"office1\"},\"objects\":[{\"type\":\"pc\",\"name\":\"Office Computer\",\"takeable\":false,\"lockType\":\"password\",\"requires\":\"office2024\",\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"postitNote\":\"Password: office2024\",\"showPostit\":true,\"observations\":\"A standard office computer with a sticky note on the monitor\"},{\"type\":\"notes\",\"name\":\"Shredded Document\",\"takeable\":true,\"readable\":true,\"text\":\"Partially readable: '...offshore account...transfer complete...delete all traces...'\",\"observations\":\"A partially shredded document that someone failed to dispose of properly\"},{\"type\":\"key\",\"name\":\"CEO Office Key\",\"takeable\":true,\"key_id\":\"ceo_office_key\",\"keyPins\":[25,45,65,75],\"observations\":\"A spare key to the CEO's office, carelessly left behind\"}]},\"office3\":{\"type\":\"room_office\",\"connections\":{\"north\":\"server1\",\"south\":\"office1\"},\"objects\":[{\"type\":\"pc\",\"name\":\"IT Staff Computer\",\"takeable\":false,\"requires\":\"bluetooth\",\"lockType\":\"bluetooth\",\"mac\":\"00:11:22:33:44:55\",\"observations\":\"An IT staff computer showing network security logs\"},{\"type\":\"notes\",\"name\":\"Network Logs\",\"takeable\":true,\"readable\":true,\"text\":\"Large data transfers detected to unknown external IPs - All originating from CEO's office\",\"observations\":\"Suspicious network activity logs\"}]},\"ceo\":{\"type\":\"room_ceo\",\"connections\":{\"north\":\"closet\",\"south\":\"office2\"},\"locked\":true,\"lockType\":\"key\",\"requires\":\"ceo_office_key\",\"keyPins\":[25,45,65,75],\"difficulty\":\"easy\",\"objects\":[{\"type\":\"pc\",\"name\":\"CEO Computer\",\"takeable\":false,\"lockType\":\"password\",\"requires\":\"ceo2024\",\"showKeyboard\":true,\"maxAttempts\":3,\"locked\":true,\"postitNote\":\"Password: ceo2024\",\"showPostit\":true,\"observations\":\"The CEO's laptop, still warm - recently used. A sticky note is attached to the screen.\"},{\"type\":\"suitcase\",\"name\":\"CEO Briefcase\",\"takeable\":false,\"locked\":true,\"lockType\":\"key\",\"requires\":\"briefcase_key\",\"keyPins\":[45,35,25,55],\"difficulty\":\"medium\",\"observations\":\"An expensive leather briefcase with a sturdy lock\",\"contents\":[{\"type\":\"notes\",\"name\":\"Private Note\",\"takeable\":true,\"readable\":true,\"text\":\"Closet keypad code: 7391 - Must move evidence to safe before audit\",\"observations\":\"A hastily written note on expensive paper\"},{\"type\":\"key\",\"name\":\"Safe Key\",\"takeable\":true,\"key_id\":\"safe_key\",\"keyPins\":[52,29,44,37],\"observations\":\"A heavy-duty safe key hidden behind server equipment\"}]},{\"type\":\"phone\",\"name\":\"CEO Phone\",\"takeable\":false,\"readable\":true,\"text\":\"Recent calls: 'Offshore Bank', 'Unknown', 'Data Buyer'\",\"sender\":\"Call Log\",\"timestamp\":\"Last 24 hours\",\"observations\":\"The CEO's phone shows suspicious recent calls\"}]},\"closet\":{\"type\":\"room_closet\",\"connections\":{\"south\":\"ceo\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"7391\",\"objects\":[{\"type\":\"safe\",\"name\":\"Hidden Safe\",\"takeable\":false,\"locked\":true,\"lockType\":\"key\",\"requires\":\"safe_key\",\"keyPins\":[52,29,44,37],\"difficulty\":\"hard\",\"observations\":\"A well-hidden wall safe behind a painting\",\"contents\":[{\"type\":\"notes\",\"name\":\"Incriminating Documents\",\"takeable\":true,\"readable\":true,\"text\":\"Contract for sale of proprietary technology\\nBank transfers from competing companies\\nDetails of upcoming corporate espionage operations\",\"observations\":\"A folder containing damning evidence of corporate espionage. Congratulations! You've recovered the incriminating documents. flag{ceo_exfil_flag}\"}]}]},\"server1\":{\"type\":\"room_servers\",\"connections\":{\"south\":\"office3\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"4829\",\"objects\":[{\"type\":\"pc\",\"name\":\"Server Terminal\",\"takeable\":false,\"observations\":\"The main server terminal showing massive data exfiltration\"},{\"type\":\"key\",\"name\":\"Briefcase Key\",\"takeable\":true,\"key_id\":\"briefcase_key\",\"keyPins\":[45,35,25,55],\"observations\":\"A small key labeled 'Personal - Do Not Copy'\"}]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"phone\",\"name\":\"Your Phone\",\"takeable\":true,\"phoneId\":\"player_phone\",\"npcIds\":[\"neye_eve\",\"gossip_girl\",\"helper_npc\"],\"observations\":\"Your personal phone with some interesting contacts\"},{\"type\":\"workstation\",\"name\":\"Crypto Analysis Station\",\"takeable\":true,\"observations\":\"A powerful workstation for cryptographic analysis\"},{\"type\":\"lockpick\",\"name\":\"Lock Pick Kit\",\"takeable\":true,\"observations\":\"A professional lock picking kit with various picks and tension wrenches\"}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:43:57.745006"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:43:57.744364"], ["updated_at", "2025-11-29 01:43:57.744364"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Redirected to http://www.example.com/break_escape/games/1
+Completed 302 Found in 2ms (ActiveRecord: 0.3ms (4 queries, 0 cached) | GC: 0.2ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mActiveRecord::InternalMetadata Load (0.1ms)[0m [1m[34mSELECT * FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? ORDER BY "ar_internal_metadata"."key" ASC LIMIT 1[0m [[nil, "schema_sha1"]]
+ [1m[36mActiveRecord::SchemaMigration Load (0.0ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+ [1m[35m (0.1ms)[0m [1m[35mPRAGMA foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = ON[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = OFF[0m
+ [1m[36mFixtures Load (0.1ms)[0m [1m[31mDELETE FROM "break_escape_demo_users";
+DELETE FROM "break_escape_missions";
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (149617800, 'test_user', '2025-11-29 01:57:34', '2025-11-29 01:57:34');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-29 01:57:34', '2025-11-29 01:57:34');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (418560898, 'ceo_exfil', 'CEO Exfiltration', 'Test scenario', 1, 3, '2025-11-29 01:57:34', '2025-11-29 01:57:34');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (636030761, 'test_unpublished', 'Unpublished Test', 'Not visible', 0, 1, '2025-11-29 01:57:34', '2025-11-29 01:57:34');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at", "secgen_scenario", "collection") VALUES (899573729, 'secgen_vm_lab', 'SecGen VM Lab - Linux Introduction', 'Test VM and flag integration with SecGen scenario', 1, 2, '2025-11-29 01:57:34', '2025-11-29 01:57:34', 'labs/introducing_attacks/1_intro_linux.xml', 'vm_labs')[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = 0[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = 1[0m
+ [1m[36mTRANSACTION (0.9ms)[0m [1m[36mcommit transaction[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_key_check[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_sync_state_should_update_player_state_for_current_room
+---------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.2ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.299350"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.299257"], ["updated_at", "2025-11-29 01:57:34.299257"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started PUT "/break_escape/games/1/sync_state" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#sync_state as HTML
+ Parameters: {"currentRoom"=>"reception", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.1ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 5ms (Views: 0.1ms | ActiveRecord: 0.3ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_return_HTML_with_game_container
+----------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.316455"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.316403"], ["updated_at", "2025-11-29 01:57:34.316403"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 1.6ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.7ms | GC: 0.0ms)
+Completed 200 OK in 7ms (Views: 3.5ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_require_npc_parameter
+--------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.326652"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.326593"], ["updated_at", "2025-11-29 01:57:34.326593"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 400 Bad Request in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_non-existent_NPC
+------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.329345"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.329303"], ["updated_at", "2025-11-29 01:57:34.329303"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=non-existent" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"non-existent", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: non-existent
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_reject_invalid_attempts
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.331714"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.331648"], ["updated_at", "2025-11-29 01:57:34.331648"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"room", "targetId"=>"office", "attempt"=>"wrong_code", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=room, id=office, attempt=wrong_code, method=pin
+[BreakEscape] Object not found: office
+Completed 422 Unprocessable Content in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_scenario_endpoint_should_return_JSON
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.333919"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.333870"], ["updated_at", "2025-11-29 01:57:34.333870"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/scenario" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#scenario as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_NPC_without_story_file
+------------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.336097"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.336056"], ["updated_at", "2025-11-29 01:57:34.336056"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=missing-npc" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"missing-npc", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: missing-npc
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_game_setup_has_correct_scenario_data
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.338392"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.338350"], ["updated_at", "2025-11-29 01:57:34.338350"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_inject_game_configuration
+----------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.339649"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.339592"], ["updated_at", "2025-11-29 01:57:34.339592"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.2ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_accept_correct_pin_code
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.342655"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.342611"], ["updated_at", "2025-11-29 01:57:34.342611"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"door", "targetId"=>"office", "attempt"=>"1234", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=door, id=office, attempt=1234, method=pin
+[BreakEscape] Room data: locked=true, lockType=pin, requires=1234
+[BreakEscape] Room is LOCKED, method must be valid: pin
+[BreakEscape] pin validation result: true
+[BreakEscape] validate_unlock returning: true
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\",\"office\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 01:57:34.344485"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.2ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------
+BreakEscape::GamesControllerTest: test_should_show_game
+-------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.345939"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.345894"], ["updated_at", "2025-11-29 01:57:34.345894"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.2ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_inventory_endpoint_should_add_items
+--------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 01:57:34.348845"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 01:57:34.348803"], ["updated_at", "2025-11-29 01:57:34.348803"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Update (0.1ms)[0m [1m[33mUPDATE "break_escape_games" SET "scenario_data" = ?, "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[{\"id\":\"note_1\",\"type\":\"note\",\"name\":\"Test Note\",\"takeable\":true}]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 01:57:34.349373"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/inventory" for 127.0.0.1 at 2025-11-29 01:57:34 +0000
+Processing by BreakEscape::GamesController#inventory as HTML
+ Parameters: {"action_type"=>"add", "item"=>{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}, "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] inventory endpoint: action=add, item=#"note", "name"=>"Test Note", "id"=>"note_1"} permitted: false>
+[BreakEscape] validate_item_collectible: type=note, id=note_1, name=Test Note
+[BreakEscape] Item collection valid: note
+[BreakEscape] Adding item to inventory: note / Test Note
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"note\",\"name\":\"Test Note\",\"id\":\"note_1\"}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 01:57:34.351035"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+[BreakEscape] Item added successfully. Current inventory: [{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}]
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.2ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mActiveRecord::InternalMetadata Load (0.0ms)[0m [1m[34mSELECT * FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? ORDER BY "ar_internal_metadata"."key" ASC LIMIT 1[0m [[nil, "schema_sha1"]]
+ [1m[36mActiveRecord::SchemaMigration Load (0.0ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+ [1m[35m (0.1ms)[0m [1m[35mPRAGMA foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = ON[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = OFF[0m
+ [1m[36mFixtures Load (0.1ms)[0m [1m[31mDELETE FROM "break_escape_demo_users";
+DELETE FROM "break_escape_missions";
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (149617800, 'test_user', '2025-11-29 12:15:49', '2025-11-29 12:15:49');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-29 12:15:49', '2025-11-29 12:15:49');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (418560898, 'ceo_exfil', 'CEO Exfiltration', 'Test scenario', 1, 3, '2025-11-29 12:15:49', '2025-11-29 12:15:49');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (636030761, 'test_unpublished', 'Unpublished Test', 'Not visible', 0, 1, '2025-11-29 12:15:49', '2025-11-29 12:15:49');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at", "secgen_scenario", "collection") VALUES (899573729, 'secgen_vm_lab', 'SecGen VM Lab - Linux Introduction', 'Test VM and flag integration with SecGen scenario', 1, 2, '2025-11-29 12:15:49', '2025-11-29 12:15:49', 'labs/introducing_attacks/1_intro_linux.xml', 'vm_labs')[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = 0[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = 1[0m
+ [1m[36mTRANSACTION (1.1ms)[0m [1m[36mcommit transaction[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_key_check[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_accept_correct_pin_code
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.731251"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.731106"], ["updated_at", "2025-11-29 12:15:49.731106"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"door", "targetId"=>"office", "attempt"=>"1234", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.1ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=door, id=office, attempt=1234, method=pin
+[BreakEscape] Room data: locked=true, lockType=pin, requires=1234
+[BreakEscape] Room is LOCKED, method must be valid: pin
+[BreakEscape] pin validation result: true
+[BreakEscape] validate_unlock returning: true
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\",\"office\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 12:15:49.743134"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 4ms (Views: 0.1ms | ActiveRecord: 0.3ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_return_HTML_with_game_container
+----------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.745223"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.745182"], ["updated_at", "2025-11-29 12:15:49.745182"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.1ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 1.5ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.6ms | GC: 0.0ms)
+Completed 200 OK in 5ms (Views: 2.8ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_sync_state_should_update_player_state_for_current_room
+---------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.753273"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.753227"], ["updated_at", "2025-11-29 12:15:49.753227"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started PUT "/break_escape/games/1/sync_state" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#sync_state as HTML
+ Parameters: {"currentRoom"=>"reception", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.2ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_non-existent_NPC
+------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.756194"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.756150"], ["updated_at", "2025-11-29 12:15:49.756150"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=non-existent" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"non-existent", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: non-existent
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_NPC_without_story_file
+------------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.758572"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.758511"], ["updated_at", "2025-11-29 12:15:49.758511"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=missing-npc" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"missing-npc", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: missing-npc
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_inject_game_configuration
+----------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.760769"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.760730"], ["updated_at", "2025-11-29 12:15:49.760730"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.2ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_inventory_endpoint_should_add_items
+--------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.763515"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.763457"], ["updated_at", "2025-11-29 12:15:49.763457"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Update (0.1ms)[0m [1m[33mUPDATE "break_escape_games" SET "scenario_data" = ?, "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[{\"id\":\"note_1\",\"type\":\"note\",\"name\":\"Test Note\",\"takeable\":true}]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 12:15:49.764021"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/inventory" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#inventory as HTML
+ Parameters: {"action_type"=>"add", "item"=>{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}, "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] inventory endpoint: action=add, item=#"note", "name"=>"Test Note", "id"=>"note_1"} permitted: false>
+[BreakEscape] validate_item_collectible: type=note, id=note_1, name=Test Note
+[BreakEscape] Item collection valid: note
+[BreakEscape] Adding item to inventory: note / Test Note
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"note\",\"name\":\"Test Note\",\"id\":\"note_1\"}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 12:15:49.765550"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+[BreakEscape] Item added successfully. Current inventory: [{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}]
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.2ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_reject_invalid_attempts
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.766846"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.766811"], ["updated_at", "2025-11-29 12:15:49.766811"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"room", "targetId"=>"office", "attempt"=>"wrong_code", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=room, id=office, attempt=wrong_code, method=pin
+[BreakEscape] Object not found: office
+Completed 422 Unprocessable Content in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_game_setup_has_correct_scenario_data
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.768923"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.768881"], ["updated_at", "2025-11-29 12:15:49.768881"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_scenario_endpoint_should_return_JSON
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.770071"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.770019"], ["updated_at", "2025-11-29 12:15:49.770019"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/scenario" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#scenario as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------
+BreakEscape::GamesControllerTest: test_should_show_game
+-------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.772238"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.772200"], ["updated_at", "2025-11-29 12:15:49.772200"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.1ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_require_npc_parameter
+--------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 12:15:49.774813"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 12:15:49.774775"], ["updated_at", "2025-11-29 12:15:49.774775"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink" for 127.0.0.1 at 2025-11-29 12:15:49 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 400 Bad Request in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mActiveRecord::InternalMetadata Load (0.1ms)[0m [1m[34mSELECT * FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? ORDER BY "ar_internal_metadata"."key" ASC LIMIT 1[0m [[nil, "schema_sha1"]]
+ [1m[36mActiveRecord::SchemaMigration Load (0.0ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
+ [1m[36mTRANSACTION (0.1ms)[0m [1m[36mbegin transaction[0m
+ [1m[35m (0.1ms)[0m [1m[35mPRAGMA foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = ON[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = OFF[0m
+ [1m[36mFixtures Load (0.1ms)[0m [1m[31mDELETE FROM "break_escape_demo_users";
+DELETE FROM "break_escape_missions";
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (149617800, 'test_user', '2025-11-29 15:58:43', '2025-11-29 15:58:43');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-29 15:58:43', '2025-11-29 15:58:43');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (418560898, 'ceo_exfil', 'CEO Exfiltration', 'Test scenario', 1, 3, '2025-11-29 15:58:43', '2025-11-29 15:58:43');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at") VALUES (636030761, 'test_unpublished', 'Unpublished Test', 'Not visible', 0, 1, '2025-11-29 15:58:43', '2025-11-29 15:58:43');
+INSERT INTO "break_escape_missions" ("id", "name", "display_name", "description", "published", "difficulty_level", "created_at", "updated_at", "secgen_scenario", "collection") VALUES (899573729, 'secgen_vm_lab', 'SecGen VM Lab - Linux Introduction', 'Test VM and flag integration with SecGen scenario', 1, 2, '2025-11-29 15:58:43', '2025-11-29 15:58:43', 'labs/introducing_attacks/1_intro_linux.xml', 'vm_labs')[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA defer_foreign_keys = 0[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_keys = 1[0m
+ [1m[36mTRANSACTION (2.4ms)[0m [1m[36mcommit transaction[0m
+ [1m[35m (0.0ms)[0m [1m[35mPRAGMA foreign_key_check[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_sync_state_should_update_player_state_for_current_room
+---------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.1ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.2ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.307093"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.307002"], ["updated_at", "2025-11-29 15:58:43.307002"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started PUT "/break_escape/games/1/sync_state" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#sync_state as HTML
+ Parameters: {"currentRoom"=>"reception", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.1ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 4ms (Views: 0.1ms | ActiveRecord: 0.2ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_require_npc_parameter
+--------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.323342"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.323294"], ["updated_at", "2025-11-29 15:58:43.323294"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 400 Bad Request in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_reject_invalid_attempts
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.325685"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.325646"], ["updated_at", "2025-11-29 15:58:43.325646"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"room", "targetId"=>"office", "attempt"=>"wrong_code", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=room, id=office, attempt=wrong_code, method=pin
+[BreakEscape] Object not found: office
+Completed 422 Unprocessable Content in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_game_setup_has_correct_scenario_data
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.327763"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.327725"], ["updated_at", "2025-11-29 15:58:43.327725"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+--------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_inventory_endpoint_should_add_items
+--------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.328946"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.328907"], ["updated_at", "2025-11-29 15:58:43.328907"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Update (0.1ms)[0m [1m[33mUPDATE "break_escape_games" SET "scenario_data" = ?, "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[{\"id\":\"note_1\",\"type\":\"note\",\"name\":\"Test Note\",\"takeable\":true}]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 15:58:43.329464"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/inventory" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#inventory as HTML
+ Parameters: {"action_type"=>"add", "item"=>{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}, "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] inventory endpoint: action=add, item=#"note", "name"=>"Test Note", "id"=>"note_1"} permitted: false>
+[BreakEscape] validate_item_collectible: type=note, id=note_1, name=Test Note
+[BreakEscape] Item collection valid: note
+[BreakEscape] Adding item to inventory: note / Test Note
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"note\",\"name\":\"Test Note\",\"id\":\"note_1\"}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 15:58:43.331053"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+[BreakEscape] Item added successfully. Current inventory: [{"type"=>"note", "name"=>"Test Note", "id"=>"note_1"}]
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.2ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_return_HTML_with_game_container
+----------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.332450"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.332410"], ["updated_at", "2025-11-29 15:58:43.332410"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 1.5ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.6ms | GC: 0.0ms)
+Completed 200 OK in 7ms (Views: 3.6ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+---------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_scenario_endpoint_should_return_JSON
+---------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.342244"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.342200"], ["updated_at", "2025-11-29 15:58:43.342200"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/scenario" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#scenario as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+----------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_show_should_inject_game_configuration
+----------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.344593"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.344552"], ["updated_at", "2025-11-29 15:58:43.344552"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.2ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_unlock_endpoint_should_accept_correct_pin_code
+-------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.347589"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.347524"], ["updated_at", "2025-11-29 15:58:43.347524"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started POST "/break_escape/games/1/unlock" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#unlock as HTML
+ Parameters: {"targetType"=>"door", "targetId"=>"office", "attempt"=>"1234", "method"=>"pin", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] validate_unlock: type=door, id=office, attempt=1234, method=pin
+[BreakEscape] Room data: locked=true, lockType=pin, requires=1234
+[BreakEscape] Room is LOCKED, method must be valid: pin
+[BreakEscape] pin validation result: true
+[BreakEscape] validate_unlock returning: true
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Mission Load (0.1ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Game Update (0.0ms)[0m [1m[33mUPDATE "break_escape_games" SET "player_state" = ?, "updated_at" = ? WHERE "break_escape_games"."id" = ?[0m [["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\",\"office\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["updated_at", "2025-11-29 15:58:43.349438"], ["id", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Completed 200 OK in 1ms (Views: 0.0ms | ActiveRecord: 0.2ms (5 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+-------------------------------------------------------
+BreakEscape::GamesControllerTest: test_should_show_game
+-------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.350849"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.350809"], ["updated_at", "2025-11-29 15:58:43.350809"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#show as HTML
+ Parameters: {"id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ Rendering layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb
+ Rendering /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application
+ Rendered /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/break_escape/games/show.html.erb within layouts/break_escape/application (Duration: 0.1ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.1ms | GC: 0.0ms)
+Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.1ms (4 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_non-existent_NPC
+------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.353678"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.353638"], ["updated_at", "2025-11-29 15:58:43.353638"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=non-existent" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"non-existent", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: non-existent
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+------------------------------------------------------------------------------------------------
+BreakEscape::GamesControllerTest: test_ink_endpoint_should_return_404_for_NPC_without_story_file
+------------------------------------------------------------------------------------------------
+ [1m[36mBreakEscape::Mission Load (0.0ms)[0m [1m[34mSELECT "break_escape_missions".* FROM "break_escape_missions" WHERE "break_escape_missions"."id" = ? LIMIT ?[0m [["id", 418560898], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mSAVEPOINT active_record_1[0m
+ [1m[36mBreakEscape::Game Create (0.1ms)[0m [1m[32mINSERT INTO "break_escape_games" ("player_type", "player_id", "mission_id", "scenario_data", "player_state", "status", "started_at", "completed_at", "score", "created_at", "updated_at", "objectives_completed", "tasks_completed") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING "id"[0m [["player_type", "BreakEscape::DemoUser"], ["player_id", 149617800], ["mission_id", 418560898], ["scenario_data", "{\"startRoom\":\"reception\",\"startItemsInInventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"rooms\":{\"reception\":{\"type\":\"room_reception\",\"connections\":{\"north\":\"office\"},\"locked\":false,\"objects\":[]},\"office\":{\"type\":\"office\",\"connections\":{\"south\":\"reception\"},\"locked\":true,\"lockType\":\"pin\",\"requires\":\"1234\",\"objects\":[]}}}"], ["player_state", "{\"currentRoom\":\"reception\",\"unlockedRooms\":[\"reception\"],\"unlockedObjects\":[],\"inventory\":[{\"type\":\"lockpick\",\"name\":\"Lockpick\",\"id\":\"lockpick_1\",\"takeable\":true}],\"encounteredNPCs\":[],\"globalVariables\":{},\"biometricSamples\":[],\"biometricUnlocks\":[],\"bluetoothDevices\":[],\"notes\":[],\"health\":100,\"submitted_flags\":[],\"flag_rewards_claimed\":[],\"pending_events\":[]}"], ["status", "in_progress"], ["started_at", "2025-11-29 15:58:43.355891"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-29 15:58:43.355852"], ["updated_at", "2025-11-29 15:58:43.355852"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink?npc=missing-npc" for 127.0.0.1 at 2025-11-29 15:58:43 +0000
+Processing by BreakEscape::GamesController#ink as HTML
+ Parameters: {"npc"=>"missing-npc", "id"=>"1"}
+ [1m[36mBreakEscape::Game Load (0.0ms)[0m [1m[34mSELECT "break_escape_games".* FROM "break_escape_games" WHERE "break_escape_games"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" ORDER BY "break_escape_demo_users"."id" ASC LIMIT ?[0m [["LIMIT", 1]]
+ [1m[36mBreakEscape::DemoUser Load (0.0ms)[0m [1m[34mSELECT "break_escape_demo_users".* FROM "break_escape_demo_users" WHERE "break_escape_demo_users"."id" = ? LIMIT ?[0m [["id", 149617800], ["LIMIT", 1]]
+[BreakEscape] Loading ink for NPC: missing-npc
+[BreakEscape] No NPCs found in scenario data
+Completed 404 Not Found in 1ms (Views: 0.0ms | ActiveRecord: 0.1ms (3 queries, 0 cached) | GC: 0.0ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
diff --git a/test/dummy/storage/test.sqlite3 b/test/dummy/storage/test.sqlite3
index 2999b9b..1007fc2 100644
Binary files a/test/dummy/storage/test.sqlite3 and b/test/dummy/storage/test.sqlite3 differ