Files
tazlin 415277541a feat: build out test framework, CI test stack, and load-surfaced hardening
Establishes additional unit/integration testing, the CI to run it, and the runtime fixes those tests surfaced

Testing
- Add unit (ORM/function) and integration (API route) tiers over a shared dependency runtime that auto-provisions Postgres, Redis, and S3-compatible object storage (Garage) via testcontainers, with an external-deps mode.
- Add fixtures/factories plus coverage for user roles, kudos pricing, worker.can_generate, countermeasures, WP activation, and adversarial malformed-input route sweeps.
- Modularize the locust suite into locustsuite/ and add a stability-only smoke gate (check_smoke_results.py).

CI / tooling
- Rework pr/main/trusted-fork/publish workflows around composite actions (setup-test-python, start-test-stack) and a Dockerized PG+Redis+Garage stack.
- Bump the Python floor to 3.12 (+3.13 matrix) and consolidate on uv.

Runtime hardening
- Make telemetry activation opt-in (telemetry_enabled): the image stays telemetry-capable but a bare server.py is inert unless an OTLP endpoint or AI_HORDE_TELEMETRY_ENABLED is set.
- Turn NUL bytes, out-of-range user ids, and duplicate worker names into clean 4xx responses instead of session-poisoning 500s.
- Fix get_anon() to resolve the anon account by oauth_id (re-enabling the kudos-transfer guards); shrink and instrument the SQLAlchemy pool (15/5) to expose connection-hold inefficiency as pool timeouts rather than PG refusals.
2026-07-05 13:36:59 -04:00

158 lines
5.2 KiB
Python

# SPDX-FileCopyrightText: 2022 Konstantinos Thoukydidis <mail@dbzer0.com>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
import sys
from functools import partialmethod
from loguru import logger
STDOUT_LEVELS = ["GENERATION", "PROMPT"]
INIT_LEVELS = ["INIT", "INIT_OK", "INIT_WARN", "INIT_ERR"]
MESSAGE_LEVELS = ["MESSAGE"]
# By default we're at error level or higher
verbosity = 40
quiet = 0
def set_logger_verbosity(count):
global verbosity
# The count comes reversed. So count = 0 means minimum verbosity
# While count 5 means maximum verbosity
# So the more count we have, the lowe we drop the versbosity maximum
verbosity = 40 - (count * 10)
def quiesce_logger(count):
global quiet
# The bigger the count, the more silent we want our logger
quiet = count * 10
def is_stdout_log(record):
if record["level"].name not in STDOUT_LEVELS:
return False
if record["level"].no < verbosity + quiet:
return False
return True
def is_init_log(record):
if record["level"].name not in INIT_LEVELS:
return False
if record["level"].no < verbosity + quiet:
return False
return True
def is_msg_log(record):
if record["level"].name not in MESSAGE_LEVELS:
return False
if record["level"].no < verbosity + quiet:
return False
return True
def is_stderr_log(record):
if record["level"].name in STDOUT_LEVELS + INIT_LEVELS + MESSAGE_LEVELS:
return False
if record["level"].no < verbosity + quiet:
return False
return True
def test_logger():
logger.generation(
"This is a generation message\nIt is typically multiline\nThee Lines".encode("unicode_escape").decode("utf-8"),
)
logger.prompt("This is a prompt message")
logger.debug("Debug Message")
logger.info("Info Message")
logger.warning("Info Warning")
logger.error("Error Message")
logger.critical("Critical Message")
logger.init("This is an init message", status="Starting")
logger.init_ok("This is an init message", status="OK")
logger.init_warn("This is an init message", status="Warning")
logger.init_err("This is an init message", status="Error")
logger.message("This is user message")
sys.exit()
logfmt = (
"<level>{level: <10}</level> | <green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
"<green>{name}</green>:<green>{function}</green>:<green>{line}</green> - <level>{message}</level>"
)
genfmt = "<level>{level: <10}</level> @ <green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{message}</level>"
initfmt = "<magenta>INIT </magenta> | <level>{extra[status]: <14}</level> | <magenta>{message}</magenta>"
msgfmt = "<level>{level: <10}</level> | <level>{message}</level>"
logger.level("GENERATION", no=24, color="<cyan>")
logger.level("PROMPT", no=23, color="<yellow>")
logger.level("INIT", no=31, color="<white>")
logger.level("INIT_OK", no=31, color="<green>")
logger.level("INIT_WARN", no=31, color="<yellow>")
logger.level("INIT_ERR", no=31, color="<red>")
logger.level("AUDIT", no=19, color="<blue>")
# Messages contain important information without which this application might not be able to be used
# As such, they have the highest priority
logger.level("MESSAGE", no=61, color="<green>")
logger.__class__.generation = partialmethod(logger.__class__.log, "GENERATION")
logger.__class__.prompt = partialmethod(logger.__class__.log, "PROMPT")
logger.__class__.init = partialmethod(logger.__class__.log, "INIT")
logger.__class__.init_ok = partialmethod(logger.__class__.log, "INIT_OK")
logger.__class__.init_warn = partialmethod(logger.__class__.log, "INIT_WARN")
logger.__class__.init_err = partialmethod(logger.__class__.log, "INIT_ERR")
logger.__class__.message = partialmethod(logger.__class__.log, "MESSAGE")
logger.__class__.audit = partialmethod(logger.__class__.log, "AUDIT")
# Default config with colorize=False (reconfigure_from_args() applies CLI settings)
config = {
"handlers": [
{
"sink": sys.stderr,
"format": logfmt,
"colorize": False,
"filter": is_stderr_log,
},
{
"sink": sys.stdout,
"format": genfmt,
"level": "PROMPT",
"colorize": False,
"filter": is_stdout_log,
},
{
"sink": sys.stdout,
"format": initfmt,
"level": "INIT",
"colorize": False,
"filter": is_init_log,
},
{
"sink": sys.stdout,
"format": msgfmt,
"level": "MESSAGE",
"colorize": False,
"filter": is_msg_log,
},
],
}
logger.configure(**config)
logger.add("horde.log", retention="7 days", rotation="1d", compression="bz2", level=19)
logger.disable("__main__")
logger.warning("disabled")
logger.enable("")
logger.enable(None)
def reconfigure_from_args(args):
"""Apply CLI args to logger. Called from server.py after argparse runs."""
set_logger_verbosity(args.verbosity)
quiesce_logger(args.quiet)
if args.color:
for handler in config["handlers"]:
handler["colorize"] = True
logger.configure(**config)