+
Configure Test Flags
+
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 %>
+
+
+
+
+
💡 How it works:
+
+ - Enter the flags you plan to capture in this scenario
+ - During the game, navigate to the Flag Station
+ - Submit your captured flags to verify them
+ - Receive rewards for correct flags
+
+
+
+ <%= f.submit "Start Mission", class: "btn btn-primary btn-large" %>
+ <% end %>
+
+ <%= link_to "← Back to Missions", missions_path, class: "btn btn-secondary" %>
+
+ <% end %>
<% else %>
<%# Non-VM mission - just start %>
@@ -108,6 +145,88 @@
padding-bottom: 10px;
margin-bottom: 20px;
}
+
+ .standalone-flags h2 {
+ color: #00ff00;
+ border-bottom: 2px solid #00ff00;
+ padding-bottom: 10px;
+ margin-bottom: 20px;
+ }
+
+ .flags-help {
+ color: #cccccc;
+ font-family: 'VT323', monospace;
+ font-size: 14px;
+ margin-bottom: 20px;
+ line-height: 1.5;
+ }
+
+ .form-group {
+ margin-bottom: 20px;
+ }
+
+ .form-group label {
+ display: block;
+ color: #00ff00;
+ margin-bottom: 10px;
+ font-size: 14px;
+ font-weight: bold;
+ }
+
+ .flags-textarea {
+ width: 100%;
+ background: #000;
+ border: 2px solid #00ff00;
+ color: #00ff00;
+ padding: 12px;
+ font-family: 'Courier New', monospace;
+ font-size: 14px;
+ box-sizing: border-box;
+ resize: vertical;
+ }
+
+ .flags-textarea:focus {
+ outline: none;
+ border-color: #00ff00;
+ box-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
+ }
+
+ .form-hint {
+ color: #888;
+ font-size: 12px;
+ margin-top: 8px;
+ }
+
+ .form-hint code {
+ background: #1a1a1a;
+ padding: 2px 6px;
+ border-radius: 2px;
+ }
+
+ .flags-info {
+ background: rgba(0, 255, 0, 0.05);
+ border: 1px solid #00ff00;
+ padding: 15px;
+ margin: 20px 0;
+ color: #cccccc;
+ font-size: 13px;
+ line-height: 1.5;
+ }
+
+ .flags-info p {
+ margin: 0 0 10px 0;
+ color: #00ff00;
+ }
+
+ .flags-info ul {
+ margin: 10px 0 0 20px;
+ padding: 0;
+ }
+
+ .flags-info li {
+ margin: 5px 0;
+ color: #cccccc;
+ }
.vm-set-list {
display: flex;
@@ -181,6 +300,7 @@
cursor: pointer;
text-decoration: none;
display: inline-block;
+ margin-top: 10px;
}
.btn:hover {
@@ -196,6 +316,8 @@
background: #333;
color: #00ff00;
border-color: #00ff00;
+ width: auto;
+ margin-right: 10px;
}
.btn-secondary:hover {
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 d986b0e..ac8c573 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
@@ -14,7 +14,7 @@ export class FlagStationMinigame extends MinigameScene {
this.stationName = params.stationName || 'Flag Submission Terminal';
this.expectedFlags = params.flags || [];
this.submittedFlags = params.submittedFlags || window.gameState?.submittedFlags || [];
- this.gameId = params.gameId || window.gameConfig?.gameId;
+ this.gameId = params.gameId || window.breakEscapeConfig?.gameId || window.gameConfig?.gameId;
this.isSubmitting = false;
}
diff --git a/public/break_escape/js/minigames/vm-launcher/vm-launcher-minigame.js b/public/break_escape/js/minigames/vm-launcher/vm-launcher-minigame.js
index 45e7864..c083d16 100644
--- a/public/break_escape/js/minigames/vm-launcher/vm-launcher-minigame.js
+++ b/public/break_escape/js/minigames/vm-launcher/vm-launcher-minigame.js
@@ -205,6 +205,28 @@ export class VmLauncherMinigame extends MinigameScene {
color: #ffaa00;
}
+ .vm-names {
+ display: flex;
+ gap: 15px;
+ justify-content: center;
+ margin: 20px 0;
+ }
+
+ .vm-name-badge {
+ background: #00aa00;
+ color: #000;
+ padding: 12px 24px;
+ font-weight: bold;
+ font-size: 16px;
+ border: 2px solid #000;
+ font-family: 'Courier New', monospace;
+ }
+
+ .standalone-instructions h3 {
+ color: #00ff00;
+ margin-top: 0;
+ }
+
.standalone-instructions ol {
margin: 0;
padding-left: 20px;
@@ -242,9 +264,9 @@ export class VmLauncherMinigame extends MinigameScene {
`;
} else {
return `
-
-
Standalone Mode
-
VMs are not available in standalone mode.
+
+
VM Terminal
+
You've discovered a computer terminal in the game. To interact with it, you need to launch the virtual machines on your local system.
${this.buildStandaloneInstructions()}
`;
@@ -300,13 +322,17 @@ export class VmLauncherMinigame extends MinigameScene {
buildStandaloneInstructions() {
return `
-
VirtualBox Instructions
+
Load These VMs in VirtualBox:
+
- - Open
VirtualBox on your local machine
- - Import the mission VM file (.ova)
- - Start the VM and wait for it to boot
- - Note the VM's IP address (shown on login screen)
- - Return to this game and complete objectives
+ - Open VirtualBox on your local machine
+ - Import the kali and desktop VMs (.ova files)
+ - Start both VMs and wait for them to boot
+ - Note their IP addresses
+ - Return to this game to complete the mission
`;
diff --git a/scenarios/secgen_vm_lab/mission.json b/scenarios/secgen_vm_lab/mission.json
new file mode 100644
index 0000000..050cd60
--- /dev/null
+++ b/scenarios/secgen_vm_lab/mission.json
@@ -0,0 +1,20 @@
+{
+ "display_name": "SecGen VM Lab - Linux Introduction",
+ "description": "This mission demonstrates VM integration with BreakEscape. Launch the provided VMs (desktop and kali) and capture flags from the SecGen environment. Learn basic Linux attack techniques in a controlled lab setting.",
+ "difficulty_level": 2,
+ "secgen_scenario": "labs/introducing_attacks/1_intro_linux.xml",
+ "collection": "vm_labs",
+ "cybok": [
+ {
+ "ka": "SS",
+ "topic": "System Security",
+ "keywords": ["Virtual machines", "Linux security", "Attack vectors"]
+ },
+ {
+ "ka": "F",
+ "topic": "Forensics",
+ "keywords": ["Evidence collection", "Log analysis"]
+ }
+ ]
+}
+
diff --git a/scenarios/secgen_vm_lab/scenario.json.erb b/scenarios/secgen_vm_lab/scenario.json.erb
new file mode 100644
index 0000000..d3f1614
--- /dev/null
+++ b/scenarios/secgen_vm_lab/scenario.json.erb
@@ -0,0 +1,109 @@
+{
+ "scenario_brief": "Welcome to the SecGen VM Lab! This environment is designed to teach you about Linux security and attack techniques. You have access to two VMs:\n\n• **kali** - Attacker machine with penetration testing tools\n• **desktop** - Target system running vulnerable services\n\nYour mission is to launch these VMs, exploit vulnerabilities, and capture flags demonstrating your understanding of the attack vectors.",
+ "endGoal": "Successfully launch the 'kali' and 'desktop' VMs, perform attacks, and capture all available flags to complete the lab.",
+ "startRoom": "lab_entrance",
+ "startItemsInInventory": [],
+ "rooms": {
+ "lab_entrance": {
+ "type": "room_reception",
+ "connections": {
+ "north": "lab_workstation"
+ },
+ "locked": false,
+ "objects": [
+ {
+ "type": "notes",
+ "name": "Lab Welcome Guide",
+ "takeable": true,
+ "readable": true,
+ "text": "Welcome to the SecGen Linux Introduction Lab!\n\nSTART HERE:\n1. Interact with the VM LAUNCHER TERMINAL\n2. Select and launch the 'kali' VM first\n3. Then launch the 'desktop' VM\n\nWhat each VM does:\n • kali - Your attack platform (Kali Linux with hacking tools)\n • desktop - The target system (vulnerable Linux services)\n\nOnce running:\n1. Connect to the desktop VM from kali\n2. Find and exploit vulnerabilities\n3. Capture flags along the way\n4. Submit flags at the FLAG STATION TERMINAL\n\nEach flag you submit will unlock rewards and progression.\n\nGood luck, and learn responsibly!",
+ "observations": "A comprehensive guide to the lab environment"
+ },
+ {
+ "type": "notes",
+ "name": "Lab Rules",
+ "takeable": true,
+ "readable": true,
+ "text": "Lab Rules & Information:\n\n- VMs are automatically destroyed after 1 hour of inactivity\n- Screenshots and evidence capture recommended\n- All traffic is logged for learning purposes\n- Contact lab administrator if VMs fail to start\n- Estimated time: 30-45 minutes\n- Difficulty: Beginner to Intermediate",
+ "observations": "Important lab operational information"
+ }
+ ]
+ },
+ "lab_workstation": {
+ "type": "room_office",
+ "connections": {
+ "south": "lab_entrance",
+ "east": "flag_room"
+ },
+ "locked": false,
+ "objects": [
+ {
+ "type": "vm-launcher",
+ "id": "vm_launcher_1",
+ "name": "Kali Console Terminal",
+ "takeable": false,
+ "observations": "A terminal interface for accessing the Kali Linux VM. Ready to launch.",
+ "hacktivityMode": <%= vm_context && vm_context['hacktivity_mode'] ? 'true' : 'false' %>,
+ "vms": <%= vm_context && vm_context['vms'] ? vm_context['vms'].to_json : '[{"id":1,"title":"kali","ip":"192.168.1.10","enable_console":true}]' %>
+ },
+ {
+ "type": "vm-launcher",
+ "id": "vm_launcher_2",
+ "name": "Desktop Lab Terminal",
+ "takeable": false,
+ "observations": "A terminal interface for accessing the Desktop Linux VM. Ready to launch.",
+ "hacktivityMode": <%= vm_context && vm_context['hacktivity_mode'] ? 'true' : 'false' %>,
+ "vms": <%= vm_context && vm_context['vms'] ? vm_context['vms'].to_json : '[{"id":2,"title":"desktop","ip":"192.168.1.20","enable_console":true}]' %>
+ },
+ {
+ "type": "notes",
+ "name": "Attack Hints",
+ "takeable": true,
+ "readable": true,
+ "text": "Attack Strategy for kali → desktop:\n\n1. FROM KALI - Reconnaissance:\n - Use 'nmap' on desktop to scan open ports\n - Example: nmap -sV
\n - Identify running services and versions\n - Research known vulnerabilities\n\n2. FROM KALI - Exploitation:\n - Look for weak credentials on desktop\n - Try common default passwords\n - Use Metasploit or manual exploitation\n - Escalate privileges if needed\n\n3. ON DESKTOP - Flag Capture:\n - Flags typically located in:\n /flag.txt (root flag)\n /home/*/flag.txt (user flags)\n /etc/shadow or /etc/passwd\n Database files (/var/lib/mysql/)\n\n4. Submit Flags:\n - Return to this game\n - Find the FLAG STATION TERMINAL\n - Enter captured flags (format: flag{...})\n - Receive points and unlock rewards",
+ "observations": "Strategic guidance for completing the lab objectives"
+ }
+ ]
+ },
+ "flag_room": {
+ "type": "room_office",
+ "connections": {
+ "west": "lab_workstation"
+ },
+ "locked": false,
+ "objects": [
+ {
+ "type": "flag-station",
+ "id": "flag_station_1",
+ "name": "Flag Submission 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"
+ }
+ }
+ },
+ {
+ "type": "notes",
+ "name": "Flag Format",
+ "takeable": true,
+ "readable": true,
+ "text": "Flag Format Information:\n\nFlags in this lab follow the standard CTF format:\n flag{...}\n\nCommon flag locations:\n - /flag.txt\n - /root/flag.txt\n - /home/user/flag.txt\n - Environment variables\n - Application data\n\nWhen submitting:\n1. Enter the complete flag text (including 'flag{}' wrapper)\n2. Click SUBMIT\n3. Wait for verification\n4. Receive confirmation and rewards\n\nYou may submit multiple flags.\nAlready-submitted flags cannot be re-submitted.",
+ "observations": "Guide on flag capture and submission"
+ },
+ {
+ "type": "notes",
+ "name": "Progress Tracker",
+ "takeable": true,
+ "readable": true,
+ "text": "Lab Progress Tracking:\n\nTotal Flags Available: Multiple (see Flag Station for exact count)\n\nObjectives:\n□ Launch Kali VM\n□ Launch Desktop VM\n□ Complete reconnaissance\n□ Identify vulnerabilities\n□ Perform exploitation\n□ Capture flags\n□ Submit all flags\n\nEstimated Points:\n- Each flag: 10-50 points (varies by difficulty)\n- Lab completion bonus: 25 points\n- Documentation bonus: 10 points\n\nYour progress is auto-saved with each flag submission.",
+ "observations": "Real-time progress tracking for lab completion"
+ }
+ ]
+ }
+ }
+}
+
diff --git a/test/dummy/log/test.log b/test/dummy/log/test.log
index 5b24ebc..b88e53f 100644
--- a/test/dummy/log/test.log
+++ b/test/dummy/log/test.log
@@ -50838,3 +50838,2518 @@ BreakEscape::GamesControllerTest: test_game_setup_has_correct_scenario_data
[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-28 16:26:18.667896"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:26:18.667838"], ["updated_at", "2025-11-28 16:26:18.667838"], ["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[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[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[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, "environment"]]
+ [1m[36mActiveRecord::SchemaMigration Load (0.0ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
+Migrating to CreateSecgenVmLabMission (20251128000002)
+ [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, "environment"]]
+ [1m[36mActiveRecord::SchemaMigration Load (0.0ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
+Migrating to CreateSecgenVmLabMission (20251128000002)
+ [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, "environment"]]
+ [1m[36mActiveRecord::SchemaMigration Load (0.0ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
+Migrating to CreateSecgenVmLabMission (20251128000002)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[36mbegin transaction[0m
+ [1m[35m (0.1ms)[0m [1m[34mSELECT COUNT(*) FROM break_escape_missions WHERE name = 'secgen_vm_lab'[0m
+ [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-28 16:48:23', '2025-11-28 16:48:23');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-28 16:48:23', '2025-11-28 16:48:23');
+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-28 16:48:23', '2025-11-28 16:48:23');
+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-28 16:48:23', '2025-11-28 16:48:23');
+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-28 16:48:23', '2025-11-28 16:48:23', '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.8ms)[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_ink_endpoint_should_require_npc_parameter
+--------------------------------------------------------------------------------
+ [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.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-28 16:48:23.085461"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.085377"], ["updated_at", "2025-11-28 16:48:23.085377"], ["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-28 16:48:23 +0000
+Processing by BreakEscape::GamesController#ink 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]]
+Completed 400 Bad Request in 3ms (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_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-28 16:48:23.097138"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.097086"], ["updated_at", "2025-11-28 16:48:23.097086"], ["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_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-28 16:48:23.098414"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.098374"], ["updated_at", "2025-11-28 16:48:23.098374"], ["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-28 16:48:23 +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 5ms (Views: 2.9ms | 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_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-28 16:48:23.106121"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.106077"], ["updated_at", "2025-11-28 16:48:23.106077"], ["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-28 16:48:23 +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_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-28 16:48:23.109021"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.108956"], ["updated_at", "2025-11-28 16:48:23.108956"], ["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-28 16:48:23 +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_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-28 16:48:23.111283"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.111242"], ["updated_at", "2025-11-28 16:48:23.111242"], ["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-28 16:48:23.111864"], ["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-28 16:48:23 +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-28 16:48:23.115356"], ["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-28 16:48:23.116700"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.116660"], ["updated_at", "2025-11-28 16:48:23.116660"], ["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-28 16:48:23 +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-28 16:48:23.118793"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.118755"], ["updated_at", "2025-11-28 16:48:23.118755"], ["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-28 16:48:23 +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_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-28 16:48:23.120868"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.120831"], ["updated_at", "2025-11-28 16:48:23.120831"], ["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-28 16:48:23 +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-28 16:48:23.123571"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.123532"], ["updated_at", "2025-11-28 16:48:23.123532"], ["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-28 16:48:23 +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_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-28 16:48:23.125816"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.125774"], ["updated_at", "2025-11-28 16:48:23.125774"], ["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-28 16:48:23 +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-28 16:48:23.129004"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.128960"], ["updated_at", "2025-11-28 16:48:23.128960"], ["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-28 16:48:23 +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-28 16:48:23.130658"], ["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::MissionsControllerTest: test_index_should_return_HTML_with_mission_list
+------------------------------------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-28 16:48:23 +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.7ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 8.9ms | GC: 2.7ms)
+Completed 200 OK in 10ms (Views: 8.8ms | ActiveRecord: 0.3ms (5 queries, 1 cached) | GC: 2.7ms)
+ [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-28 16:48:23 +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.9ms | 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_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-28 16:48:23 +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-28 16:48:23.147458"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.146776"], ["updated_at", "2025-11-28 16:48:23.146776"], ["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.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-28 16:48:23 +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.9ms | GC: 0.1ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.9ms | GC: 0.1ms)
+Completed 200 OK in 1ms (Views: 0.9ms | 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_create_game_and_redirect_when_showing_mission
+----------------------------------------------------------------------------------------------
+ [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 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-28 16:48:23 +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.1ms)[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.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", "{\"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-28 16:48:23.159614"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:48:23.158518"], ["updated_at", "2025-11-28 16:48:23.158518"], ["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 4ms (ActiveRecord: 0.4ms (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[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-28 16:49:48', '2025-11-28 16:49:48');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-28 16:49:48', '2025-11-28 16:49:48');
+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-28 16:49:48', '2025-11-28 16:49:48');
+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-28 16:49:48', '2025-11-28 16:49:48');
+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-28 16:49:48', '2025-11-28 16:49:48', '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_game_setup_has_correct_scenario_data
+---------------------------------------------------------------------------
+ [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-28 16:49:48.320534"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.320450"], ["updated_at", "2025-11-28 16:49:48.320450"], ["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_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-28 16:49:48.321984"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.321944"], ["updated_at", "2025-11-28 16:49:48.321944"], ["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-28 16:49:48 +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_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-28 16:49:48.335111"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.335068"], ["updated_at", "2025-11-28 16:49:48.335068"], ["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-28 16:49:48 +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-28 16:49:48.337407"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.337368"], ["updated_at", "2025-11-28 16:49:48.337368"], ["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-28 16:49:48 +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.7ms | GC: 0.0ms)
+Completed 200 OK in 5ms (Views: 2.9ms | 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-28 16:49:48.344684"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.344639"], ["updated_at", "2025-11-28 16:49:48.344639"], ["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-28 16:49:48 +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.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_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-28 16:49:48.347076"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.347038"], ["updated_at", "2025-11-28 16:49:48.347038"], ["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-28 16:49:48 +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_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-28 16:49:48.349280"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.349241"], ["updated_at", "2025-11-28 16:49:48.349241"], ["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-28 16:49:48 +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-28 16:49:48.351770"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.351731"], ["updated_at", "2025-11-28 16:49:48.351731"], ["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-28 16:49:48 +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-28 16:49:48.353333"], ["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_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-28 16:49:48.354684"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.354644"], ["updated_at", "2025-11-28 16:49:48.354644"], ["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-28 16:49:48.355207"], ["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-28 16:49:48 +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-28 16:49:48.356787"], ["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-28 16:49:48.358132"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.358092"], ["updated_at", "2025-11-28 16:49:48.358092"], ["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-28 16:49:48 +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.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-28 16:49:48.361350"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.361306"], ["updated_at", "2025-11-28 16:49:48.361306"], ["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-28 16:49:48 +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-28 16:49:48.363478"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.363438"], ["updated_at", "2025-11-28 16:49:48.363438"], ["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-28 16:49:48 +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::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-28 16:49:48 +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.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", "{\"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-28 16:49:48.370518"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.369637"], ["updated_at", "2025-11-28 16:49:48.369637"], ["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[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-28 16:49:48 +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-28 16:49:48.374180"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:49:48.373384"], ["updated_at", "2025-11-28 16:49:48.373384"], ["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_index_should_display_published_missions
+---------------------------------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-28 16:49:48 +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: 5.9ms | GC: 0.1ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 6.0ms | GC: 0.1ms)
+Completed 200 OK in 6ms (Views: 5.9ms | ActiveRecord: 0.3ms (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_index_should_return_HTML_with_mission_list
+------------------------------------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-28 16:49:48 +0000
+Processing by BreakEscape::MissionsController#index as HTML
+ [1m[36mBreakEscape::DemoUser Load (0.1ms)[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.2ms | GC: 0.1ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.3ms | GC: 0.1ms)
+Completed 200 OK in 2ms (Views: 1.3ms | ActiveRecord: 0.2ms (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_get_index
+----------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-28 16:49:48 +0000
+Processing by BreakEscape::MissionsController#index as HTML
+ [1m[36mBreakEscape::DemoUser Load (0.1ms)[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.1ms | GC: 0.0ms)
+Completed 200 OK in 2ms (Views: 1.1ms | ActiveRecord: 0.1ms (5 queries, 1 cached) | GC: 0.1ms)
+ [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-28 16:52:52', '2025-11-28 16:52:52');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-28 16:52:52', '2025-11-28 16:52:52');
+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-28 16:52:52', '2025-11-28 16:52:52');
+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-28 16:52:52', '2025-11-28 16:52:52');
+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-28 16:52:52', '2025-11-28 16:52:52', '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_ink_endpoint_should_require_npc_parameter
+--------------------------------------------------------------------------------
+ [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-28 16:52:52.527844"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.527757"], ["updated_at", "2025-11-28 16:52:52.527757"], ["objectives_completed", 0], ["tasks_completed", 0]]
+ [1m[36mTRANSACTION (0.1ms)[0m [1m[35mRELEASE SAVEPOINT active_record_1[0m
+Started GET "/break_escape/games/1/ink" for 127.0.0.1 at 2025-11-28 16:52:52 +0000
+Processing by BreakEscape::GamesController#ink 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]]
+Completed 400 Bad Request in 3ms (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_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-28 16:52:52.539327"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.539273"], ["updated_at", "2025-11-28 16:52:52.539273"], ["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-28 16:52:52.539962"], ["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-28 16:52:52 +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-28 16:52:52.543958"], ["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 2ms (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.1ms)[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-28 16:52:52.545477"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.545437"], ["updated_at", "2025-11-28 16:52:52.545437"], ["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-28 16:52:52 +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_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-28 16:52:52.547834"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.547788"], ["updated_at", "2025-11-28 16:52:52.547788"], ["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-28 16:52:52 +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_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-28 16:52:52.550003"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.549965"], ["updated_at", "2025-11-28 16:52:52.549965"], ["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-28 16:52:52 +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-28 16:52:52.552052"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.552012"], ["updated_at", "2025-11-28 16:52:52.552012"], ["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-28 16:52:52 +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_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-28 16:52:52.554126"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.554088"], ["updated_at", "2025-11-28 16:52:52.554088"], ["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-28 16:52:52 +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_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-28 16:52:52.556876"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.556815"], ["updated_at", "2025-11-28 16:52:52.556815"], ["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-28 16:52:52 +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.7ms | GC: 0.0ms)
+Completed 200 OK in 5ms (Views: 2.9ms | 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_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-28 16:52:52.564012"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.563970"], ["updated_at", "2025-11-28 16:52:52.563970"], ["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-28 16:52:52 +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_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-28 16:52:52.566559"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.566520"], ["updated_at", "2025-11-28 16:52:52.566520"], ["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-28 16:52:52 +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-28 16:52:52.569471"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.569431"], ["updated_at", "2025-11-28 16:52:52.569431"], ["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-28 16:52:52 +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-28 16:52:52.571122"], ["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_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-28 16:52:52.572565"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.572525"], ["updated_at", "2025-11-28 16:52:52.572525"], ["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::MissionsControllerTest: test_should_get_index
+----------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-28 16:52:52 +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.3ms | GC: 0.1ms)
+ 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.8ms | GC: 2.8ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 8.8ms | GC: 2.8ms)
+Completed 200 OK in 10ms (Views: 8.7ms | ActiveRecord: 0.3ms (5 queries, 1 cached) | GC: 2.8ms)
+ [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-28 16:52:52 +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-28 16:52:52.586192"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.585546"], ["updated_at", "2025-11-28 16:52:52.585546"], ["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.2ms)
+ [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-28 16:52:52 +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-28 16:52:52.589408"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:52:52.588746"], ["updated_at", "2025-11-28 16:52:52.588746"], ["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.0ms)
+ [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-28 16:52:52 +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.1ms | 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-28 16:52:52 +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.9ms | 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: 0.9ms | 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[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-28 16:54:55', '2025-11-28 16:54:55');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-28 16:54:55', '2025-11-28 16:54:55');
+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-28 16:54:55', '2025-11-28 16:54:55');
+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-28 16:54:55', '2025-11-28 16:54:55');
+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-28 16:54:55', '2025-11-28 16:54:55', '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_scenario_endpoint_should_return_JSON
+---------------------------------------------------------------------------
+ [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-28 16:54:55.733377"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.733294"], ["updated_at", "2025-11-28 16:54:55.733294"], ["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-28 16:54:55 +0000
+Processing by BreakEscape::GamesController#scenario 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.1ms)[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 5ms (Views: 0.1ms | ActiveRecord: 0.2ms (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.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-28 16:54:55.749126"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.749050"], ["updated_at", "2025-11-28 16:54:55.749050"], ["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-28 16:54:55 +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: 2.3ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 2.5ms | GC: 0.0ms)
+Completed 200 OK in 9ms (Views: 4.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_show_should_return_HTML_with_game_container
+----------------------------------------------------------------------------------
+ [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-28 16:54:55.761618"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.761569"], ["updated_at", "2025-11-28 16:54:55.761569"], ["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-28 16:54:55 +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_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-28 16:54:55.765004"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.764961"], ["updated_at", "2025-11-28 16:54:55.764961"], ["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_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-28 16:54:55.766134"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.766096"], ["updated_at", "2025-11-28 16:54:55.766096"], ["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-28 16:54:55 +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_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.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-28 16:54:55.770101"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.770061"], ["updated_at", "2025-11-28 16:54:55.770061"], ["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-28 16:54:55 +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_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.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-28 16:54:55.772655"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.772604"], ["updated_at", "2025-11-28 16:54:55.772604"], ["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-28 16:54:55 +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_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.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-28 16:54:55.775743"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.775682"], ["updated_at", "2025-11-28 16:54:55.775682"], ["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-28 16:54:55 +0000
+Processing by BreakEscape::GamesController#ink 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]]
+Completed 400 Bad Request 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_should_show_game
+-------------------------------------------------------
+ [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-28 16:54:55.779637"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.779560"], ["updated_at", "2025-11-28 16:54:55.779560"], ["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-28 16:54:55 +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.2ms | 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.4ms | 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-28 16:54:55.783773"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.783723"], ["updated_at", "2025-11-28 16:54:55.783723"], ["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-28 16:54:55.784424"], ["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-28 16:54:55 +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-28 16:54:55.786396"], ["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_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-28 16:54:55.787796"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.787755"], ["updated_at", "2025-11-28 16:54:55.787755"], ["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-28 16:54:55 +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-28 16:54:55.789536"], ["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-28 16:54:55.790954"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.790913"], ["updated_at", "2025-11-28 16:54:55.790913"], ["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-28 16:54:55 +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::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-28 16:54:55 +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-28 16:54:55.796243"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.795563"], ["updated_at", "2025-11-28 16:54:55.795563"], ["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.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-28 16:54:55 +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.3ms | GC: 0.1ms)
+ 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: 6.6ms | GC: 0.5ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 6.6ms | GC: 0.5ms)
+Completed 200 OK in 7ms (Views: 6.5ms | ActiveRecord: 0.3ms (5 queries, 1 cached) | GC: 0.5ms)
+ [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_get_index
+----------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-28 16:54:55 +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.4ms | GC: 0.1ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.4ms | GC: 0.1ms)
+Completed 200 OK in 2ms (Views: 1.5ms | ActiveRecord: 0.2ms (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-28 16:54:55 +0000
+Processing by BreakEscape::MissionsController#show as HTML
+ Parameters: {"id"=>"418560898"}
+ [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" 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-28 16:54:55.816852"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 16:54:55.816115"], ["updated_at", "2025-11-28 16:54:55.816115"], ["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.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_index_should_return_HTML_with_mission_list
+------------------------------------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-28 16:54:55 +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.9ms | 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: 0.9ms | ActiveRecord: 0.1ms (5 queries, 1 cached) | GC: 0.1ms)
+ [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-28 17:17:22', '2025-11-28 17:17:22');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-28 17:17:22', '2025-11-28 17:17:22');
+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-28 17:17:22', '2025-11-28 17:17:22');
+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-28 17:17:22', '2025-11-28 17:17:22');
+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-28 17:17:22', '2025-11-28 17:17:22', '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_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-28 17:17:22.452407"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.452313"], ["updated_at", "2025-11-28 17:17:22.452313"], ["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-28 17:17:22.453314"], ["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-28 17:17:22 +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-28 17:17:22.464858"], ["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 4ms (Views: 0.1ms | 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-28 17:17:22.467228"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.467185"], ["updated_at", "2025-11-28 17:17:22.467185"], ["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-28 17:17:22 +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_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-28 17:17:22.469663"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.469623"], ["updated_at", "2025-11-28 17:17:22.469623"], ["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-28 17:17:22 +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_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-28 17:17:22.472437"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.472397"], ["updated_at", "2025-11-28 17:17:22.472397"], ["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-28 17:17:22 +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-28 17:17:22.474080"], ["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_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-28 17:17:22.475335"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.475298"], ["updated_at", "2025-11-28 17:17:22.475298"], ["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-28 17:17:22 +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 5ms (Views: 3.0ms | 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_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-28 17:17:22.483017"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.482972"], ["updated_at", "2025-11-28 17:17:22.482972"], ["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-28 17:17:22 +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_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-28 17:17:22.486034"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.485994"], ["updated_at", "2025-11-28 17:17:22.485994"], ["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-28 17:17:22 +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-28 17:17:22.488311"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.488271"], ["updated_at", "2025-11-28 17:17:22.488271"], ["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-28 17:17:22 +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_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-28 17:17:22.490464"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.490426"], ["updated_at", "2025-11-28 17:17:22.490426"], ["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-28 17:17:22 +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_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-28 17:17:22.492535"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.492499"], ["updated_at", "2025-11-28 17:17:22.492499"], ["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-28 17:17:22 +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-28 17:17:22.494551"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.494513"], ["updated_at", "2025-11-28 17:17:22.494513"], ["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-28 17:17:22 +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_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-28 17:17:22.497168"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.497129"], ["updated_at", "2025-11-28 17:17:22.497129"], ["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::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-28 17:17:22 +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-28 17:17:22.503662"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.502940"], ["updated_at", "2025-11-28 17:17:22.502940"], ["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 5ms (ActiveRecord: 0.3ms (4 queries, 0 cached) | GC: 2.7ms)
+ [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-28 17:17:22 +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.1ms)[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: 5.9ms | GC: 0.2ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 6.1ms | GC: 0.3ms)
+Completed 200 OK in 7ms (Views: 5.9ms | ActiveRecord: 0.3ms (5 queries, 1 cached) | GC: 0.3ms)
+ [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-28 17:17:22 +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.1ms)
+Completed 200 OK in 1ms (Views: 0.9ms | 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-28 17:17:22 +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: 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[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-28 17:17:22 +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.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", "{\"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-28 17:17:22.522635"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:17:22.521563"], ["updated_at", "2025-11-28 17:17:22.521563"], ["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 4ms (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-28 17:20:23', '2025-11-28 17:20:23');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-28 17:20:23', '2025-11-28 17:20:23');
+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-28 17:20:23', '2025-11-28 17:20:23');
+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-28 17:20:23', '2025-11-28 17:20:23');
+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-28 17:20:23', '2025-11-28 17:20:23', '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_should_show_game
+-------------------------------------------------------
+ [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-28 17:20:23.846057"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.845959"], ["updated_at", "2025-11-28 17:20:23.845959"], ["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-28 17:20:23 +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.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 8ms (Views: 3.0ms | 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_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-28 17:20:23.863123"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.863046"], ["updated_at", "2025-11-28 17:20:23.863046"], ["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-28 17:20:23 +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.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_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-28 17:20:23.865804"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.865762"], ["updated_at", "2025-11-28 17:20:23.865762"], ["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-28 17:20:23 +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_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-28 17:20:23.868033"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.867994"], ["updated_at", "2025-11-28 17:20:23.867994"], ["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-28 17:20:23 +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_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-28 17:20:23.870333"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.870294"], ["updated_at", "2025-11-28 17:20:23.870294"], ["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-28 17:20:23 +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_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-28 17:20:23.872557"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.872520"], ["updated_at", "2025-11-28 17:20:23.872520"], ["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-28 17:20:23 +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-28 17:20:23.875692"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.875655"], ["updated_at", "2025-11-28 17:20:23.875655"], ["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-28 17:20:23 +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-28 17:20:23.879283"], ["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_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-28 17:20:23.880765"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.880720"], ["updated_at", "2025-11-28 17:20:23.880720"], ["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-28 17:20:23 +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_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-28 17:20:23.883705"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.883661"], ["updated_at", "2025-11-28 17:20:23.883661"], ["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-28 17:20:23 +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_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-28 17:20:23.886515"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.886474"], ["updated_at", "2025-11-28 17:20:23.886474"], ["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-28 17:20:23 +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_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-28 17:20:23.888730"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.888693"], ["updated_at", "2025-11-28 17:20:23.888693"], ["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-28 17:20:23.889234"], ["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-28 17:20:23 +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-28 17:20:23.890751"], ["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_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-28 17:20:23.892037"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.892001"], ["updated_at", "2025-11-28 17:20:23.892001"], ["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::MissionsControllerTest: test_should_get_index
+----------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-28 17:20:23 +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.4ms | GC: 0.2ms)
+ 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: 9.2ms | GC: 3.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 9.3ms | GC: 3.0ms)
+Completed 200 OK in 10ms (Views: 9.1ms | ActiveRecord: 0.3ms (5 queries, 1 cached) | GC: 3.0ms)
+ [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-28 17:20:23 +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.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", "{\"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-28 17:20:23.906446"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.905778"], ["updated_at", "2025-11-28 17:20:23.905778"], ["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[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-28 17:20:23 +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.9ms | 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 1ms (Views: 1.0ms | ActiveRecord: 0.1ms (5 queries, 1 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_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-28 17:20:23 +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-28 17:20:23.912128"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:20:23.911501"], ["updated_at", "2025-11-28 17:20:23.911501"], ["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[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-28 17:20:23 +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.9ms | 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[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-28 17:25:47', '2025-11-28 17:25:47');
+INSERT INTO "break_escape_demo_users" ("id", "handle", "created_at", "updated_at") VALUES (618102942, 'other_user', '2025-11-28 17:25:47', '2025-11-28 17:25:47');
+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-28 17:25:47', '2025-11-28 17:25:47');
+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-28 17:25:47', '2025-11-28 17:25:47');
+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-28 17:25:47', '2025-11-28 17:25:47', '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_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-28 17:25:47.154361"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.154275"], ["updated_at", "2025-11-28 17:25:47.154275"], ["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-28 17:25:47 +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.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_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-28 17:25:47.168361"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.168309"], ["updated_at", "2025-11-28 17:25:47.168309"], ["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-28 17:25:47.169640"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.169599"], ["updated_at", "2025-11-28 17:25:47.169599"], ["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-28 17:25:47 +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-28 17:25:47.171990"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.171952"], ["updated_at", "2025-11-28 17:25:47.171952"], ["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-28 17:25:47 +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-28 17:25:47.173694"], ["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-28 17:25:47.175148"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.175110"], ["updated_at", "2025-11-28 17:25:47.175110"], ["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-28 17:25:47 +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.7ms | GC: 0.0ms)
+Completed 200 OK in 5ms (Views: 2.9ms | 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-28 17:25:47.182677"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.182630"], ["updated_at", "2025-11-28 17:25:47.182630"], ["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-28 17:25:47 +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_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-28 17:25:47.185095"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.185054"], ["updated_at", "2025-11-28 17:25:47.185054"], ["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-28 17:25:47 +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-28 17:25:47.187298"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.187253"], ["updated_at", "2025-11-28 17:25:47.187253"], ["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-28 17:25:47 +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_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-28 17:25:47.189375"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.189337"], ["updated_at", "2025-11-28 17:25:47.189337"], ["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-28 17:25:47 +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_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-28 17:25:47.192575"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.192533"], ["updated_at", "2025-11-28 17:25:47.192533"], ["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-28 17:25:47.193121"], ["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-28 17:25:47 +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-28 17:25:47.194853"], ["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-28 17:25:47.196271"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.196231"], ["updated_at", "2025-11-28 17:25:47.196231"], ["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-28 17:25:47 +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-28 17:25:47.198506"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.198468"], ["updated_at", "2025-11-28 17:25:47.198468"], ["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-28 17:25:47 +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_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-28 17:25:47 +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.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", "{\"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-28 17:25:47.206542"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.203241"], ["updated_at", "2025-11-28 17:25:47.203241"], ["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 5ms (ActiveRecord: 0.3ms (4 queries, 0 cached) | GC: 2.6ms)
+ [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-28 17:25:47 +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: 6.3ms | GC: 0.3ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 6.4ms | GC: 0.3ms)
+Completed 200 OK in 7ms (Views: 6.3ms | ActiveRecord: 0.3ms (5 queries, 1 cached) | GC: 0.3ms)
+ [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-28 17:25:47 +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.1ms)[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", "{\"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-28 17:25:47.218534"], ["completed_at", nil], ["score", 0], ["created_at", "2025-11-28 17:25:47.217906"], ["updated_at", "2025-11-28 17:25:47.217906"], ["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.4ms (4 queries, 0 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-28 17:25:47 +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.9ms | GC: 0.1ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 0.9ms | GC: 0.1ms)
+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_get_index
+----------------------------------------------------------
+Started GET "/break_escape/missions" for 127.0.0.1 at 2025-11-28 17:25:47 +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.3ms | GC: 0.0ms)
+ Rendered layout /home/cliffe/Files/Projects/Code/BreakEscape/BreakEscape/app/views/layouts/break_escape/application.html.erb (Duration: 1.4ms | GC: 0.0ms)
+Completed 200 OK in 2ms (Views: 1.4ms | ActiveRecord: 0.2ms (5 queries, 1 cached) | GC: 0.1ms)
+ [1m[36mTRANSACTION (0.0ms)[0m [1m[31mrollback transaction[0m
diff --git a/test/dummy/storage/test.sqlite3 b/test/dummy/storage/test.sqlite3
index c107f67..3b32bcf 100644
Binary files a/test/dummy/storage/test.sqlite3 and b/test/dummy/storage/test.sqlite3 differ
diff --git a/test/fixtures/break_escape_missions.yml b/test/fixtures/break_escape_missions.yml
index ffd7fb0..864153f 100644
--- a/test/fixtures/break_escape_missions.yml
+++ b/test/fixtures/break_escape_missions.yml
@@ -15,3 +15,14 @@ unpublished:
difficulty_level: 1
created_at: <%= Time.now %>
updated_at: <%= Time.now %>
+
+secgen_vm_lab:
+ name: secgen_vm_lab
+ display_name: SecGen VM Lab - Linux Introduction
+ description: Test VM and flag integration with SecGen scenario
+ published: true
+ difficulty_level: 2
+ secgen_scenario: labs/introducing_attacks/1_intro_linux.xml
+ collection: vm_labs
+ created_at: <%= Time.now %>
+ updated_at: <%= Time.now %>