diff --git a/tests/test_convert.py b/tests/test_convert.py new file mode 100644 index 0000000..c57e409 --- /dev/null +++ b/tests/test_convert.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import pytest +import tempfile +from src import boot +from typing import Callable +from unittest.mock import patch, mock_open + + +@pytest.fixture(autouse=True) +def init_opts(): + boot.init_opts() + yield + + +@pytest.fixture +def temp_dir_setup(): + env_backup = os.environ.copy() + os.environ['CERT_DIR'] = (cert_dir := tempfile.mkdtemp()) + os.environ['TEMP_DIR'] = (temp_dir := tempfile.mkdtemp()) + os.environ['WORK_DIR'] = (work_dir := tempfile.mkdtemp()) + + yield cert_dir, temp_dir, work_dir + + os.environ.clear() + os.environ.update(env_backup) + + +def convert_builder(keep_port: bool = False, keep_salt: bool = False) -> Callable[[boot.SyncplayOptions], list[str]]: + def convert(opts: boot.SyncplayOptions) -> list[str]: + output = boot.sp_convert(opts) + + if not keep_port: + if '--port' in output and '8999' in output: + if (index := output.index('--port')) + 1 == output.index('8999'): + output = output[:index] + output[index + 2:] + + if not keep_salt: + if '--salt' in output and '' in output: + if (index := output.index('--salt')) + 1 == output.index(''): + output = output[:index] + output[index + 2:] + + return output + + return convert + + +def test_port() -> None: + """ + Test port field of options conversion. + """ + convert = convert_builder(keep_port=True) + assert convert({}) == ['--port', '8999'] # default port + assert convert({'port': 12345}) == ['--port', '12345'] # custom port + + +def test_password() -> None: + """ + Test password field of options conversion. + """ + convert = convert_builder() + assert convert({}) == [] + assert convert({'password': 'PWD'}) == ['--password', 'PWD'] # custom password + + +@patch('src.boot.open', new_callable=mock_open) +def test_motd(mock_file) -> None: + """ + Test motd field of options conversion. + """ + convert = convert_builder() + assert convert({}) == [] + assert convert({'motd': 'MSG'}) == ['--motd-file', '/tmp/motd.data'] + mock_file.assert_called_with('/tmp/motd.data', 'w', encoding='utf-8') + mock_file().write.assert_called_with('MSG') + + +def test_salt_handling() -> None: + """ + Test salt related handling of options conversion. + """ + convert = convert_builder(keep_salt=True) + assert convert({}) == ['--salt', ''] + assert convert({'salt': ''}) == ['--salt', ''] + assert convert({'salt': 'SALT'}) == ['--salt', 'SALT'] + + assert convert({'random_salt': True}) == [] + assert convert({'salt': '', 'random_salt': True}) == ['--salt', ''] + assert convert({'salt': 'SALT', 'random_salt': True}) == ['--salt', 'SALT'] + + +def test_boolean_flags() -> None: + """ + Test boolean flags handling of options conversion. + """ + convert = convert_builder() + assert convert({'isolate_rooms': True}) == [f'--isolate-rooms'] + assert convert({'disable_chat': True}) == [f'--disable-chat'] + assert convert({'disable_ready': True}) == [f'--disable-ready'] + + assert convert({'enable_stats': True}) == ['--stats-db-file', '/data/stats.db'] + assert convert({'enable_tls': True}) == ['--tls', '/certs/'] + assert convert({'persistent': True}) == ['--rooms-db-file', '/data/rooms.db'] + + +def test_limited_values() -> None: + """ + Test limited values handling of options conversion. + """ + convert = convert_builder() + assert convert({'max_username': 20}) == ['--max-username-length', '20'] + assert convert({'max_chat_message': 500}) == ['--max-chat-message-length', '500'] + + +@patch('src.boot.open', new_callable=mock_open) +def test_permanent_rooms(mock_file) -> None: + """ + Test permanent rooms field of options conversion. + """ + convert = convert_builder() + assert convert({'permanent_rooms': ['R1', 'R2', 'R3']}) == ['--permanent-rooms-file', '/tmp/rooms.list'] + mock_file.assert_called_with('/tmp/rooms.list', 'w', encoding='utf-8') + mock_file().write.assert_called_with('R1\nR2\nR3') + + +def test_ip_configure() -> None: + """ + Test IP configure handling of options conversion. + """ + convert = convert_builder() + assert convert({}) == [] + assert convert({'listen_ipv4': '0.0.0.0'}) == ['--ipv4-only', '--interface-ipv4', '0.0.0.0'] + assert convert({'listen_ipv6': '::'}) == ['--ipv6-only', '--interface-ipv6', '::'] + assert convert({'listen_ipv4': '0.0.0.0', 'listen_ipv6': '::'}) == ['--interface-ipv4', '0.0.0.0', '--interface-ipv6', '::'] + + +def test_path_env(temp_dir_setup) -> None: + """ + Test path env variables of options conversion. + """ + convert = convert_builder() + cert_dir, temp_dir, work_dir = temp_dir_setup + + assert convert({'enable_tls': True}) == ['--tls', cert_dir] # tls using CERT_DIR + + path = os.path.join(temp_dir, 'motd.data') # motd using TEMP_DIR + assert convert({'motd': 'MESSAGE'}) == ['--motd-file', path] + + path = os.path.join(work_dir, 'stats.db') # stats-db using WORK_DIR + assert convert({'enable_stats': True}) == ['--stats-db-file', path] + + +def test_full_options() -> None: + """ + Test full options conversion. + """ + convert = convert_builder(keep_port=True, keep_salt=True) + options = convert({ + 'port': 12345, + 'password': 'secret', + 'motd': 'Welcome to Syncplay', + 'salt': 'mysalt', + 'random_salt': True, + 'isolate_rooms': True, + 'disable_chat': True, + 'disable_ready': True, + 'enable_stats': True, + 'enable_tls': True, + 'persistent': True, + 'max_username': 20, + 'max_chat_message': 500, + 'permanent_rooms': ['room1', 'room2', 'room3'], + 'listen_ipv4': '0.0.0.0', + 'listen_ipv6': '::' + }) + + expected = [ + '--port', '12345', + '--password', 'secret', + '--motd-file', '/tmp/motd.data', + '--salt', 'mysalt', + '--isolate-rooms', + '--disable-chat', + '--disable-ready', + '--stats-db-file', '/data/stats.db', + '--tls', '/certs/', + '--rooms-db-file', '/data/rooms.db', + '--max-username-length', '20', + '--max-chat-message-length', '500', + '--permanent-rooms-file', '/tmp/rooms.list', + '--interface-ipv4', '0.0.0.0', + '--interface-ipv6', '::' + ] + + assert options == expected diff --git a/tests/test_env_opts.py b/tests/test_env_opts.py index d016d43..1c122e5 100644 --- a/tests/test_env_opts.py +++ b/tests/test_env_opts.py @@ -16,7 +16,10 @@ def env_cleanup(): os.environ.clear() os.environ['UNKNOWN'] = 'unknown_value' os.environ['ANOTHER_UNKNOWN'] = 'something' + yield + + os.environ.clear() os.environ.update(env_backup)