Initial curated RAGcore source from c16667f0c9397e2bfc8dd0255b114fba23c8d05e
This commit is contained in:
commit
7ca726caf2
659 files changed
+98171
No files matched your search
@@ -0,0 +1,166 @@
|
||||
"""Validate compose.production.yaml renders correctly against real Docker
|
||||
Compose -- the exact command the M6-06 backlog task specifies as this
|
||||
task's own validation."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
COMPOSE_TIMEOUT_SECONDS = 60
|
||||
|
||||
|
||||
def _has_docker_compose() -> bool:
|
||||
return shutil.which("docker") is not None
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def production_config() -> dict:
|
||||
if not _has_docker_compose():
|
||||
pytest.skip("docker compose not available")
|
||||
env_path = REPO_ROOT / ".env"
|
||||
created = not env_path.exists()
|
||||
if created:
|
||||
example = (REPO_ROOT / ".env.example").read_text(encoding="utf-8")
|
||||
placeholder_secret = "test-secret-value-with-at-least-32-characters!!"
|
||||
env_path.write_text(
|
||||
example.replace("replace-with-at-least-32-random-characters", placeholder_secret)
|
||||
.replace("replace-me", placeholder_secret)
|
||||
.replace("replace-with-independent-random-pepper", placeholder_secret)
|
||||
.replace("RAGCORE_TRUSTED_PROXY_IPS=\n", "RAGCORE_TRUSTED_PROXY_IPS=172.30.50.2\n"),
|
||||
encoding="utf-8",
|
||||
)
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[ # noqa: S607 -- trusted fixed argv, no untrusted input
|
||||
"docker",
|
||||
"compose",
|
||||
"-f",
|
||||
"compose.yaml",
|
||||
"-f",
|
||||
"compose.production.yaml",
|
||||
"config",
|
||||
],
|
||||
cwd=REPO_ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=COMPOSE_TIMEOUT_SECONDS,
|
||||
check=True,
|
||||
)
|
||||
finally:
|
||||
if created:
|
||||
env_path.unlink(missing_ok=True)
|
||||
return yaml.safe_load(result.stdout)
|
||||
|
||||
|
||||
def test_only_app_and_mcp_publish_a_port(production_config: dict) -> None:
|
||||
# mcp is the other genuinely externally-reachable surface (read-only,
|
||||
# its own bearer auth) -- postgres/qdrant must never be published.
|
||||
services = production_config["services"]
|
||||
published = {name for name, service in services.items() if "ports" in service}
|
||||
assert published == {"app", "mcp"}
|
||||
|
||||
|
||||
def test_app_host_port_is_loopback_only(production_config: dict) -> None:
|
||||
published = production_config["services"]["app"]["ports"]
|
||||
assert published == [
|
||||
{
|
||||
"mode": "ingress",
|
||||
"target": 8080,
|
||||
"published": "8088",
|
||||
"protocol": "tcp",
|
||||
"host_ip": "127.0.0.1",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_app_uses_the_dedicated_external_proxy_network(production_config: dict) -> None:
|
||||
app_networks = production_config["services"]["app"]["networks"]
|
||||
assert "proxy" in app_networks
|
||||
assert "ragcore-app" in app_networks["proxy"]["aliases"]
|
||||
assert production_config["networks"]["proxy"]["external"] is True
|
||||
|
||||
|
||||
def test_production_server_has_no_wildcard_proxy_trust(production_config: dict) -> None:
|
||||
app = production_config["services"]["app"]
|
||||
assert app["command"] == ["python", "-m", "ragcore.main"]
|
||||
assert app["environment"]["RAGCORE_TRUSTED_PROXY_IPS"] == "172.30.50.2"
|
||||
|
||||
|
||||
def test_backend_network_stays_internal(production_config: dict) -> None:
|
||||
assert production_config["networks"]["backend"]["internal"] is True
|
||||
|
||||
|
||||
def test_app_worker_and_mcp_have_resource_limits(production_config: dict) -> None:
|
||||
for name in ("app", "worker", "mcp"):
|
||||
limits = production_config["services"][name]["deploy"]["resources"]["limits"]
|
||||
assert limits["cpus"]
|
||||
assert limits["memory"]
|
||||
|
||||
|
||||
def test_app_worker_and_mcp_run_read_only_with_no_new_privileges(production_config: dict) -> None:
|
||||
for name in ("app", "worker", "mcp"):
|
||||
service = production_config["services"][name]
|
||||
assert service["read_only"] is True
|
||||
assert "no-new-privileges:true" in service["security_opt"]
|
||||
|
||||
|
||||
def test_app_worker_and_mcp_load_values_from_the_real_env_file(production_config: dict) -> None:
|
||||
# docker compose config resolves env_file directives into `environment`
|
||||
# rather than preserving an `env_file` key, so this proves the fixture's
|
||||
# generated .env (not the insecure .env.example placeholders) actually
|
||||
# supplied the value.
|
||||
for name in ("app", "worker", "mcp"):
|
||||
env = production_config["services"][name]["environment"]
|
||||
assert env["RAGCORE_SECRET_KEY"] == "test-secret-value-with-at-least-32-characters!!"
|
||||
|
||||
|
||||
def test_app_worker_and_mcp_are_forced_into_production_mode(production_config: dict) -> None:
|
||||
# .env.example (still loaded by the base compose.yaml for local-dev
|
||||
# convenience) sets RAGCORE_ENV=development; the production overlay must
|
||||
# force it back to "production" regardless, or an operator who copies
|
||||
# .env.example to .env without touching RAGCORE_ENV would silently
|
||||
# deploy with every fail-closed Settings check disabled.
|
||||
for name in ("app", "worker", "mcp"):
|
||||
env = production_config["services"][name]["environment"]
|
||||
assert env["RAGCORE_ENV"] == "production"
|
||||
|
||||
|
||||
def test_postgres_and_qdrant_never_publish_a_port_in_the_base_topology() -> None:
|
||||
if not _has_docker_compose():
|
||||
pytest.skip("docker compose not available")
|
||||
result = subprocess.run(
|
||||
["docker", "compose", "-f", "compose.yaml", "config"], # noqa: S607
|
||||
cwd=REPO_ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=COMPOSE_TIMEOUT_SECONDS,
|
||||
check=True,
|
||||
)
|
||||
services = yaml.safe_load(result.stdout)["services"]
|
||||
for name in ("postgres", "qdrant"):
|
||||
assert "ports" not in services[name]
|
||||
|
||||
|
||||
def test_production_config_fails_closed_without_a_real_env_file() -> None:
|
||||
if not _has_docker_compose():
|
||||
pytest.skip("docker compose not available")
|
||||
env_path = REPO_ROOT / ".env"
|
||||
assert not env_path.exists(), "a real .env exists; cannot prove the fail-closed path"
|
||||
result = subprocess.run(
|
||||
["docker", "compose", "-f", "compose.yaml", "-f", "compose.production.yaml", "config"], # noqa: S607
|
||||
cwd=REPO_ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=COMPOSE_TIMEOUT_SECONDS,
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode != 0
|
||||
assert ".env" in result.stderr
|
||||
@@ -0,0 +1,48 @@
|
||||
"""Release artifacts must not masquerade as something they are not.
|
||||
|
||||
* No ``SHA256SUMS`` at the repository root: the only checksum manifest ever
|
||||
kept there described the 2026-08-01 blueprint package, not the working
|
||||
tree, and now lives under ``docs/archive/blueprint-package/``.
|
||||
* Any ``dist/ragcore-v<version>.zip`` directly under ``dist/`` must carry the
|
||||
current release version; stale local zips belong in ``dist/archive/``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def _current_version() -> str:
|
||||
with (REPO_ROOT / "pyproject.toml").open("rb") as handle:
|
||||
return str(tomllib.load(handle)["project"]["version"])
|
||||
|
||||
|
||||
def test_no_stale_checksum_manifest_at_the_repository_root() -> None:
|
||||
assert not (REPO_ROOT / "SHA256SUMS").exists(), (
|
||||
"a root SHA256SUMS looks like it validates the working tree; archive it under "
|
||||
"docs/archive/ or regenerate it as part of a real release"
|
||||
)
|
||||
archived = REPO_ROOT / "docs" / "archive" / "blueprint-package" / "SHA256SUMS"
|
||||
assert archived.is_file()
|
||||
assert (archived.parent / "README.md").is_file(), "the archived manifest must be explained"
|
||||
|
||||
|
||||
def test_top_level_dist_archives_match_the_current_version() -> None:
|
||||
dist = REPO_ROOT / "dist"
|
||||
if not dist.is_dir():
|
||||
return
|
||||
version = _current_version()
|
||||
pattern = re.compile(r"^ragcore-v(?P<version>\d+\.\d+\.\d+)\.zip$")
|
||||
stale = [
|
||||
path.name
|
||||
for path in dist.iterdir()
|
||||
if path.is_file() and (match := pattern.match(path.name)) and match["version"] != version
|
||||
]
|
||||
assert not stale, (
|
||||
f"dist/ holds artifacts of another release than {version}: {stale}; move them to "
|
||||
"dist/archive/ (see dist/README.md)"
|
||||
)
|
||||
@@ -0,0 +1,145 @@
|
||||
"""Unit tests for scripts/unraid_preflight.py's individual checks.
|
||||
|
||||
The one hard blocking acceptance criterion from the M6-06 backlog task --
|
||||
"Preflight blocks unsafe /mnt/user active state" -- is tested directly and
|
||||
exhaustively; the rest are tested for correct pass/fail/advisory shape.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "scripts"))
|
||||
|
||||
import unraid_preflight as preflight
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"state_root",
|
||||
[
|
||||
Path("/mnt/pool/appdata/ragcore"),
|
||||
Path("/mnt/user"),
|
||||
Path("/mnt/pool/appdata/ragcore/postgres"),
|
||||
],
|
||||
)
|
||||
def test_state_root_on_user_share_is_blocked(state_root: Path) -> None:
|
||||
result = preflight.check_state_root_not_user_share(state_root)
|
||||
assert result.blocking is True
|
||||
assert result.passed is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"state_root",
|
||||
[
|
||||
Path("/mnt/cache/appdata/ragcore"),
|
||||
Path("/mnt/pool1/appdata/ragcore"),
|
||||
Path("/srv/ragcore/state"),
|
||||
],
|
||||
)
|
||||
def test_state_root_on_direct_pool_path_is_allowed(state_root: Path) -> None:
|
||||
result = preflight.check_state_root_not_user_share(state_root)
|
||||
assert result.blocking is True
|
||||
assert result.passed is True
|
||||
|
||||
|
||||
def test_disk_space_check_fails_when_below_threshold(tmp_path: Path) -> None:
|
||||
result = preflight.check_disk_space(tmp_path, min_free_gb=10**9)
|
||||
assert result.blocking is True
|
||||
assert result.passed is False
|
||||
|
||||
|
||||
def test_disk_space_check_passes_with_trivial_threshold(tmp_path: Path) -> None:
|
||||
result = preflight.check_disk_space(tmp_path, min_free_gb=0.0)
|
||||
assert result.passed is True
|
||||
|
||||
|
||||
def test_backup_target_writable_creates_and_uses_directory(tmp_path: Path) -> None:
|
||||
target = tmp_path / "backups" / "ragcore"
|
||||
result = preflight.check_backup_target(target)
|
||||
assert result.passed is True
|
||||
assert target.is_dir()
|
||||
|
||||
|
||||
def test_backup_target_not_writable_fails(tmp_path: Path) -> None:
|
||||
if not hasattr(__import__("os"), "getuid") or __import__("os").getuid() == 0:
|
||||
pytest.skip("cannot simulate a permission failure as root or on non-POSIX")
|
||||
target = tmp_path / "locked"
|
||||
target.mkdir()
|
||||
target.chmod(0o400)
|
||||
try:
|
||||
result = preflight.check_backup_target(target / "sub")
|
||||
assert result.passed is False
|
||||
finally:
|
||||
target.chmod(0o700)
|
||||
|
||||
|
||||
def test_model_endpoint_check_is_advisory_when_not_given() -> None:
|
||||
result = preflight.check_model_endpoint(None)
|
||||
assert result.blocking is False
|
||||
assert result.passed is True
|
||||
|
||||
|
||||
def test_model_endpoint_check_fails_closed_but_advisory_on_unreachable_host() -> None:
|
||||
result = preflight.check_model_endpoint("http://127.0.0.1:1")
|
||||
assert result.blocking is False
|
||||
assert result.passed is False
|
||||
|
||||
|
||||
def test_reverse_proxy_check_is_advisory_when_not_given() -> None:
|
||||
result = preflight.check_reverse_proxy_headers(None)
|
||||
assert result.blocking is False
|
||||
assert result.passed is True
|
||||
|
||||
|
||||
def test_run_all_blocks_on_unsafe_state_root(tmp_path: Path) -> None:
|
||||
results = preflight.run_all(
|
||||
state_root=Path("/mnt/pool/appdata/ragcore"),
|
||||
backup_dir=tmp_path / "backups",
|
||||
ollama_url=None,
|
||||
base_url=None,
|
||||
)
|
||||
names = {r.name: r for r in results}
|
||||
assert names["state root is not /mnt/user"].passed is False
|
||||
assert names["state root is not /mnt/user"].blocking is True
|
||||
|
||||
|
||||
def test_main_exits_nonzero_when_state_root_is_unsafe(tmp_path: Path, monkeypatch) -> None:
|
||||
monkeypatch.setattr(
|
||||
sys,
|
||||
"argv",
|
||||
[
|
||||
"unraid_preflight.py",
|
||||
"--state-root",
|
||||
"/mnt/pool/appdata/ragcore",
|
||||
"--backup-dir",
|
||||
str(tmp_path / "backups"),
|
||||
],
|
||||
)
|
||||
assert preflight.main() == 1
|
||||
|
||||
|
||||
def test_main_exits_zero_for_a_safe_configuration(tmp_path: Path, monkeypatch) -> None:
|
||||
state_root = tmp_path / "pool" / "appdata" / "ragcore"
|
||||
state_root.mkdir(parents=True)
|
||||
monkeypatch.setattr(
|
||||
sys,
|
||||
"argv",
|
||||
[
|
||||
"unraid_preflight.py",
|
||||
"--state-root",
|
||||
str(state_root),
|
||||
"--backup-dir",
|
||||
str(tmp_path / "backups"),
|
||||
],
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
preflight,
|
||||
"check_ownership",
|
||||
lambda *_a, **_k: preflight.CheckResult(
|
||||
"state root ownership", blocking=True, passed=True, detail="skipped for test"
|
||||
),
|
||||
)
|
||||
assert preflight.main() == 0
|
||||
@@ -0,0 +1,115 @@
|
||||
"""One release version, everywhere it's hand-maintained.
|
||||
|
||||
Several of these are genuinely independent sources (pyproject.toml files,
|
||||
package.json, two hand-written SDK version constants that each carry their
|
||||
own "kept in sync by hand" comment) -- nothing generates them from a single
|
||||
source today, so this is the guard against them silently drifting apart.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def _toml_version(path: Path) -> str:
|
||||
with path.open("rb") as handle:
|
||||
document = tomllib.load(handle)
|
||||
return str(document["project"]["version"])
|
||||
|
||||
|
||||
def _package_json_version(path: Path) -> str:
|
||||
document = json.loads(path.read_text(encoding="utf-8"))
|
||||
return str(document["version"])
|
||||
|
||||
|
||||
def _package_lock_versions(path: Path) -> dict[str, str]:
|
||||
"""Both places an npm lockfile records the package's own version.
|
||||
|
||||
A lockfile is generated, but only by a command someone has to run: editing
|
||||
``package.json`` by hand leaves it behind, which is exactly what happened
|
||||
(``package.json`` said 1.2.0 while the lockfile still said 0.1.0 -- the
|
||||
version this package carried before its first release). It states the
|
||||
version twice, at the document root and in the root entry of ``packages``,
|
||||
and both are what a consumer or a CI cache reads, so both are checked.
|
||||
"""
|
||||
|
||||
document = json.loads(path.read_text(encoding="utf-8"))
|
||||
return {
|
||||
"sdk/typescript/package-lock.json": str(document["version"]),
|
||||
'sdk/typescript/package-lock.json packages[""]': str(document["packages"][""]["version"]),
|
||||
}
|
||||
|
||||
|
||||
def _uv_lock_versions(path: Path) -> dict[str, str]:
|
||||
"""The workspace members' versions as the resolved Python lockfile has
|
||||
them.
|
||||
|
||||
``uv`` rewrites this whenever it syncs, so it normally heals itself --
|
||||
which is exactly why a stale entry is worth asserting on: it means the
|
||||
committed lockfile was produced before the version bump and nobody has run
|
||||
the project's own tooling since.
|
||||
"""
|
||||
|
||||
with path.open("rb") as handle:
|
||||
document = tomllib.load(handle)
|
||||
members = {"ragcore", "ragcore-sdk"}
|
||||
return {
|
||||
f"uv.lock [{package['name']}]": str(package["version"])
|
||||
for package in document["package"]
|
||||
if package["name"] in members
|
||||
}
|
||||
|
||||
|
||||
def _openapi_version(path: Path) -> str:
|
||||
document = yaml.safe_load(path.read_text(encoding="utf-8"))
|
||||
return str(document["info"]["version"])
|
||||
|
||||
|
||||
def _constant_version(path: Path, pattern: str) -> str:
|
||||
match = re.search(pattern, path.read_text(encoding="utf-8"))
|
||||
assert match is not None, f"could not find a version constant in {path}"
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def test_every_hand_maintained_version_string_matches() -> None:
|
||||
versions = {
|
||||
"pyproject.toml": _toml_version(REPO_ROOT / "pyproject.toml"),
|
||||
"src/ragcore/__init__.py": _constant_version(
|
||||
REPO_ROOT / "src" / "ragcore" / "__init__.py", r'__version__ = "([^"]+)"'
|
||||
),
|
||||
"sdk/python/pyproject.toml": _toml_version(REPO_ROOT / "sdk" / "python" / "pyproject.toml"),
|
||||
"sdk/python/src/ragcore_sdk/_version.py": _constant_version(
|
||||
REPO_ROOT / "sdk" / "python" / "src" / "ragcore_sdk" / "_version.py",
|
||||
r'__version__ = "([^"]+)"',
|
||||
),
|
||||
"sdk/typescript/package.json": _package_json_version(
|
||||
REPO_ROOT / "sdk" / "typescript" / "package.json"
|
||||
),
|
||||
**_package_lock_versions(REPO_ROOT / "sdk" / "typescript" / "package-lock.json"),
|
||||
"sdk/typescript/src/client.ts": _constant_version(
|
||||
REPO_ROOT / "sdk" / "typescript" / "src" / "client.ts",
|
||||
r'_SDK_VERSION = "([^"]+)"',
|
||||
),
|
||||
**_uv_lock_versions(REPO_ROOT / "uv.lock"),
|
||||
# The binding REST contract carries the release version too; it used
|
||||
# to lag behind (1.0.0 while the code was 1.1.0).
|
||||
"docs/contracts/openapi.yaml": _openapi_version(
|
||||
REPO_ROOT / "docs" / "contracts" / "openapi.yaml"
|
||||
),
|
||||
}
|
||||
|
||||
distinct = set(versions.values())
|
||||
assert len(distinct) == 1, f"version strings have diverged: {versions}"
|
||||
|
||||
|
||||
def test_the_shared_version_is_the_current_release() -> None:
|
||||
with (REPO_ROOT / "pyproject.toml").open("rb") as handle:
|
||||
document = tomllib.load(handle)
|
||||
assert document["project"]["version"] == "1.2.1"
|
||||
Reference in new issue
Block a user