| """
|
| Юнит-тесты для утилитарных функций colab_tunnel._utils.
|
| """
|
| import sys
|
| import subprocess
|
|
|
| import pytest
|
|
|
| from colab_tunnel._utils import is_ipv4, run, read_until_pattern
|
|
|
|
|
|
|
|
|
| @pytest.mark.parametrize('addr, expected', [
|
| ('192.168.1.1', True),
|
| ('10.0.0.1', True),
|
| ('255.255.255.255', True),
|
| ('0.0.0.0', True),
|
| ('192.168.1.256', False),
|
| ('192.168.1', False),
|
| ('not-an-ip', False),
|
| ('', False),
|
| ('999.0.0.1', False),
|
| ('1.2.3.4.5', False),
|
| ])
|
| def test_is_ipv4(addr: str, expected: bool) -> None:
|
| assert is_ipv4(addr) is expected
|
|
|
|
|
|
|
|
|
| def test_run_captures_output() -> None:
|
| result = run('echo hello_world')
|
| assert result['status_code'] == 0
|
| assert 'hello_world' in result['output']
|
|
|
|
|
| def test_run_nonzero_exit() -> None:
|
| result = run('sh -c "exit 42"')
|
| assert result['status_code'] == 42
|
|
|
|
|
| def test_run_captures_stderr() -> None:
|
| result = run('sh -c "echo err_msg >&2"')
|
| assert 'err_msg' in result['output']
|
|
|
|
|
| def test_run_timeout_returns_minus_one() -> None:
|
| result = run('sleep 60', timeout=0.3)
|
| assert result['status_code'] == -1
|
| assert 'Таймаут' in result['output']
|
|
|
|
|
|
|
|
|
| @pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| def test_read_until_pattern_found_in_stdout() -> None:
|
| process = subprocess.Popen(
|
| [sys.executable, '-c',
|
| 'import sys, time; print("https://example.com"); sys.stdout.flush(); time.sleep(60)'],
|
| stdout=subprocess.PIPE,
|
| stderr=subprocess.PIPE,
|
| )
|
| try:
|
| url, output = read_until_pattern(process, r'https://\S+', timeout=5.0)
|
| assert url == 'https://example.com'
|
| assert 'https://example.com' in output
|
| finally:
|
| process.kill()
|
| process.wait()
|
|
|
|
|
| @pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| def test_read_until_pattern_found_in_stderr() -> None:
|
| process = subprocess.Popen(
|
| [sys.executable, '-c',
|
| 'import sys, time; sys.stderr.write("https://stderr.example.com\\n"); sys.stderr.flush(); time.sleep(60)'],
|
| stdout=subprocess.PIPE,
|
| stderr=subprocess.PIPE,
|
| )
|
| try:
|
| url, _ = read_until_pattern(
|
| process, r'https://\S+', timeout=5.0, read_from_stderr=True
|
| )
|
| assert 'https://stderr.example.com' in url
|
| finally:
|
| process.kill()
|
| process.wait()
|
|
|
|
|
| @pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| def test_read_until_pattern_raises_on_timeout() -> None:
|
| process = subprocess.Popen(
|
| [sys.executable, '-c', 'import time; time.sleep(60)'],
|
| stdout=subprocess.PIPE,
|
| stderr=subprocess.PIPE,
|
| )
|
| try:
|
| with pytest.raises(RuntimeError, match='URL не найден'):
|
| read_until_pattern(process, r'https://\S+', timeout=0.5)
|
| finally:
|
| process.kill()
|
| process.wait()
|
|
|
|
|
| @pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| def test_read_until_pattern_raises_when_process_exits_without_url() -> None:
|
| process = subprocess.Popen(
|
| [sys.executable, '-c', 'print("no url here at all")'],
|
| stdout=subprocess.PIPE,
|
| stderr=subprocess.PIPE,
|
| )
|
| with pytest.raises(RuntimeError):
|
| read_until_pattern(process, r'https://\S+', timeout=5.0)
|
|
|
|
|
| @pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| def test_read_both_streams_finds_url_in_either() -> None:
|
| """URL в stderr должен находиться при read_both_streams=True."""
|
| process = subprocess.Popen(
|
| [sys.executable, '-c',
|
| 'import sys, time; sys.stderr.write("https://both.example.com\\n"); sys.stderr.flush(); time.sleep(60)'],
|
| stdout=subprocess.PIPE,
|
| stderr=subprocess.PIPE,
|
| )
|
| try:
|
| url, _ = read_until_pattern(
|
| process, r'https://\S+', timeout=5.0, read_both_streams=True
|
| )
|
| assert 'https://both.example.com' in url
|
| finally:
|
| process.kill()
|
| process.wait()
|
|
|