aboutsummaryrefslogtreecommitdiff
path: root/tests/integration/test_client_hub_messaging.py
blob: f5765cf16abaec25053982eac0714ba26f38bf4e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
"""
Integration tests for client-hub messaging using Selenium.
Tests that client doesn't receive its own Join message back.
"""
import pytest
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

pytestmark = [pytest.mark.nondestructive]


@pytest.mark.integration
def test_client_does_not_receive_own_join_message(ready_app, wait):
    """
    Test that when a client sends a Join message to the hub,
    it doesn't receive it back through its own message handler.
    
    This test:
    1. Clicks "Host" to create a hub and connect host client
    2. Injects JS to monitor if the host client receives a Join message
    3. Verifies the host client does NOT receive its own Join message
    """
    driver = ready_app
    
    # Inject monitoring script before clicking Host
    driver.execute_script("""
        // Track if client receives Join message
        window.clientReceivedOwnJoin = false;
        
        // Monkey-patch Client.handle_message to detect Join messages
        (function() {
            // Wait for modules to load
            var checkInterval = setInterval(function() {
                try {
                    var clientModule = require('client');
                    var Client = clientModule.Client;
                    
                    if (Client && Client.prototype && Client.prototype.handle_message) {
                        var originalHandleMessage = Client.prototype.handle_message;
                        
                        Client.prototype.handle_message = function(callback_id, message_data) {
                            // Check if this is a Join message
                            if (message_data && Array.isArray(message_data) && message_data[0] === 'Join') {
                                console.log('[TEST] Client received Join message!');
                                window.clientReceivedOwnJoin = true;
                            }
                            
                            // Call original method
                            return originalHandleMessage.call(this, callback_id, message_data);
                        };
                        
                        console.log('[TEST] Successfully patched Client.handle_message');
                        clearInterval(checkInterval);
                    }
                } catch (e) {
                    // Modules not loaded yet, keep trying
                }
            }, 100);
            
            // Give up after 10 seconds
            setTimeout(function() {
                clearInterval(checkInterval);
            }, 10000);
        })();
    """)
    
    # Wait for Host button to be available
    time.sleep(1)
    
    # Click Host button
    try:
        # Try to find and click Host button via JavaScript
        clicked = driver.execute_script("""
            // Look for Host button in the UI
            var buttons = document.querySelectorAll('canvas');
            if (buttons.length > 0) {
                // Simulate click on canvas where Host button would be
                var canvas = buttons[0];
                var event = new MouseEvent('click', {
                    view: window,
                    bubbles: true,
                    cancelable: true,
                    clientX: canvas.width / 2,
                    clientY: canvas.height / 2 - 64  // Approximate position of Host button
                });
                canvas.dispatchEvent(event);
                return true;
            }
            return false;
        """)
        
        if not clicked:
            # Fallback: Try clicking the canvas directly
            canvas = driver.find_element(By.ID, "canvas")
            canvas.click()
    except Exception as e:
        pytest.fail(f"Failed to click Host button: {e}")
    
    # Wait for connection to establish
    time.sleep(3)
    
    # Check if client received its own Join message
    received_own_join = driver.execute_script("return window.clientReceivedOwnJoin || false;")
    
    # Get console logs for debugging
    logs = driver.get_log("browser")
    test_logs = [log['message'] for log in logs if '[TEST]' in log.get('message', '')]
    
    print(f"\n=== Test Debug Info ===")
    print(f"Client received own Join: {received_own_join}")
    print(f"Test logs: {test_logs}")
    
    # Assert client did NOT receive its own Join message
    assert not received_own_join, "Client should not receive its own Join message back from hub"


