Exponential backoff up to 300s made no sense with rotating Tor exits where each reconnect gets a fresh IP. Single 1s delay is sufficient. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
203 lines
4.9 KiB
Python
203 lines
4.9 KiB
Python
"""Tests for configuration loading."""
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from bouncer.config import load
|
|
|
|
MINIMAL_CONFIG = """\
|
|
[bouncer]
|
|
password = "secret"
|
|
|
|
[proxy]
|
|
host = "127.0.0.1"
|
|
port = 1080
|
|
|
|
[networks.test]
|
|
host = "irc.example.com"
|
|
"""
|
|
|
|
FULL_CONFIG = """\
|
|
[bouncer]
|
|
bind = "0.0.0.0"
|
|
port = 6668
|
|
password = "hunter2"
|
|
|
|
[bouncer.backlog]
|
|
max_messages = 5000
|
|
replay_on_connect = false
|
|
|
|
[proxy]
|
|
host = "10.0.0.1"
|
|
port = 9050
|
|
|
|
[networks.libera]
|
|
host = "irc.libera.chat"
|
|
port = 6697
|
|
tls = true
|
|
nick = "testbot"
|
|
user = "testuser"
|
|
realname = "Test Bot"
|
|
channels = ["#test", "#dev"]
|
|
autojoin = true
|
|
|
|
[networks.oftc]
|
|
host = "irc.oftc.net"
|
|
port = 6697
|
|
tls = true
|
|
nick = "testbot"
|
|
channels = ["#debian"]
|
|
"""
|
|
|
|
|
|
def _write_config(content: str) -> Path:
|
|
f = tempfile.NamedTemporaryFile(mode="w", suffix=".toml", delete=False)
|
|
f.write(content)
|
|
f.close()
|
|
return Path(f.name)
|
|
|
|
|
|
class TestLoad:
|
|
def test_minimal(self):
|
|
cfg = load(_write_config(MINIMAL_CONFIG))
|
|
assert cfg.bouncer.password == "secret"
|
|
assert cfg.bouncer.bind == "127.0.0.1"
|
|
assert cfg.bouncer.port == 6667
|
|
assert cfg.bouncer.backlog.max_messages == 10000
|
|
assert cfg.proxy.host == "127.0.0.1"
|
|
assert "test" in cfg.networks
|
|
net = cfg.networks["test"]
|
|
assert net.host == "irc.example.com"
|
|
assert net.port == 6667
|
|
assert net.tls is False
|
|
|
|
def test_full(self):
|
|
cfg = load(_write_config(FULL_CONFIG))
|
|
assert cfg.bouncer.bind == "0.0.0.0"
|
|
assert cfg.bouncer.port == 6668
|
|
assert cfg.bouncer.backlog.max_messages == 5000
|
|
assert cfg.bouncer.backlog.replay_on_connect is False
|
|
assert cfg.proxy.port == 9050
|
|
assert len(cfg.networks) == 2
|
|
libera = cfg.networks["libera"]
|
|
assert libera.tls is True
|
|
assert libera.port == 6697
|
|
assert libera.channels == ["#test", "#dev"]
|
|
assert libera.nick == "testbot"
|
|
|
|
def test_no_networks_raises(self):
|
|
config = """\
|
|
[bouncer]
|
|
password = "x"
|
|
|
|
[proxy]
|
|
"""
|
|
with pytest.raises(ValueError, match="at least one network"):
|
|
load(_write_config(config))
|
|
|
|
def test_slash_in_network_name_raises(self):
|
|
config = """\
|
|
[bouncer]
|
|
password = "x"
|
|
|
|
[proxy]
|
|
|
|
[networks."lib/era"]
|
|
host = "irc.example.com"
|
|
"""
|
|
with pytest.raises(ValueError, match="must not contain '/'"):
|
|
load(_write_config(config))
|
|
|
|
def test_tls_default_port(self):
|
|
config = """\
|
|
[bouncer]
|
|
password = "x"
|
|
|
|
[proxy]
|
|
|
|
[networks.test]
|
|
host = "irc.example.com"
|
|
tls = true
|
|
"""
|
|
cfg = load(_write_config(config))
|
|
assert cfg.networks["test"].port == 6697
|
|
|
|
def test_channel_keys_parsed(self):
|
|
config = """\
|
|
[bouncer]
|
|
password = "x"
|
|
|
|
[proxy]
|
|
|
|
[networks.test]
|
|
host = "irc.example.com"
|
|
channels = ["#secret", "#public"]
|
|
channel_keys = { "#secret" = "hunter2" }
|
|
"""
|
|
cfg = load(_write_config(config))
|
|
net = cfg.networks["test"]
|
|
assert net.channel_keys == {"#secret": "hunter2"}
|
|
assert "#secret" in net.channels
|
|
|
|
def test_channel_keys_default_empty(self):
|
|
cfg = load(_write_config(MINIMAL_CONFIG))
|
|
net = cfg.networks["test"]
|
|
assert net.channel_keys == {}
|
|
|
|
def test_operational_defaults(self):
|
|
"""Ensure all operational values have sane defaults."""
|
|
cfg = load(_write_config(MINIMAL_CONFIG))
|
|
b = cfg.bouncer
|
|
assert b.probation_seconds == 45
|
|
assert b.backoff_steps == [1]
|
|
assert b.nick_timeout == 10
|
|
assert b.rejoin_delay == 3
|
|
assert b.http_timeout == 15
|
|
assert b.captcha_api_key == ""
|
|
assert b.captcha_poll_interval == 3
|
|
assert b.captcha_poll_timeout == 120
|
|
assert b.email_poll_interval == 15
|
|
assert b.email_max_polls == 30
|
|
assert b.email_request_timeout == 20
|
|
assert b.cert_validity_days == 3650
|
|
|
|
def test_operational_overrides(self):
|
|
"""Configurable operational values are parsed from TOML."""
|
|
config = """\
|
|
[bouncer]
|
|
password = "x"
|
|
probation_seconds = 60
|
|
backoff_steps = [10, 30, 90]
|
|
nick_timeout = 20
|
|
rejoin_delay = 5
|
|
http_timeout = 30
|
|
captcha_api_key = "test-key"
|
|
captcha_poll_interval = 5
|
|
captcha_poll_timeout = 60
|
|
email_poll_interval = 10
|
|
email_max_polls = 20
|
|
email_request_timeout = 25
|
|
cert_validity_days = 365
|
|
|
|
[proxy]
|
|
|
|
[networks.test]
|
|
host = "irc.example.com"
|
|
"""
|
|
cfg = load(_write_config(config))
|
|
b = cfg.bouncer
|
|
assert b.probation_seconds == 60
|
|
assert b.backoff_steps == [10, 30, 90]
|
|
assert b.nick_timeout == 20
|
|
assert b.rejoin_delay == 5
|
|
assert b.http_timeout == 30
|
|
assert b.captcha_api_key == "test-key"
|
|
assert b.captcha_poll_interval == 5
|
|
assert b.captcha_poll_timeout == 60
|
|
assert b.email_poll_interval == 10
|
|
assert b.email_max_polls == 20
|
|
assert b.email_request_timeout == 25
|
|
assert b.cert_validity_days == 365
|