292 lines
7.9 KiB
Rust
292 lines
7.9 KiB
Rust
|
|
// Copyright (c) 2024 Jason Witty <jasonpwitty+socktop@proton.me>.
|
||
|
|
// All rights reserved.
|
||
|
|
//
|
||
|
|
// Integration tests for configuration and constants
|
||
|
|
|
||
|
|
#![allow(clippy::assertions_on_constants)]
|
||
|
|
|
||
|
|
use std::time::Duration;
|
||
|
|
|
||
|
|
// Test that reasonable timeout values are used
|
||
|
|
#[test]
|
||
|
|
fn test_heartbeat_interval_reasonable() {
|
||
|
|
// Heartbeat should be frequent enough to catch disconnects quickly
|
||
|
|
// but not so frequent it creates unnecessary traffic
|
||
|
|
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
HEARTBEAT_INTERVAL.as_secs() >= 1,
|
||
|
|
"Heartbeat interval too short"
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
HEARTBEAT_INTERVAL.as_secs() <= 30,
|
||
|
|
"Heartbeat interval too long"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_client_timeout_reasonable() {
|
||
|
|
// Client timeout should be longer than heartbeat interval
|
||
|
|
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
||
|
|
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
CLIENT_TIMEOUT > HEARTBEAT_INTERVAL,
|
||
|
|
"Client timeout must be longer than heartbeat interval"
|
||
|
|
);
|
||
|
|
assert!(CLIENT_TIMEOUT.as_secs() >= 5, "Client timeout too short");
|
||
|
|
assert!(CLIENT_TIMEOUT.as_secs() <= 60, "Client timeout too long");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_idle_timeout_reasonable() {
|
||
|
|
// Idle timeout should be long enough for legitimate use
|
||
|
|
const IDLE_TIMEOUT: Duration = Duration::from_secs(300);
|
||
|
|
|
||
|
|
assert!(IDLE_TIMEOUT.as_secs() >= 60, "Idle timeout too short");
|
||
|
|
assert!(IDLE_TIMEOUT.as_secs() <= 3600, "Idle timeout too long");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_idle_check_interval_reasonable() {
|
||
|
|
// Idle check should be frequent enough to be responsive
|
||
|
|
// but not so frequent it wastes resources
|
||
|
|
const IDLE_CHECK_INTERVAL: Duration = Duration::from_secs(30);
|
||
|
|
const IDLE_TIMEOUT: Duration = Duration::from_secs(300);
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
IDLE_CHECK_INTERVAL < IDLE_TIMEOUT,
|
||
|
|
"Idle check interval must be less than idle timeout"
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
IDLE_CHECK_INTERVAL.as_secs() >= 10,
|
||
|
|
"Idle check too frequent"
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
IDLE_CHECK_INTERVAL.as_secs() <= 120,
|
||
|
|
"Idle check too infrequent"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_timeout_relationships() {
|
||
|
|
// Verify the logical relationship between different timeouts
|
||
|
|
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
||
|
|
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
|
||
|
|
const IDLE_CHECK_INTERVAL: Duration = Duration::from_secs(30);
|
||
|
|
const IDLE_TIMEOUT: Duration = Duration::from_secs(300);
|
||
|
|
|
||
|
|
// Client timeout should be at least 2x heartbeat interval
|
||
|
|
assert!(
|
||
|
|
CLIENT_TIMEOUT >= HEARTBEAT_INTERVAL * 2,
|
||
|
|
"Client timeout should be at least 2x heartbeat interval"
|
||
|
|
);
|
||
|
|
|
||
|
|
// Idle timeout should be much longer than client timeout
|
||
|
|
assert!(
|
||
|
|
IDLE_TIMEOUT > CLIENT_TIMEOUT * 10,
|
||
|
|
"Idle timeout should be significantly longer than client timeout"
|
||
|
|
);
|
||
|
|
|
||
|
|
// Idle check should be less than idle timeout
|
||
|
|
assert!(
|
||
|
|
IDLE_CHECK_INTERVAL < IDLE_TIMEOUT,
|
||
|
|
"Idle check interval must be less than idle timeout"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_pty_initial_size() {
|
||
|
|
// Test that initial PTY size is reasonable
|
||
|
|
const INITIAL_ROWS: u16 = 24;
|
||
|
|
const INITIAL_COLS: u16 = 80;
|
||
|
|
|
||
|
|
// Verify the constants are within reasonable ranges
|
||
|
|
assert!(INITIAL_ROWS > 0, "Initial rows should be positive");
|
||
|
|
assert!(INITIAL_COLS > 0, "Initial cols should be positive");
|
||
|
|
assert!(INITIAL_ROWS <= 500, "Initial rows should not exceed 500");
|
||
|
|
assert!(INITIAL_COLS <= 1000, "Initial cols should not exceed 1000");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_buffer_size() {
|
||
|
|
// Test that buffer size for reading from PTY is reasonable
|
||
|
|
const BUFFER_SIZE: usize = 8192;
|
||
|
|
|
||
|
|
// Verify it's a power of 2 (which implies it's >= 1)
|
||
|
|
assert!(
|
||
|
|
BUFFER_SIZE.is_power_of_two(),
|
||
|
|
"Buffer size should be power of 2"
|
||
|
|
);
|
||
|
|
// Verify it's in a reasonable range (power of 2 check above ensures >= 1)
|
||
|
|
assert!(BUFFER_SIZE <= 65536, "Buffer too large for practical use");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test path validation
|
||
|
|
#[test]
|
||
|
|
fn test_template_path_format() {
|
||
|
|
let template_path = "./templates/term.html";
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
template_path.starts_with("./"),
|
||
|
|
"Template path should be relative"
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
template_path.ends_with(".html"),
|
||
|
|
"Template should be HTML file"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_static_paths_format() {
|
||
|
|
let static_paths = vec![
|
||
|
|
"./static/terminal.js",
|
||
|
|
"./static/terminado-addon.js",
|
||
|
|
"./static/styles.css",
|
||
|
|
"./static/bg.png",
|
||
|
|
"./static/favicon.png",
|
||
|
|
];
|
||
|
|
|
||
|
|
for path in static_paths {
|
||
|
|
assert!(
|
||
|
|
path.starts_with("./static/"),
|
||
|
|
"Static file {} should be in ./static/ directory",
|
||
|
|
path
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test endpoint format
|
||
|
|
#[test]
|
||
|
|
fn test_endpoint_format() {
|
||
|
|
let websocket_endpoint = "/websocket";
|
||
|
|
let static_endpoint = "/static";
|
||
|
|
let assets_endpoint = "/assets";
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
websocket_endpoint.starts_with('/'),
|
||
|
|
"Endpoint should start with /"
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
!websocket_endpoint.ends_with('/'),
|
||
|
|
"Endpoint should not end with /"
|
||
|
|
);
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
static_endpoint.starts_with('/'),
|
||
|
|
"Static endpoint should start with /"
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
assets_endpoint.starts_with('/'),
|
||
|
|
"Assets endpoint should start with /"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_default_shell() {
|
||
|
|
let default_shell = "/bin/sh";
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
default_shell.starts_with('/'),
|
||
|
|
"Shell path should be absolute"
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
!default_shell.contains(' '),
|
||
|
|
"Shell path should not contain spaces"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_default_port() {
|
||
|
|
let default_port: u16 = 8082;
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
default_port >= 1024,
|
||
|
|
"Port should not be in privileged range"
|
||
|
|
);
|
||
|
|
// Note: u16 max is 65535, so this is always true for u16
|
||
|
|
// but we keep it for documentation purposes
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_default_host() {
|
||
|
|
let localhost = "127.0.0.1";
|
||
|
|
let all_interfaces = "0.0.0.0";
|
||
|
|
|
||
|
|
// Verify valid IP addresses
|
||
|
|
assert_eq!(
|
||
|
|
localhost.split('.').count(),
|
||
|
|
4,
|
||
|
|
"Localhost should have 4 octets"
|
||
|
|
);
|
||
|
|
assert_eq!(
|
||
|
|
all_interfaces.split('.').count(),
|
||
|
|
4,
|
||
|
|
"0.0.0.0 should have 4 octets"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test environment variables
|
||
|
|
#[test]
|
||
|
|
fn test_term_env_var() {
|
||
|
|
let term_var = "xterm";
|
||
|
|
|
||
|
|
// String literals are never empty, but we verify the expected value
|
||
|
|
assert_eq!(term_var, "xterm", "TERM variable should be xterm");
|
||
|
|
assert!(
|
||
|
|
!term_var.contains(' '),
|
||
|
|
"TERM variable should not contain spaces"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test size boundaries
|
||
|
|
#[test]
|
||
|
|
fn test_terminal_size_boundaries() {
|
||
|
|
// Minimum valid size
|
||
|
|
let min_rows: u16 = 1;
|
||
|
|
let min_cols: u16 = 1;
|
||
|
|
|
||
|
|
assert!(min_rows > 0, "Minimum rows must be positive");
|
||
|
|
assert!(min_cols > 0, "Minimum cols must be positive");
|
||
|
|
|
||
|
|
// Maximum reasonable size
|
||
|
|
let max_rows: u16 = 1000;
|
||
|
|
let max_cols: u16 = 1000;
|
||
|
|
|
||
|
|
assert!(max_rows < u16::MAX / 2, "Max rows should be reasonable");
|
||
|
|
assert!(max_cols < u16::MAX / 2, "Max cols should be reasonable");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_zero_size_handling() {
|
||
|
|
// Zero-sized terminals should be rejected
|
||
|
|
let zero_rows: u16 = 0;
|
||
|
|
let zero_cols: u16 = 0;
|
||
|
|
|
||
|
|
// These would be rejected by the resize handler
|
||
|
|
assert_eq!(zero_rows, 0);
|
||
|
|
assert_eq!(zero_cols, 0);
|
||
|
|
// In actual code, these should trigger an early return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test WebSocket message size limits
|
||
|
|
#[test]
|
||
|
|
fn test_message_size_reasonable() {
|
||
|
|
// Messages should have reasonable size limits
|
||
|
|
const MAX_MESSAGE_SIZE: usize = 1024 * 1024; // 1MB
|
||
|
|
const MIN_MESSAGE_SIZE: usize = 8192; // 8KB
|
||
|
|
const MAX_ALLOWED_SIZE: usize = 10 * 1024 * 1024; // 10MB
|
||
|
|
|
||
|
|
// Verify the relationship between constants
|
||
|
|
assert!(
|
||
|
|
MAX_MESSAGE_SIZE >= MIN_MESSAGE_SIZE,
|
||
|
|
"Max message size should be at least {} bytes",
|
||
|
|
MIN_MESSAGE_SIZE
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
MAX_MESSAGE_SIZE <= MAX_ALLOWED_SIZE,
|
||
|
|
"Max message size should not exceed {} bytes",
|
||
|
|
MAX_ALLOWED_SIZE
|
||
|
|
);
|
||
|
|
}
|