@pytest.mark.integration
def test_hub_receives_join_message(ready_app, wait):
    """
    Test that the hub correctly receives the Join message from a connecting client.
    
    This test:
    1. Clicks "Host" to create a hub
    2. Monitors hub.handle_message for Join messages
    3. Verifies the hub receives the Join message
    """
    driver = ready_app
    
    # Inject monitoring script
    driver.execute_script("""
        // Track if hub receives Join message
        window.hubReceivedJoin = false;
        window.joinMessageData = null;
        
        // Monkey-patch Hub.handle_message to detect Join messages
        (function() {
            var checkInterval = setInterval(function() {
                try {
                    var hubModule = require('hub');
                    var Hub = hubModule.Hub;
                    
                    if (Hub && Hub.prototype && Hub.prototype.handle_message) {
                        var originalHandleMessage = Hub.prototype.handle_message;
                        
                        Hub.prototype.handle_message = function(from_client, msgname, data) {
                            // Normalise arguments: depending on the Lua/JS bridge,
                            // the message array may arrive as `msgname` or `data`,
                            // and may be 0- or 1-indexed from JS.
                            var message = null;
                            if (Array.isArray(msgname)) {
                                message = msgname;
                            } else if (Array.isArray(data)) {
                                message = data;
                            }
                            var msg_type = null;
                            var msg_data = null;
                            if (message) {
                                msg_type = message[0] || message[1] || null;
                                msg_data = message[1] || message[2] || null;
                            }
                            if (msg_type === 'Join') {
                                console.log('[TEST] Hub received Join message from:', from_client);
                                window.hubReceivedJoin = true;
                                window.joinMessageData = msg_data;
                            }
                            
                            // Call original method
                            return originalHandleMessage.call(this, from_client, msgname, data);
                        };
                        
                        console.log('[TEST] Successfully patched Hub.handle_message');
                        clearInterval(checkInterval);
                    }
                } catch (e) {
                    // Modules not loaded yet, keep trying
                }
            }, 100);
            
            setTimeout(function() {
                clearInterval(checkInterval);
            }, 10000);
        })();
    """)
    
    # Wait for modules to load
    time.sleep(1)
    
    # Click Host button (same approach as previous test)
    try:
        clicked = driver.execute_script("""
            var canvas = document.getElementById('canvas');
            if (canvas) {
                var event = new MouseEvent('click', {
                    view: window,
                    bubbles: true,
                    cancelable: true,
                    clientX: canvas.width / 2,
                    clientY: canvas.height / 2 - 64
                });
                canvas.dispatchEvent(event);
                return true;
            }
            return false;
        """)
        
        if not clicked:
            canvas = driver.find_element(By.ID, "canvas")
            canvas.click()
    except Exception as e:
        pytest.fail(f"Failed to click Host button: {e}")
    
    # Wait for connection and message processing
    time.sleep(3)
    
    # Debug world/hub state before assertions
    world_debug = driver.execute_script("""
        try {
            var world = typeof require === 'function' ? require('world') : null;
            return {
                hasWorld: !!world,
                hasHub: !!(world && world.hub),
                hasNetwork: !!(world && world.network)
            };
        } catch (e) {
            return { error: String(e) };
        }
    """)
    print(f"World debug: {world_debug}")
    js_flags = driver.execute_script("""
        return {
            clientConnected: !!window._clientConnectedToHub,
            clientJoinPayload: window._clientJoinPayload || null,
            hubJoinReceived: !!window._hubJoinReceived,
            hubJoinData: window._hubJoinData || null
        };
    """)
    print(f"JS flags: {js_flags}")
    
    # Check if hub received Join message (Lua side exposes this via js_bridge)
    hub_received_join = driver.execute_script("return (window.hubReceivedJoin || window._hubJoinReceived) || false;")
    join_data = driver.execute_script("return window.joinMessageData || window._hubJoinData || null;")
    
    # Get console logs for debugging
    logs = driver.get_log("browser")
    test_logs = [log['message'] for log in logs if '[TEST]' in log.get('message', '')]
    
    print(f"\n=== Test Debug Info ===")
    print(f"Hub received Join: {hub_received_join}")
    print(f"Join data: {join_data}")
    print(f"Test logs: {test_logs}")
    
    # Assert hub received the Join message
    assert hub_received_join, "Hub should receive Join message from connecting client"
    assert join_data is not None, "Join message should contain data"
    # Check that join data has a 'name' field
    if join_data:
        assert 'name' in join_data, "Join message data should contain 'name' field"


