chore: FSD stabilization, strict TDD enforcement, and unlearn mechanism
- implemented self-healing unlearn for Qdrant false positives - centralized testing logic in conftest - documented core rules, ai standards, and goap philosophy - purged old dev scratchpads
This commit is contained in:
@@ -8,30 +8,32 @@ from GramAddict.core.bot_flow import _extract_post_content, _run_zero_latency_fe
|
||||
|
||||
class TestBotFlowEdgeCases:
|
||||
|
||||
def test_extract_post_content_edge_cases(self):
|
||||
# 1. Empty string / Invalid XML should not crash
|
||||
@patch('GramAddict.core.telepathic_engine.TelepathicEngine.get_instance')
|
||||
def test_extract_post_content_edge_cases(self, mock_get_telepathic):
|
||||
mock_engine = MagicMock()
|
||||
mock_get_telepathic.return_value = mock_engine
|
||||
|
||||
# 1. Empty string / Invalid XML should not crash (mock finds nothing)
|
||||
mock_engine.find_best_node.return_value = None
|
||||
res = _extract_post_content("")
|
||||
assert res.get("username") == ""
|
||||
assert res.get("description") == ""
|
||||
|
||||
# 2. Extract when only username exists
|
||||
xml = "<node resource-id='com.instagram.android:id/row_feed_photo_profile_name' text='just_user'/>"
|
||||
res = _extract_post_content(xml)
|
||||
# Side effect: first call (author) returns node, second (media) returns None
|
||||
mock_engine.find_best_node.side_effect = [{"original_attribs": {"text": "just_user"}}, None]
|
||||
res = _extract_post_content("<xml/>")
|
||||
assert res.get("username") == "just_user"
|
||||
assert res.get("description") == ""
|
||||
|
||||
# 3. Extract when emoji only in description
|
||||
xml = "<node resource-id='com.instagram.android:id/row_feed_photo_imageview' content-desc='🔥🔥🔥'/>"
|
||||
res = _extract_post_content(xml)
|
||||
# However, bot_flow requires len(desc) > 10!
|
||||
# So "🔥🔥🔥" will NOT be extracted if it's too short. Let's provide a long text.
|
||||
xml = "<node resource-id='com.instagram.android:id/row_feed_photo_imageview' content-desc='🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥'/>"
|
||||
res = _extract_post_content(xml)
|
||||
# 3. Extract description
|
||||
mock_engine.find_best_node.side_effect = [None, {"original_attribs": {"desc": "🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥"}}]
|
||||
res = _extract_post_content("<xml/>")
|
||||
assert res.get("description") == "🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥"
|
||||
|
||||
# 4. Another valid description tag
|
||||
xml = "<node resource-id='com.instagram.android:id/clips_media_component' content-desc='some desc with more than 10 chars limits'/>"
|
||||
res = _extract_post_content(xml)
|
||||
mock_engine.find_best_node.side_effect = [None, {"original_attribs": {"desc": "some desc with more than 10 chars limits"}}]
|
||||
res = _extract_post_content("<xml/>")
|
||||
assert res.get("description") == "some desc with more than 10 chars limits"
|
||||
|
||||
@patch('GramAddict.core.bot_flow.random.random', return_value=0.5)
|
||||
|
||||
@@ -104,8 +104,10 @@ def test_empty_content_extraction_guard(test_dumps):
|
||||
broken_xml = mutate_xml_to_foreign(test_dumps["post"])
|
||||
device.dump_hierarchy.return_value = broken_xml
|
||||
|
||||
from GramAddict.core.situational_awareness import SituationType
|
||||
with patch('GramAddict.core.bot_flow._humanized_scroll') as mock_scroll, \
|
||||
patch('GramAddict.core.bot_flow.sleep'):
|
||||
patch('GramAddict.core.bot_flow.sleep'), \
|
||||
patch('GramAddict.core.situational_awareness.SituationalAwarenessEngine.perceive', return_value=SituationType.NORMAL):
|
||||
|
||||
result = _run_zero_latency_feed_loop(device, None, nav_graph, configs, MagicMock(), "HomeFeed", cognitive_stack)
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ from GramAddict.core.telepathic_engine import TelepathicEngine
|
||||
class TestTrapEscape(unittest.TestCase):
|
||||
@patch('GramAddict.core.q_nav_graph.time.sleep', return_value=None)
|
||||
@patch('GramAddict.core.q_nav_graph.random_sleep', return_value=None)
|
||||
@patch('GramAddict.core.situational_awareness.random_sleep', return_value=None)
|
||||
def test_trap_guard_autonomous_ai_escape(self, mock_sae_sleep, mock_q_rand_sleep, mock_q_sleep):
|
||||
@patch('GramAddict.core.situational_awareness.SituationalAwarenessEngine.ensure_clear_screen', return_value=False)
|
||||
def test_trap_guard_autonomous_ai_escape(self, mock_sae_clear, mock_q_rand_sleep, mock_q_sleep):
|
||||
print("Starting TDD: Testing autonomous Trap Escape with semantic bypass...")
|
||||
|
||||
# 1. Setup mocks
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import os
|
||||
import glob
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
# Removed sys.modules poison that mock qdrant_client globally
|
||||
|
||||
from GramAddict.core.telepathic_engine import TelepathicEngine
|
||||
|
||||
# Path to real xml dumps
|
||||
DUMPS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "debug", "xml_dumps")
|
||||
|
||||
# Gather all XML files
|
||||
xml_files = glob.glob(os.path.join(DUMPS_DIR, "*.xml"))
|
||||
|
||||
if not xml_files:
|
||||
print(f"WARNING: No xml dumps found in {DUMPS_DIR}. Fuzzer cannot run.")
|
||||
xml_files = ["dummy_path_to_prevent_pytest_crash"]
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_ai_services():
|
||||
"""Ensure that the fuzzer never makes real LLM API or Qdrant DB calls."""
|
||||
with patch('GramAddict.core.qdrant_memory.QdrantClient'), \
|
||||
patch('GramAddict.core.qdrant_memory.QdrantBase._get_embedding', return_value=[0.0]*1536), \
|
||||
patch('GramAddict.core.telepathic_engine.query_telepathic_llm', return_value='{"index": 0, "reason": "Fuzz Mock"}'):
|
||||
yield
|
||||
|
||||
@pytest.mark.parametrize("xml_path", xml_files, ids=lambda x: os.path.basename(x))
|
||||
def test_xml_parser_does_not_crash(xml_path):
|
||||
"""
|
||||
Reads an arbitrary XML dump from the physical device during crash events
|
||||
and guarantees that the core TelepathicEngine parser handles it gracefully.
|
||||
"""
|
||||
if xml_path == "dummy_path_to_prevent_pytest_crash":
|
||||
pytest.skip("No XML dumps found. Skipping fuzzer.")
|
||||
if not os.path.exists(xml_path):
|
||||
pytest.skip(f"XML dump missing: {xml_path}")
|
||||
|
||||
with open(xml_path, "r", encoding="utf-8") as f:
|
||||
xml_content = f.read()
|
||||
|
||||
engine = TelepathicEngine()
|
||||
|
||||
try:
|
||||
# Phase 1: Pure parsing stability
|
||||
nodes = engine._extract_semantic_nodes(xml_content)
|
||||
|
||||
# Verify node structure if nodes exist
|
||||
for n in nodes:
|
||||
assert "raw_bounds" in n, f"Extracted node is missing raw_bounds. Content: {n}"
|
||||
assert "semantic_string" in n, f"Extracted node missing semantic_string. Content: {n}"
|
||||
|
||||
if len(nodes) == 0:
|
||||
print(f"WARN: {os.path.basename(xml_path)} parsed perfectly, but yielded ZERO readable nodes.")
|
||||
|
||||
# Phase 2: Query resolution stability (Keyword + Vector + VLM Fallbacks)
|
||||
device_mock = MagicMock()
|
||||
device_mock.get_info.return_value = {"displayHeight": 2400, "displayWidth": 1080}
|
||||
|
||||
# Find completely arbitrary intent, just to trigger full resolution path
|
||||
best_node = engine.find_best_node(xml_content, "dismiss this modal immediately or try clicking like", device=device_mock)
|
||||
|
||||
# It's totally fine if `best_node` is None (e.g. 0 nodes). We just verify NO Crash.
|
||||
|
||||
except Exception as e:
|
||||
pytest.fail(f"Fuzz test crashed on {os.path.basename(xml_path)} with error: {str(e)}")
|
||||
Reference in New Issue
Block a user