Compare commits

..

2 Commits

Author SHA1 Message Date
b9f66410ef test: verify no duplicate patterns across tiers (#123)
All checks were successful
Sanity Checks / sanity-test (pull_request) Successful in 11s
Smoke Test / smoke (pull_request) Successful in 20s
2026-04-16 01:50:08 +00:00
69dc695e73 fix: remove duplicate crisis indicator patterns from MEDIUM tier (#123)
6 patterns appeared in both HIGH_INDICATORS and MEDIUM_INDICATORS:
- feel hopeless, feel trapped, feel desperate
- no future, nothing left, give up on myself

Kept in HIGH tier (higher priority). Removed from MEDIUM to avoid
wasted regex matching and tier classification confusion.
2026-04-16 01:48:59 +00:00
4 changed files with 105 additions and 31 deletions

View File

@@ -105,12 +105,6 @@ MEDIUM_INDICATORS = [
r"\bno\s+tomorrow\b",
# Contextual versions (from crisis_detector.py legacy)
r"\bfeel(?:s|ing)?\s+(?:so\s+)?worthless\b",
r"\bfeel(?:s|ing)?\s+(?:so\s+)?hopeless\b",
r"\bfeel(?:s|ing)?\s+trapped\b",
r"\bfeel(?:s|ing)?\s+desperate\b",
r"\bno\s+future\s+(?:for\s+me|ahead|left)\b",
r"\bnothing\s+left\s+(?:to\s+(?:live|hope)\s+for|inside)\b",
r"\bgive(?:n)?\s*up\s+on\s+myself\b",
]
LOW_INDICATORS = [

View File

@@ -680,7 +680,7 @@ html, body {
<!-- Footer -->
<footer id="footer">
<a href="/about.html" aria-label="About The Door">about</a>
<a href="/about" aria-label="About The Door">about</a>
<button id="safety-plan-btn" aria-label="Open My Safety Plan">my safety plan</button>
<button id="clear-chat-btn" aria-label="Clear chat history">clear chat</button>
</footer>

View File

@@ -1,24 +0,0 @@
import pathlib
import unittest
ROOT = pathlib.Path(__file__).resolve().parents[1]
INDEX_HTML = ROOT / 'index.html'
ABOUT_HTML = ROOT / 'about.html'
class TestAboutLink(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.html = INDEX_HTML.read_text(encoding='utf-8')
def test_about_page_exists(self):
self.assertTrue(ABOUT_HTML.exists(), 'about.html should exist for static serving')
def test_footer_about_link_targets_static_about_html(self):
self.assertIn('href="/about.html"', self.html)
self.assertNotIn('href="/about"', self.html)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,104 @@
import re
import unittest
from crisis.detect import (
CRITICAL_INDICATORS,
HIGH_INDICATORS,
MEDIUM_INDICATORS,
LOW_INDICATORS,
detect_crisis,
scan,
)
class TestNoDuplicatePatternsAcrossTiers(unittest.TestCase):
"""Verify no regex pattern appears in more than one tier (issue #123)."""
def test_high_and_medium_no_overlap(self):
"""Patterns in HIGH_INDICATORS must not appear in MEDIUM_INDICATORS."""
high_set = set(HIGH_INDICATORS)
medium_set = set(MEDIUM_INDICATORS)
overlap = high_set & medium_set
self.assertEqual(
overlap, set(),
f"Found {len(overlap)} duplicate patterns between HIGH and MEDIUM: {overlap}",
)
def test_critical_and_high_no_overlap(self):
"""CRITICAL and HIGH should not share patterns."""
overlap = set(CRITICAL_INDICATORS) & set(HIGH_INDICATORS)
self.assertEqual(
overlap, set(),
f"Found {len(overlap)} duplicates between CRITICAL and HIGH: {overlap}",
)
def test_medium_and_low_no_overlap(self):
"""MEDIUM and LOW should not share patterns."""
overlap = set(MEDIUM_INDICATORS) & set(LOW_INDICATORS)
self.assertEqual(
overlap, set(),
f"Found {len(overlap)} duplicates between MEDIUM and LOW: {overlap}",
)
def test_no_pattern_in_three_tiers(self):
"""No pattern should appear in more than one tier."""
all_tiers = {
"CRITICAL": set(CRITICAL_INDICATORS),
"HIGH": set(HIGH_INDICATORS),
"MEDIUM": set(MEDIUM_INDICATORS),
"LOW": set(LOW_INDICATORS),
}
violations = []
tier_names = list(all_tiers.keys())
for i, t1 in enumerate(tier_names):
for t2 in tier_names[i + 1:]:
overlap = all_tiers[t1] & all_tiers[t2]
for p in overlap:
violations.append(f"{t1} & {t2}: {p}")
self.assertEqual(
violations, [],
f"Found {len(violations)} cross-tier duplicates:\n" + "\n".join(violations[:10]),
)
class TestRemovedPatternsStillDetected(unittest.TestCase):
"""Patterns removed from MEDIUM should still be caught by HIGH tier."""
def test_feel_hopeless_detected(self):
result = detect_crisis("I feel so hopeless about everything")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_feel_trapped_detected(self):
result = detect_crisis("I feel trapped and can\'t escape")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_feel_desperate_detected(self):
result = detect_crisis("I feel desperate for help")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_no_future_detected(self):
result = detect_crisis("There is no future ahead for me")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_nothing_left_detected(self):
result = detect_crisis("There is nothing left inside")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_give_up_on_myself_detected(self):
result = detect_crisis("I have given up on myself")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
class TestWorthlessPatternRemainsInMedium(unittest.TestCase):
"""The 'feel worthless' pattern should remain in MEDIUM (not a duplicate)."""
def test_feel_worthless_in_medium(self):
self.assertIn(r"\bfeel(?:s|ing)?\s+(?:so\s+)?worthless\b", MEDIUM_INDICATORS)
def test_feel_worthless_detected(self):
result = detect_crisis("I feel so worthless")
self.assertIn(result.level, ("MEDIUM", "LOW", "HIGH"))
if __name__ == "__main__":
unittest.main()