@pytest.mark.integration  
def test_message_flow_integrity(ready_app, wait):
    """
    End-to-end test of message flow: client sends, hub receives, but client doesn't get echo.
    
    This combines both previous tests to verify the complete message flow.
    """
    driver = ready_app
    
    # Inject comprehensive monitoring
    driver.execute_script("""
        window.testResults = {
            clientReceivedJoin: false,
            hubReceivedJoin: false,
            joinData: null
        };
        
        (function() {
            var checkInterval = setInterval(function() {
                try {
                    // Patch Client
                    var clientModule = require('client');
                    var hubModule = require('hub');
                    
                    if (clientModule && clientModule.Client && 
                        hubModule && hubModule.Hub) {
                        
                        var Client = clientModule.Client;
                        var Hub = hubModule.Hub;
                        
                        // Patch client
                        if (Client.prototype.handle_message) {
                            var origClientHandle = Client.prototype.handle_message;
                            Client.prototype.handle_message = function(cid, mdata) {
                                if (mdata && mdata[0] === 'Join') {
                                    console.log('[TEST] CLIENT RECEIVED JOIN - THIS IS THE BUG!');
                                    window.testResults.clientReceivedJoin = true;
                                }
                                return origClientHandle.call(this, cid, mdata);
                            };
                        }
                        
                        // Patch hub
                        if (Hub.prototype.handle_message) {
                            var origHubHandle = Hub.prototype.handle_message;
                            Hub.prototype.handle_message = function(from, msgname, data) {
                                // Normalise message array from Lua/JS bridge.
                                var message = null;
                                if (Array.isArray(msgname)) {
                                    message = msgname;
                                } else if (Array.isArray(data)) {
                                    message = data;
                                }
                                var msg_type = null;
                                var msg_data = null;
                                if (message) {
                                    msg_type = message[0] || message[1] || null;
                                    msg_data = message[1] || message[2] || null;
                                }
                                if (msg_type === 'Join') {
                                    console.log('[TEST] Hub received Join - CORRECT!');
                                    window.testResults.hubReceivedJoin = true;
                                    window.testResults.joinData = msg_data;
                                }
                                return origHubHandle.call(this, from, msgname, data);
                            };
                        }
                        
                        console.log('[TEST] Patching complete');
                        clearInterval(checkInterval);
                    }
                } catch (e) {
                    // Keep trying
                }
            }, 100);
            
            setTimeout(function() { clearInterval(checkInterval); }, 10000);
        })();
    """)
    
    time.sleep(1)
    
    # Click Host
    canvas = driver.find_element(By.ID, "canvas")
    canvas.click()
    
    # Wait for messages to flow
    time.sleep(3)
    
    # Get results, augmenting from Lua-side hub instrumentation if present
    results = driver.execute_script("""
        if (typeof window.testResults === 'undefined') {
            window.testResults = {
                clientReceivedJoin: false,
                hubReceivedJoin: false,
                joinData: null
            };
        }
        if (window._hubJoinReceived) {
            window.testResults.hubReceivedJoin = true;
        }
        if (window._hubJoinData) {
            window.testResults.joinData = window._hubJoinData;
        }
        return window.testResults;
    """)
    
    print(f"\n=== Complete Test Results ===")
    print(f"Results: {results}")
    
    # Assertions
    assert results['hubReceivedJoin'], "Hub must receive Join message"
    assert not results['clientReceivedJoin'], "Client must NOT receive its own Join message (bug fixed!)"
    assert results['joinData'] is not None, "Join message must have data"