Replaces LICENSE (GPLv3 -> AGPLv3) and prepends `SPDX-License-Identifier: AGPL-3.0-or-later` to every source file across decnet/, decnet_web/, tests/, scripts/, and tools/. Rationale: closes the GPLv3 ASP loophole so any party operating a modified DECNET as a network service must offer their modified source. Personal copyright (Samuel Paschuan) + inbound=outbound contributions make a future unilateral relicense infeasible. - LICENSE: full AGPL-3.0 text (gnu.org/licenses/agpl-3.0.txt) - COPYRIGHT: project copyright notice - tools/add_spdx_headers.py: idempotent header injector (shebang- and PEP 263-aware) Touches 1565 source files (.py, .ts, .tsx, .js, .jsx, .css, .sh). No behavior change; comments only.
110 lines
4.0 KiB
Python
110 lines
4.0 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
"""Parametrized ``rule_store`` fixture for E.2.14b.
|
|
|
|
The conformance contract from ``development/TTP_TAGGING.md`` §E.2.14b:
|
|
both backends — :class:`FilesystemRuleStore` and
|
|
:class:`DatabaseRuleStore` — must satisfy the same observable
|
|
behavior. Tests that consume :func:`rule_store` are run twice, once
|
|
per backend.
|
|
|
|
Filesystem is skipped on non-Linux (it raises ``RuntimeError`` from
|
|
``__init__`` on macOS / Windows because the inotify dep is
|
|
Linux-only).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import AsyncIterator
|
|
|
|
import pytest
|
|
import pytest_asyncio
|
|
|
|
from decnet.ttp.store.base import RuleStore
|
|
from decnet.ttp.store.impl.database import DatabaseRuleStore
|
|
from decnet.ttp.store.impl.filesystem import FilesystemRuleStore
|
|
from decnet.web.db.models import TTPRule
|
|
|
|
|
|
async def _seed_rule_filesystem(
|
|
store: FilesystemRuleStore, rule_id: str, yaml_text: str,
|
|
) -> None:
|
|
rules_dir: Path = store._rules_dir
|
|
rules_dir.mkdir(parents=True, exist_ok=True)
|
|
(rules_dir / f"{rule_id}.yaml").write_text(yaml_text, encoding="utf-8")
|
|
|
|
|
|
async def _seed_rule_database(
|
|
store: DatabaseRuleStore, rule_id: str, yaml_text: str,
|
|
) -> None:
|
|
# Direct ``ttp_rule`` insert — bypass the master sync helper to
|
|
# keep tests deterministic. Mirrors what a swarm master would have
|
|
# written into the table.
|
|
repo = await store._ensure_repo()
|
|
async with repo._session() as session: # type: ignore[attr-defined]
|
|
from datetime import datetime, timezone # noqa: PLC0415
|
|
|
|
session.add(TTPRule(
|
|
rule_id=rule_id,
|
|
rule_version=1,
|
|
source_path=f"./rules/ttp/{rule_id}.yaml",
|
|
yaml_content=yaml_text,
|
|
updated_at=datetime.now(timezone.utc),
|
|
updated_by="test",
|
|
))
|
|
await session.commit()
|
|
|
|
|
|
async def seed_rule(store: RuleStore, rule_id: str, yaml_text: str) -> None:
|
|
"""Backend-aware test helper: write a rule into the store.
|
|
|
|
Filesystem store: drop a YAML file under ``_rules_dir``.
|
|
Database store: insert a ``ttp_rule`` row directly.
|
|
"""
|
|
if isinstance(store, FilesystemRuleStore):
|
|
await _seed_rule_filesystem(store, rule_id, yaml_text)
|
|
elif isinstance(store, DatabaseRuleStore):
|
|
await _seed_rule_database(store, rule_id, yaml_text)
|
|
else: # pragma: no cover
|
|
raise TypeError(f"unknown rule store backend: {type(store).__name__}")
|
|
|
|
|
|
@pytest_asyncio.fixture(
|
|
params=["filesystem", "database"],
|
|
ids=["filesystem", "database"],
|
|
)
|
|
async def rule_store(
|
|
request: pytest.FixtureRequest, tmp_path: Path,
|
|
) -> AsyncIterator[RuleStore]:
|
|
"""Yield a fresh :class:`RuleStore` instance per parametrization.
|
|
|
|
The filesystem backend is constructed against a ``tmp_path``
|
|
rules dir so tests never touch the real ``./rules/``. The
|
|
database backend gets a per-test SQLite repo (initialized with
|
|
``metadata.create_all``) so each test sees an empty
|
|
``ttp_rule`` / ``ttp_rule_state`` pair.
|
|
"""
|
|
backend = request.param
|
|
if backend == "filesystem":
|
|
if sys.platform != "linux":
|
|
pytest.skip("FilesystemRuleStore requires Linux (inotify)")
|
|
yield FilesystemRuleStore(rules_dir=tmp_path)
|
|
else:
|
|
from decnet.web.db.sqlite.repository import SQLiteRepository # noqa: PLC0415
|
|
|
|
repo = SQLiteRepository(db_path=str(tmp_path / "ttp_store.db"))
|
|
await repo.initialize()
|
|
store = DatabaseRuleStore(repo=repo)
|
|
# Mirror FS store's ``_rules_dir`` attr so cross-backend tests
|
|
# that need to drop sample YAML on disk have somewhere to put
|
|
# it; the DB-backend tests that need rule definitions either
|
|
# write to ``ttp_rule`` directly or call ``upsert_rule``.
|
|
store._rules_dir = tmp_path # type: ignore[attr-defined]
|
|
try:
|
|
yield store
|
|
finally:
|
|
try:
|
|
await repo.engine.dispose()
|
|
except Exception: # noqa: BLE001 — teardown best-effort
|
|
pass
|