Delete tests
Browse files- tests/__init__.py +0 -0
- tests/test_regex.py +0 -105
- tests/test_utils.py +0 -133
tests/__init__.py
DELETED
|
File without changes
|
tests/test_regex.py
DELETED
|
@@ -1,105 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Юнит-тесты для regex-паттернов поиска URL в выводе туннельных утилит.
|
| 3 |
-
Тесты не требуют сети или реальных процессов.
|
| 4 |
-
"""
|
| 5 |
-
import re
|
| 6 |
-
import pytest
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
# ── Cloudflared ────────────────────────────────────────────────────────
|
| 10 |
-
CF_PATTERN = r'(?P<url>https?://\S+\.trycloudflare\.com)'
|
| 11 |
-
|
| 12 |
-
@pytest.mark.parametrize('text, expected', [
|
| 13 |
-
(
|
| 14 |
-
'Visit your tunnel at https://autumn-bread-1234.trycloudflare.com',
|
| 15 |
-
'https://autumn-bread-1234.trycloudflare.com',
|
| 16 |
-
),
|
| 17 |
-
(
|
| 18 |
-
'https://my-test.trycloudflare.com is ready',
|
| 19 |
-
'https://my-test.trycloudflare.com',
|
| 20 |
-
),
|
| 21 |
-
])
|
| 22 |
-
def test_cloudflared(text: str, expected: str) -> None:
|
| 23 |
-
m = re.search(CF_PATTERN, text)
|
| 24 |
-
assert m is not None, f'Паттерн не нашёл URL в: {text!r}'
|
| 25 |
-
assert m.group() == expected
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
# ── Bore ───────────────────────────────────────────────────────────────
|
| 29 |
-
BORE_PATTERN = r'bore\.pub:\d+'
|
| 30 |
-
|
| 31 |
-
@pytest.mark.parametrize('text, expected', [
|
| 32 |
-
('INFO bore::client: listening at bore.pub:12345', 'bore.pub:12345'),
|
| 33 |
-
('connected to bore.pub:54321 successfully', 'bore.pub:54321'),
|
| 34 |
-
])
|
| 35 |
-
def test_bore(text: str, expected: str) -> None:
|
| 36 |
-
m = re.search(BORE_PATTERN, text)
|
| 37 |
-
assert m is not None
|
| 38 |
-
assert m.group() == expected
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
# ── MMAR ───────────────────────────────────────────────────────────────
|
| 42 |
-
@pytest.mark.parametrize('text, expected', [
|
| 43 |
-
('Tunnel at https://abc123.mmar.dev', 'https://abc123.mmar.dev'),
|
| 44 |
-
('http://test-tunnel.mmar.dev/ active', 'http://test-tunnel.mmar.dev/'),
|
| 45 |
-
])
|
| 46 |
-
def test_mmar(text: str, expected: str) -> None:
|
| 47 |
-
m = re.search(r'(?P<url>https?://\S+\.mmar\.dev)', text)
|
| 48 |
-
assert m is not None
|
| 49 |
-
assert m.group() == expected
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
# ── Tunnelite ──────────────────────────────────────────────────────────
|
| 53 |
-
def test_tunnelite() -> None:
|
| 54 |
-
text = 'Your public URL: https://random-words.tunnelite.com'
|
| 55 |
-
m = re.search(r'(?P<url>https?://\S+\.tunnelite\.com)', text)
|
| 56 |
-
assert m is not None
|
| 57 |
-
assert m.group() == 'https://random-words.tunnelite.com'
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
# ── Beeceptor ──────────────────────────────────────────────────────────
|
| 61 |
-
def test_beeceptor() -> None:
|
| 62 |
-
text = 'Tunnel running at https://my-endpoint.beeceptor.com'
|
| 63 |
-
m = re.search(r'(?P<url>https?://\S+\.beeceptor\.com)', text)
|
| 64 |
-
assert m is not None
|
| 65 |
-
assert m.group() == 'https://my-endpoint.beeceptor.com'
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
# ── Bore Digital ───────────────────────────────────────────────────────
|
| 69 |
-
def test_boredigital() -> None:
|
| 70 |
-
text = '| https://xyz123.bore.digital | active |'
|
| 71 |
-
m = re.search(r'(?P<url>https://\S+\.bore\.digital)', text)
|
| 72 |
-
assert m is not None
|
| 73 |
-
assert m.group() == 'https://xyz123.bore.digital'
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
# ── SSH-провайдеры ─────────────────────────────────────────────────────
|
| 77 |
-
@pytest.mark.parametrize('pattern, text, should_match', [
|
| 78 |
-
# Optimistix
|
| 79 |
-
(r'https://\S+\.otnl\.link',
|
| 80 |
-
'Forwarding from https://tunnel-abc.otnl.link', True),
|
| 81 |
-
|
| 82 |
-
# srv.us — минимум 10 символов в субдомене
|
| 83 |
-
(r'https://[a-z0-9]{10,}\.srv\.us/?',
|
| 84 |
-
'https://abcdefghij.srv.us', True),
|
| 85 |
-
(r'https://[a-z0-9]{10,}\.srv\.us/?',
|
| 86 |
-
'https://short.srv.us', False),
|
| 87 |
-
|
| 88 |
-
# Serveo
|
| 89 |
-
(r'https://\S+\.serveousercontent\.com',
|
| 90 |
-
'Forwarding from https://mytunnel.serveousercontent.com', True),
|
| 91 |
-
|
| 92 |
-
# localhost.run — admin исключён
|
| 93 |
-
(r'https://(?!admin\b)[a-zA-Z0-9-]+\.(?:localhost\.run|lhr\.life)',
|
| 94 |
-
'https://abc123.localhost.run', True),
|
| 95 |
-
(r'https://(?!admin\b)[a-zA-Z0-9-]+\.(?:localhost\.run|lhr\.life)',
|
| 96 |
-
'https://admin.localhost.run', False),
|
| 97 |
-
(r'https://(?!admin\b)[a-zA-Z0-9-]+\.(?:localhost\.run|lhr\.life)',
|
| 98 |
-
'https://my-tunnel.lhr.life', True),
|
| 99 |
-
])
|
| 100 |
-
def test_ssh_patterns(pattern: str, text: str, should_match: bool) -> None:
|
| 101 |
-
m = re.search(pattern, text)
|
| 102 |
-
if should_match:
|
| 103 |
-
assert m is not None, f'{pattern!r} не нашёл совпадение в {text!r}'
|
| 104 |
-
else:
|
| 105 |
-
assert m is None, f'{pattern!r} не должен совпадать с {text!r}'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_utils.py
DELETED
|
@@ -1,133 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Юнит-тесты для утилитарных функций colab_tunnel._utils.
|
| 3 |
-
"""
|
| 4 |
-
import sys
|
| 5 |
-
import subprocess
|
| 6 |
-
|
| 7 |
-
import pytest
|
| 8 |
-
|
| 9 |
-
from colab_tunnel._utils import is_ipv4, run, read_until_pattern
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
# ── is_ipv4 ────────────────────────────────────────────────────────────
|
| 13 |
-
|
| 14 |
-
@pytest.mark.parametrize('addr, expected', [
|
| 15 |
-
('192.168.1.1', True),
|
| 16 |
-
('10.0.0.1', True),
|
| 17 |
-
('255.255.255.255', True),
|
| 18 |
-
('0.0.0.0', True),
|
| 19 |
-
('192.168.1.256', False),
|
| 20 |
-
('192.168.1', False),
|
| 21 |
-
('not-an-ip', False),
|
| 22 |
-
('', False),
|
| 23 |
-
('999.0.0.1', False),
|
| 24 |
-
('1.2.3.4.5', False),
|
| 25 |
-
])
|
| 26 |
-
def test_is_ipv4(addr: str, expected: bool) -> None:
|
| 27 |
-
assert is_ipv4(addr) is expected
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
# ── run() ──────────────────────────────────────────────────────────────
|
| 31 |
-
|
| 32 |
-
def test_run_captures_output() -> None:
|
| 33 |
-
result = run('echo hello_world')
|
| 34 |
-
assert result['status_code'] == 0
|
| 35 |
-
assert 'hello_world' in result['output']
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def test_run_nonzero_exit() -> None:
|
| 39 |
-
result = run('sh -c "exit 42"')
|
| 40 |
-
assert result['status_code'] == 42
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
def test_run_captures_stderr() -> None:
|
| 44 |
-
result = run('sh -c "echo err_msg >&2"')
|
| 45 |
-
assert 'err_msg' in result['output']
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
def test_run_timeout_returns_minus_one() -> None:
|
| 49 |
-
result = run('sleep 60', timeout=0.3)
|
| 50 |
-
assert result['status_code'] == -1
|
| 51 |
-
assert 'Таймаут' in result['output']
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
# ── read_until_pattern() ───────────────────────────────────────────────
|
| 55 |
-
|
| 56 |
-
@pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| 57 |
-
def test_read_until_pattern_found_in_stdout() -> None:
|
| 58 |
-
process = subprocess.Popen(
|
| 59 |
-
[sys.executable, '-c',
|
| 60 |
-
'import sys, time; print("https://example.com"); sys.stdout.flush(); time.sleep(60)'],
|
| 61 |
-
stdout=subprocess.PIPE,
|
| 62 |
-
stderr=subprocess.PIPE,
|
| 63 |
-
)
|
| 64 |
-
try:
|
| 65 |
-
url, output = read_until_pattern(process, r'https://\S+', timeout=5.0)
|
| 66 |
-
assert url == 'https://example.com'
|
| 67 |
-
assert 'https://example.com' in output
|
| 68 |
-
finally:
|
| 69 |
-
process.kill()
|
| 70 |
-
process.wait()
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
@pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| 74 |
-
def test_read_until_pattern_found_in_stderr() -> None:
|
| 75 |
-
process = subprocess.Popen(
|
| 76 |
-
[sys.executable, '-c',
|
| 77 |
-
'import sys, time; sys.stderr.write("https://stderr.example.com\\n"); sys.stderr.flush(); time.sleep(60)'],
|
| 78 |
-
stdout=subprocess.PIPE,
|
| 79 |
-
stderr=subprocess.PIPE,
|
| 80 |
-
)
|
| 81 |
-
try:
|
| 82 |
-
url, _ = read_until_pattern(
|
| 83 |
-
process, r'https://\S+', timeout=5.0, read_from_stderr=True
|
| 84 |
-
)
|
| 85 |
-
assert 'https://stderr.example.com' in url
|
| 86 |
-
finally:
|
| 87 |
-
process.kill()
|
| 88 |
-
process.wait()
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
@pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| 92 |
-
def test_read_until_pattern_raises_on_timeout() -> None:
|
| 93 |
-
process = subprocess.Popen(
|
| 94 |
-
[sys.executable, '-c', 'import time; time.sleep(60)'],
|
| 95 |
-
stdout=subprocess.PIPE,
|
| 96 |
-
stderr=subprocess.PIPE,
|
| 97 |
-
)
|
| 98 |
-
try:
|
| 99 |
-
with pytest.raises(RuntimeError, match='URL не найден'):
|
| 100 |
-
read_until_pattern(process, r'https://\S+', timeout=0.5)
|
| 101 |
-
finally:
|
| 102 |
-
process.kill()
|
| 103 |
-
process.wait()
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
@pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| 107 |
-
def test_read_until_pattern_raises_when_process_exits_without_url() -> None:
|
| 108 |
-
process = subprocess.Popen(
|
| 109 |
-
[sys.executable, '-c', 'print("no url here at all")'],
|
| 110 |
-
stdout=subprocess.PIPE,
|
| 111 |
-
stderr=subprocess.PIPE,
|
| 112 |
-
)
|
| 113 |
-
with pytest.raises(RuntimeError):
|
| 114 |
-
read_until_pattern(process, r'https://\S+', timeout=5.0)
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
@pytest.mark.skipif(sys.platform == 'win32', reason='select() с пайпами не поддерживается на Windows')
|
| 118 |
-
def test_read_both_streams_finds_url_in_either() -> None:
|
| 119 |
-
"""URL в stderr должен находиться при read_both_streams=True."""
|
| 120 |
-
process = subprocess.Popen(
|
| 121 |
-
[sys.executable, '-c',
|
| 122 |
-
'import sys, time; sys.stderr.write("https://both.example.com\\n"); sys.stderr.flush(); time.sleep(60)'],
|
| 123 |
-
stdout=subprocess.PIPE,
|
| 124 |
-
stderr=subprocess.PIPE,
|
| 125 |
-
)
|
| 126 |
-
try:
|
| 127 |
-
url, _ = read_until_pattern(
|
| 128 |
-
process, r'https://\S+', timeout=5.0, read_both_streams=True
|
| 129 |
-
)
|
| 130 |
-
assert 'https://both.example.com' in url
|
| 131 |
-
finally:
|
| 132 |
-
process.kill()
|
| 133 |
-
process.wait()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|