Files
DECNET/tests/clustering/test_lone_wolf_fixture.py
anti f2b3393669 chore: relicense to AGPL-3.0-or-later and add SPDX headers
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.
2026-05-22 21:04:16 -04:00

76 lines
2.6 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
End-to-end pipeline test for fixture 3 (lone_wolf).
Loads the YAML spec, runs the synthetic generator, applies the
identity-clusterer placeholder (each attacker → its own cluster), and
scores against the expected bounds. This is the simplest of the six
fixtures and is deliberately the first one wired up — its ground truth
is all singletons, so an identity clusterer trivially passes, which
proves the DSL → factory → metrics pipeline works before any real
algorithm is built.
Once the connected-components clusterer (CAMPAIGN_CLUSTERING.md §4)
lands, the same fixture must continue to pass.
"""
from __future__ import annotations
from pathlib import Path
import pytest
from tests.clustering.fixture_harness import (
assert_fixture_bounds,
identity_clusterer,
)
from tests.clustering.metrics import score
from tests.factories.campaign_factory import generate, load_yaml
FIXTURE_DIR = Path(__file__).parent.parent / "fixtures" / "campaigns"
def test_lone_wolf_pipeline_passes_bounds() -> None:
spec = load_yaml(FIXTURE_DIR / "lone_wolf.yaml")
corpus = generate(spec, seed=0)
assert_fixture_bounds(corpus, identity_clusterer, FIXTURE_DIR / "lone_wolf.expected.yaml")
def test_lone_wolf_corpus_shape() -> None:
"""Sanity: 1 wolf + 8 noise scanners = 9 attackers, 9 sessions."""
spec = load_yaml(FIXTURE_DIR / "lone_wolf.yaml")
corpus = generate(spec, seed=0)
assert len(corpus.attackers) == 9
assert len(corpus.sessions) == 9
truth_campaigns = {a.truth_campaign_id for a in corpus.attackers}
assert len(truth_campaigns) == 9
def test_identity_clusterer_fails_on_a_real_campaign() -> None:
"""
Sanity for the harness, NOT a test of the clusterer: a real
multi-actor campaign should make the placeholder identity clusterer
fail completeness, since each truth-campaign gets fragmented into
one-member clusters. If this didn't fail, our metrics would be
blind to false splits — and that's the entire point of fixtures 4
and 5 in the design doc.
"""
spec = {
"campaign": {
"id": "c-real",
"actors": [
{"id": "a-1", "asn": 14061},
{"id": "a-2", "asn": 14061},
],
"phases": [
{"name": "delivery", "actor": "a-1"},
{"name": "discovery", "actor": "a-2"},
],
"duration_days": 1,
}
}
corpus = generate(spec, seed=0)
pred = identity_clusterer(corpus)
metrics = score(corpus.truth_labels(), pred)
assert metrics["completeness"] < 1.0
assert metrics["homogeneity"] == pytest.approx(1.0)