Python · 4173 bytes Raw Blame History
1 """Tests for the extended (9-dim) style fingerprint.
2
3 The extended path requires the ``style`` extra (spaCy + textstat).
4 We don't gate the test on the extra being installed — instead we test
5 that:
6
7 1. ``extended=False`` always returns 6 dims (backward-compat with the
8 v1 fingerprint contract).
9 2. ``extended=True`` returns 9 dims **when** ``_has_style_extra()`` is
10 true; the test patches the extra-detection to simulate both states.
11 3. The probe's ``extended="on"`` SKIPs cleanly when the extra is
12 missing rather than crashing.
13 """
14
15 from __future__ import annotations
16
17 from unittest.mock import patch
18
19 import numpy as np
20
21 from dlm_sway.backends.dummy import DummyDifferentialBackend, DummyResponses
22 from dlm_sway.core.result import Verdict
23 from dlm_sway.probes.base import RunContext, build_probe
24 from dlm_sway.probes.style_fingerprint import (
25 FINGERPRINT_SCHEMA_VERSION,
26 fingerprint,
27 )
28
29
30 def _backend_with_generations(prompts: list[str]) -> DummyDifferentialBackend:
31 base = {p: f"base {p} response with several words and a period." for p in prompts}
32 ft = {p: f"ft {p} response, longer, with extra punctuation, more words." for p in prompts}
33 return DummyDifferentialBackend(
34 base=DummyResponses(generations=base),
35 ft=DummyResponses(generations=ft),
36 )
37
38
39 class TestFingerprintFunction:
40 def test_default_is_6_dim(self) -> None:
41 fp = fingerprint("Hello there. This is a test sentence. Another one follows.")
42 assert fp.shape == (6,)
43
44 def test_empty_text_returns_zeros_at_requested_dim(self) -> None:
45 assert fingerprint("", extended=False).shape == (6,)
46 assert fingerprint("", extended=True).shape == (9,)
47
48 def test_extended_falls_back_when_extra_missing(self) -> None:
49 with patch("dlm_sway.probes.style_fingerprint._extended_fingerprint", return_value=None):
50 fp = fingerprint("Some text. Another sentence.", extended=True)
51 assert fp.shape == (6,)
52
53
54 class TestProbeExtendedOnRequiresExtra:
55 def test_extended_on_skips_without_extra(self) -> None:
56 prompts = ["p1"]
57 backend = _backend_with_generations(prompts)
58 probe, spec = build_probe(
59 {
60 "name": "sf",
61 "kind": "style_fingerprint",
62 "prompts": prompts,
63 "doc_reference": "The reference document. Has some sentences. Three of them.",
64 "extended": "on",
65 }
66 )
67 ctx = RunContext(backend=backend)
68 with patch("dlm_sway.probes.style_fingerprint._has_style_extra", return_value=False):
69 result = probe.run(spec, ctx)
70 assert result.verdict == Verdict.SKIP
71 assert "style" in result.message.lower()
72
73
74 class TestProbeExtendedAuto:
75 def test_auto_off_when_extra_missing(self) -> None:
76 prompts = ["p1"]
77 backend = _backend_with_generations(prompts)
78 probe, spec = build_probe(
79 {
80 "name": "sf",
81 "kind": "style_fingerprint",
82 "prompts": prompts,
83 "doc_reference": "The reference document. Has some sentences.",
84 "extended": "auto",
85 }
86 )
87 ctx = RunContext(backend=backend)
88 with patch("dlm_sway.probes.style_fingerprint._has_style_extra", return_value=False):
89 result = probe.run(spec, ctx)
90 assert result.evidence.get("extended") is False
91 assert len(result.evidence["base_fp"]) == 6
92 assert result.evidence["schema_version"] == FINGERPRINT_SCHEMA_VERSION
93
94
95 class TestExtendedProducesNineDimWhenSimulated:
96 def test_extended_path_returns_9_dim(self) -> None:
97 """Patch ``_extended_fingerprint`` to return a 9-vector and confirm
98 ``fingerprint(..., extended=True)`` passes it through unchanged."""
99 nine = np.arange(9, dtype=np.float64) / 9.0
100 with patch(
101 "dlm_sway.probes.style_fingerprint._extended_fingerprint",
102 return_value=nine,
103 ):
104 fp = fingerprint("Some text. Another sentence.", extended=True)
105 assert fp.shape == (9,)
106 np.testing.assert_array_equal(fp, nine)