File size: 3,942 Bytes
535dd76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# tests/test_replication_predictor.py
#
# Test suite for Replication Predictor (Module 21).
# 10 tests covering ensemble ML prediction, probability bounds,
# verdict generation, flag structure, and edge cases.
# Note: rf_v2.pkl is local-only — tests handle UNKNOWN fallback.

import pytest
from src.scipeerai.modules.replication_predictor import analyze

_VALID_RISK = ("low", "medium", "high", "critical", "UNKNOWN")


def _make_scores(val=0.0):
    return {
        "score_stat":        val,
        "score_method":      val,
        "score_citation":    val,
        "score_repro":       val,
        "score_novelty":     val,
        "score_grim":        val,
        "score_sprite":      val,
        "score_granularity": val,
        "score_pcurve":      val,
        "score_effect":      val,
        "score_retraction":  val,
        "score_cartel":      val,
        "score_llm":         val,
        "score_fraud":       val,
        "score_temporal":    val,
        "score_dna":         val,
        "score_dataprint":   val,
        "score_peerreview":  val,
        "score_spectrum":    val,
    }


def test_high_risk_scores_run():
    """High risk scores — module runs without raising."""
    scores = _make_scores(0.9)
    r = analyze(scores, text="High risk paper with many fraud signals detected.")
    assert r.replication_probability >= 0.0
    assert r.fraud_probability >= 0.0


def test_low_risk_scores_run():
    """All clean scores — module runs without raising."""
    scores = _make_scores(0.0)
    r = analyze(scores, text="Clean paper with no fraud signals detected.")
    assert r.replication_probability >= 0.0
    assert r.risk_level in _VALID_RISK


def test_replication_probability_bounded():
    """Replication probability always between 0 and 1."""
    scores = _make_scores(0.5)
    r = analyze(scores, text="Mixed signals paper with moderate risk scores.")
    assert 0.0 <= r.replication_probability <= 1.0


def test_fraud_probability_bounded():
    """Fraud probability always between 0 and 1."""
    scores = _make_scores(0.5)
    r = analyze(scores, text="Mixed signals paper with moderate risk scores.")
    assert 0.0 <= r.fraud_probability <= 1.0


def test_probabilities_non_negative():
    """Both probabilities are non-negative in all cases."""
    scores = _make_scores(0.3)
    r = analyze(scores, text="Paper with moderate statistical risk signals.")
    assert r.replication_probability >= 0.0
    assert r.fraud_probability >= 0.0


def test_verdict_is_string():
    """Verdict is always a string regardless of model state."""
    scores = _make_scores(0.4)
    r = analyze(scores, text="Paper under evaluation for replication risk.")
    assert isinstance(r.verdict, str)


def test_flag_structure_complete():
    """Every flag has all five required fields."""
    scores = _make_scores(0.8)
    r = analyze(scores, text="Very high risk paper with extreme fraud signals.")
    for flag in r.flags:
        assert "flag_type"   in flag
        assert "severity"    in flag
        assert "description" in flag
        assert "evidence"    in flag
        assert "suggestion"  in flag


def test_model_version_string():
    """Model version is always a non-empty string."""
    scores = _make_scores(0.2)
    r = analyze(scores, text="Paper with low risk scores across all modules.")
    assert isinstance(r.model_version, str)
    assert len(r.model_version) > 0


def test_summary_not_empty():
    """Summary always returns a non-empty string."""
    scores = _make_scores(0.3)
    r = analyze(scores, text="Paper being evaluated for scientific integrity.")
    assert isinstance(r.summary, str)
    assert len(r.summary) > 10


def test_risk_level_valid():
    """Risk level is always one of the valid values including UNKNOWN."""
    scores = _make_scores(0.5)
    r = analyze(scores, text="Paper with mixed integrity signals throughout.")
    assert r.risk_level in _VALID_RISK