socktop-webterm/tests/config_tests.rs
jasonwitty 850cf32b50
All checks were successful
Build and Deploy to K3s / test (push) Successful in 2m40s
Build and Deploy to K3s / lint (push) Successful in 1m33s
Build and Deploy to K3s / build-and-push (push) Successful in 5m17s
Build and Deploy to K3s / deploy (push) Successful in 9s
- add cargo fmt / clippy to actions build. - add common unit tests. -
improved security sanitization - security spcecific unit tests - add
unit tests to workflow build - add unami analytics.
2025-11-30 01:37:07 -08:00

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
);
}