phase 14-15: docker build validation and helm deployment
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
"""Tests for contradiction detection and disagreement representation.
|
||||
|
||||
Requirements: 6.4, 6.5
|
||||
"""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from services.aggregation.contradiction import (
|
||||
CatalystEntry,
|
||||
ContradictionResult,
|
||||
detect_contradictions,
|
||||
)
|
||||
from services.aggregation.scoring import WeightedSignal, compute_signal_weight
|
||||
|
||||
NOW = datetime(2026, 4, 11, 12, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def _sw(doc_id: str, sentiment: float, impact: float = 0.5) -> WeightedSignal:
|
||||
"""Helper to build a WeightedSignal with default scoring."""
|
||||
w = compute_signal_weight(NOW, NOW, "7d", 0.8, extraction_confidence=0.8)
|
||||
return WeightedSignal(doc_id, w, sentiment_value=sentiment, impact_score=impact)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Overall score (backward compat with compute_contradiction_score)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_no_signals_returns_zero():
|
||||
result = detect_contradictions([])
|
||||
assert result.score == 0.0
|
||||
assert result.details == []
|
||||
|
||||
|
||||
def test_all_positive_no_contradiction():
|
||||
signals = [_sw("d1", 1.0), _sw("d2", 1.0)]
|
||||
result = detect_contradictions(signals)
|
||||
assert result.score == 0.0
|
||||
assert len(result.details) == 0
|
||||
|
||||
|
||||
def test_equal_opposing_gives_half():
|
||||
signals = [_sw("d1", 1.0, 0.5), _sw("d2", -1.0, 0.5)]
|
||||
result = detect_contradictions(signals)
|
||||
assert abs(result.score - 0.5) < 1e-4
|
||||
|
||||
|
||||
def test_neutral_signals_ignored():
|
||||
signals = [_sw("d1", 0.0), _sw("d2", 0.0)]
|
||||
result = detect_contradictions(signals)
|
||||
assert result.score == 0.0
|
||||
assert result.details == []
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sentiment disagreement detail
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_sentiment_disagreement_detail_present():
|
||||
signals = [_sw("d1", 1.0, 0.6), _sw("d2", -1.0, 0.4)]
|
||||
result = detect_contradictions(signals)
|
||||
sentiments = [d for d in result.details if d.dimension == "sentiment"]
|
||||
assert len(sentiments) == 1
|
||||
detail = sentiments[0]
|
||||
assert detail.positive_doc_ids == ["d1"]
|
||||
assert detail.negative_doc_ids == ["d2"]
|
||||
assert detail.positive_weight > 0
|
||||
assert detail.negative_weight > 0
|
||||
assert "positive" in detail.description.lower() or "sentiment" in detail.description.lower()
|
||||
|
||||
|
||||
def test_no_sentiment_detail_when_all_agree():
|
||||
signals = [_sw("d1", 1.0), _sw("d2", 1.0)]
|
||||
result = detect_contradictions(signals)
|
||||
sentiments = [d for d in result.details if d.dimension == "sentiment"]
|
||||
assert len(sentiments) == 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Catalyst disagreement detail
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_catalyst_disagreement_detected():
|
||||
signals = [_sw("d1", 1.0, 0.7), _sw("d2", -1.0, 0.5)]
|
||||
entries = [
|
||||
CatalystEntry("d1", "earnings"),
|
||||
CatalystEntry("d2", "earnings"),
|
||||
]
|
||||
result = detect_contradictions(signals, entries)
|
||||
catalyst_details = [d for d in result.details if d.dimension.startswith("catalyst:")]
|
||||
assert len(catalyst_details) == 1
|
||||
assert catalyst_details[0].dimension == "catalyst:earnings"
|
||||
assert catalyst_details[0].positive_doc_ids == ["d1"]
|
||||
assert catalyst_details[0].negative_doc_ids == ["d2"]
|
||||
|
||||
|
||||
def test_no_catalyst_disagreement_when_same_sentiment():
|
||||
signals = [_sw("d1", 1.0), _sw("d2", 1.0)]
|
||||
entries = [
|
||||
CatalystEntry("d1", "earnings"),
|
||||
CatalystEntry("d2", "earnings"),
|
||||
]
|
||||
result = detect_contradictions(signals, entries)
|
||||
catalyst_details = [d for d in result.details if d.dimension.startswith("catalyst:")]
|
||||
assert len(catalyst_details) == 0
|
||||
|
||||
|
||||
def test_catalyst_disagreement_across_types():
|
||||
"""Different catalyst types with internal disagreement each get a detail."""
|
||||
signals = [
|
||||
_sw("d1", 1.0, 0.5),
|
||||
_sw("d2", -1.0, 0.5),
|
||||
_sw("d3", 1.0, 0.5),
|
||||
_sw("d4", -1.0, 0.5),
|
||||
]
|
||||
entries = [
|
||||
CatalystEntry("d1", "earnings"),
|
||||
CatalystEntry("d2", "earnings"),
|
||||
CatalystEntry("d3", "product"),
|
||||
CatalystEntry("d4", "product"),
|
||||
]
|
||||
result = detect_contradictions(signals, entries)
|
||||
catalyst_details = [d for d in result.details if d.dimension.startswith("catalyst:")]
|
||||
dims = {d.dimension for d in catalyst_details}
|
||||
assert "catalyst:earnings" in dims
|
||||
assert "catalyst:product" in dims
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Integration with assemble_trend_summary
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_trend_summary_includes_disagreement_details():
|
||||
"""assemble_trend_summary should populate disagreement_details."""
|
||||
from datetime import timedelta
|
||||
|
||||
from services.aggregation.worker import (
|
||||
ImpactRow,
|
||||
assemble_trend_summary,
|
||||
build_weighted_signals,
|
||||
)
|
||||
|
||||
impacts = [
|
||||
ImpactRow(
|
||||
document_id="d1", confidence=0.8, novelty_score=0.5,
|
||||
source_credibility=0.8, sentiment="positive", impact_score=0.7,
|
||||
catalyst_type="earnings", key_facts=[], risks=[],
|
||||
published_at=NOW - timedelta(hours=1),
|
||||
),
|
||||
ImpactRow(
|
||||
document_id="d2", confidence=0.8, novelty_score=0.5,
|
||||
source_credibility=0.8, sentiment="negative", impact_score=0.7,
|
||||
catalyst_type="earnings", key_facts=[], risks=[],
|
||||
published_at=NOW - timedelta(hours=2),
|
||||
),
|
||||
]
|
||||
signals = build_weighted_signals(impacts, NOW, "7d")
|
||||
summary = assemble_trend_summary("AAPL", "7d", signals, impacts, reference_time=NOW)
|
||||
|
||||
assert summary.contradiction_score > 0
|
||||
assert len(summary.disagreement_details) > 0
|
||||
dims = {d.dimension for d in summary.disagreement_details}
|
||||
assert "sentiment" in dims
|
||||
Reference in New Issue
Block a user