Coverage for src / beaverbunch / network / config.py: 100.0%

31 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-09 00:24 +0000

1from __future__ import annotations 

2 

3import os 

4from dataclasses import dataclass 

5 

6from beaverbunch import __version__ 

7 

8_DEFAULT_CORS_ORIGINS = ( 

9 "http://localhost:3000", 

10 "http://127.0.0.1:3000", 

11) 

12 

13 

14@dataclass(frozen=True) 

15class RuntimeConfig: 

16 app_name: str = "Beaverbunch Lobby API" 

17 app_version: str = __version__ 

18 host: str = "0.0.0.0" 

19 port: int = 8000 

20 cors_origins: tuple[str, ...] = _DEFAULT_CORS_ORIGINS 

21 max_sessions: int = 25 

22 log_level: str = "info" 

23 

24 

25def load_runtime_config() -> RuntimeConfig: 

26 return RuntimeConfig( 

27 host=os.getenv("BEAVERBUNCH_HOST", RuntimeConfig.host), 

28 port=_read_int_env("BEAVERBUNCH_PORT", RuntimeConfig.port, minimum=1), 

29 cors_origins=_read_cors_origins(), 

30 max_sessions=_read_int_env("BEAVERBUNCH_MAX_SESSIONS", RuntimeConfig.max_sessions, minimum=1), 

31 log_level=os.getenv("BEAVERBUNCH_LOG_LEVEL", RuntimeConfig.log_level).lower(), 

32 ) 

33 

34 

35def _read_int_env(name: str, default: int, *, minimum: int) -> int: 

36 raw_value = os.getenv(name) 

37 if raw_value is None or not raw_value.strip(): 

38 return default 

39 

40 try: 

41 value = int(raw_value) 

42 except ValueError as exc: # pragma: no cover - exercised through caller tests 

43 raise ValueError(f"Environment variable {name} must be an integer") from exc 

44 

45 if value < minimum: 

46 raise ValueError(f"Environment variable {name} must be >= {minimum}") 

47 return value 

48 

49 

50def _read_cors_origins() -> tuple[str, ...]: 

51 raw_value = os.getenv("BEAVERBUNCH_CORS_ORIGINS") 

52 if raw_value is None: 

53 return _DEFAULT_CORS_ORIGINS 

54 

55 origins = tuple(origin.strip() for origin in raw_value.split(",") if origin.strip()) 

56 return origins