Files
docker-swarm-termina/backend/tests/test_websocket_simulated.py
Claude 78f67d9483 Add comprehensive WebSocket tests with simulated containers
Added three levels of testing:
1. Unit tests for WebSocket handlers (test_websocket.py)
2. Simulated container tests that work without Docker (test_websocket_simulated.py)
3. Real integration tests that require Docker (test_websocket_integration.py)

New features:
- SimulatedContainer, SimulatedSocket, and SimulatedExecInstance classes
- Simulates Docker exec socket behavior including _sock attribute
- 16 new tests covering socket operations, Unicode, control chars, etc
- Pytest markers for unit vs integration tests
- Auto-skip integration tests when Docker unavailable
- Updated test documentation

Test results:
- 54 tests passing, 2 skipped (integration tests)
- Coverage: 71% (exceeds 70% threshold)

https://claude.ai/code/session_01B9dpKXH8wbD7MPtPBDHrjq
2026-01-31 01:22:48 +00:00

166 lines
5.8 KiB
Python

"""
Integration-style tests using simulated Docker containers.
These tests verify the WebSocket terminal logic without requiring real Docker.
"""
import pytest
from unittest.mock import Mock, patch
pytestmark = pytest.mark.unit
class TestWebSocketWithSimulatedContainer:
"""Test WebSocket handlers with simulated Docker containers"""
def test_sendall_with_simulated_socket_wrapper(self, simulated_container):
"""Test sendall works correctly with simulated Docker socket wrapper"""
# Get an exec instance from simulated container
exec_instance = simulated_container.exec_run(['/bin/sh'], socket=True)
# Get the socket (which has _sock attribute like real Docker sockets)
sock = exec_instance.output
# Verify it has _sock attribute
assert hasattr(sock, '_sock'), "Simulated socket should have _sock attribute"
# Test the sendall logic from handle_input
input_data = 'echo "test"\n'
if hasattr(sock, '_sock'):
sock._sock.sendall(input_data.encode('utf-8'))
else:
sock.sendall(input_data.encode('utf-8'))
# Verify sendall was called on the underlying socket
sock._sock.sendall.assert_called_once_with(b'echo "test"\n')
def test_simulated_exec_recv(self, simulated_container):
"""Test receiving data from simulated exec socket"""
exec_instance = simulated_container.exec_run(['/bin/sh'], socket=True)
sock = exec_instance.output
# Read data
data = sock.recv(4096)
# Should get simulated response
assert data is not None
assert len(data) > 0
assert b'test' in data
def test_simulated_socket_lifecycle(self, simulated_container):
"""Test simulated socket open/close lifecycle"""
exec_instance = simulated_container.exec_run(['/bin/sh'], socket=True)
sock = exec_instance.output
# Socket should be open
assert not sock.closed
# Should be able to receive data
data = sock.recv(1024)
assert data is not None
# Close socket
sock.close()
assert sock.closed
# After close, should return empty
data = sock.recv(1024)
assert data == b''
def test_handle_input_logic_with_simulated_container(self, simulated_container):
"""Test handle_input logic with simulated container"""
# This test verifies the core logic without calling the actual handler
# (which requires Flask request context)
# Create exec instance
exec_instance = simulated_container.exec_run(['/bin/sh'], socket=True)
# Simulate the logic from handle_input
input_data = 'ls -la\n'
sock = exec_instance.output
# This is the actual logic from handle_input
if hasattr(sock, '_sock'):
sock._sock.sendall(input_data.encode('utf-8'))
else:
sock.sendall(input_data.encode('utf-8'))
# Verify sendall was called on the underlying socket
exec_instance.output._sock.sendall.assert_called_once_with(b'ls -la\n')
def test_multiple_commands_simulated(self, simulated_container):
"""Test sending multiple commands to simulated container"""
exec_instance = simulated_container.exec_run(['/bin/sh'], socket=True)
sock = exec_instance.output
commands = ['ls\n', 'pwd\n', 'echo hello\n']
for cmd in commands:
if hasattr(sock, '_sock'):
sock._sock.sendall(cmd.encode('utf-8'))
else:
sock.sendall(cmd.encode('utf-8'))
# Verify all commands were sent
assert sock._sock.sendall.call_count == len(commands)
# Verify the calls
calls = sock._sock.sendall.call_args_list
for i, cmd in enumerate(commands):
assert calls[i][0][0] == cmd.encode('utf-8')
def test_unicode_handling_simulated(self, simulated_container):
"""Test Unicode handling with simulated container"""
exec_instance = simulated_container.exec_run(['/bin/sh'], socket=True)
sock = exec_instance.output
# Send Unicode
unicode_text = 'echo "Hello 世界 🚀"\n'
if hasattr(sock, '_sock'):
sock._sock.sendall(unicode_text.encode('utf-8'))
else:
sock.sendall(unicode_text.encode('utf-8'))
# Verify it was encoded and sent correctly
sock._sock.sendall.assert_called_once()
sent_data = sock._sock.sendall.call_args[0][0]
# Should be valid UTF-8
decoded = sent_data.decode('utf-8')
assert '世界' in decoded
assert '🚀' in decoded
def test_empty_input_simulated(self, simulated_container):
"""Test handling empty input with simulated container"""
exec_instance = simulated_container.exec_run(['/bin/sh'], socket=True)
sock = exec_instance.output
# Send empty string
empty_input = ''
if hasattr(sock, '_sock'):
sock._sock.sendall(empty_input.encode('utf-8'))
else:
sock.sendall(empty_input.encode('utf-8'))
# Should still work, just send empty bytes
sock._sock.sendall.assert_called_once_with(b'')
def test_binary_data_simulated(self, simulated_container):
"""Test handling binary/control characters with simulated container"""
exec_instance = simulated_container.exec_run(['/bin/sh'], socket=True)
sock = exec_instance.output
# Send control characters (Ctrl+C, Ctrl+D, etc.)
control_chars = '\x03\x04' # Ctrl+C, Ctrl+D
if hasattr(sock, '_sock'):
sock._sock.sendall(control_chars.encode('utf-8'))
else:
sock.sendall(control_chars.encode('utf-8'))
# Should handle control characters
sock._sock.sendall.assert_called_once()
assert sock._sock.sendall.call_args[0][0] == b'\x03\x04'