diff --git a/tests/test_nonfiction.py b/tests/test_nonfiction.py new file mode 100644 index 0000000..67ad547 --- /dev/null +++ b/tests/test_nonfiction.py @@ -0,0 +1,291 @@ +"""Comprehensive Tests for Opus Orchestrator. + +Tests for: +- Orchestrator +- Nonfiction framework +- Classifier +- Ingestion +""" + +import pytest +from unittest.mock import Mock, patch, AsyncMock +import json + + +# ============================================================================= +# TEST CLASSIFIER +# ============================================================================= + +class TestPurposeClassifier: + """Tests for PurposeClassifier.""" + + def test_keyword_howto(self): + """Test 'how to' triggers LEARN_HANDS_ON.""" + from opus_orchestrator.nonfiction.classifier import PurposeClassifier + + classifier = PurposeClassifier() + result = classifier._keyword_classify( + concept="How to Code in Python", + target_audience="Beginners", + intended_outcome="Learn programming", + ) + + assert result.purpose.value == "learn_hands_on" + assert result.confidence > 0.5 + + def test_keyword_transform(self): + """Test transform keywords.""" + from opus_orchestrator.nonfiction.classifier import PurposeClassifier + + classifier = PurposeClassifier() + result = classifier._keyword_classify( + concept="Transform Your Life", + target_audience="Anyone feeling stuck", + intended_outcome="Overcome challenges", + ) + + assert result.purpose.value == "transform" + + def test_keyword_decide(self): + """Test decide keywords.""" + from opus_orchestrator.nonfiction.classifier import PurposeClassifier + + classifier = PurposeClassifier() + result = classifier._keyword_classify( + concept="Best CRM Comparison", + target_audience="Business owners", + intended_outcome="Choose right CRM", + ) + + assert result.purpose.value == "decide" + + +# ============================================================================= +# TEST TAXONOMY +# ============================================================================= + +class TestTaxonomy: + """Tests for taxonomy and framework selection.""" + + def test_purpose_structure_matrix(self): + """Test PURPOSE_STRUCTURE_MATRIX exists.""" + from opus_orchestrator.nonfiction_taxonomy import PURPOSE_STRUCTURE_MATRIX + + assert len(PURPOSE_STRUCTURE_MATRIX) > 0 + + def test_select_framework_learn(self): + """Test framework selection for learn purpose.""" + from opus_orchestrator.nonfiction_taxonomy import select_framework, ReaderPurpose + + framework = select_framework(purpose=ReaderPurpose.LEARN_HANDS_ON) + + assert framework is not None + assert "stages" in framework + + def test_select_framework_transform(self): + """Test framework selection for transform purpose.""" + from opus_orchestrator.nonfiction_taxonomy import select_framework, ReaderPurpose + + framework = select_framework(purpose=ReaderPurpose.TRANSFORM) + + assert framework is not None + assert framework.get("purpose") == ReaderPurpose.TRANSFORM + + +# ============================================================================= +# TEST CRITERIA +# ============================================================================= + +class TestCritiqueCriteria: + """Tests for purpose-specific critique criteria.""" + + def test_get_criteria_learn(self): + """Test criteria for learn purpose.""" + from opus_orchestrator.nonfiction.critique_criteria import get_critique_criteria, ReaderPurpose + + criteria = get_critique_criteria(ReaderPurpose.LEARN_HANDS_ON) + + assert criteria is not None + assert len(criteria.criteria) > 0 + + def test_get_criteria_transform(self): + """Test criteria for transform purpose.""" + from opus_orchestrator.nonfiction.critique_criteria import get_critique_criteria, ReaderPurpose + + criteria = get_critique_criteria(ReaderPurpose.TRANSFORM) + + assert criteria is not None + # Should have emotional honesty as top criterion + criterion_names = [c.name for c in criteria.criteria] + assert "Emotional Honesty" in criterion_names + + +# ============================================================================= +# TEST EXPANDED FRAMEWORKS +# ============================================================================= + +class TestExpandedFrameworks: + """Tests for expanded framework library.""" + + def test_framework_count(self): + """Test we have 35+ frameworks.""" + from opus_orchestrator.nonfiction.expanded_frameworks import get_total_framework_count + + count = get_total_framework_count() + assert count >= 35 + + def test_business_frameworks(self): + """Test business frameworks exist.""" + from opus_orchestrator.nonfiction.expanded_frameworks import EXPANDED_FRAMEWORKS + + assert "big_idea" in EXPANDED_FRAMEWORKS + assert "one_thing" in EXPANDED_FRAMEWORKS + + def test_creative_frameworks(self): + """Test creative frameworks exist.""" + from opus_orchestrator.nonfiction.creative_frameworks import CREATIVE_FRAMEWORKS + + assert "choose_your_own_adventure" in CREATIVE_FRAMEWORKS + assert "epistolary_novel" in CREATIVE_FRAMEWORKS + + +# ============================================================================= +# TEST RPG FRAMEWORKS +# ============================================================================= + +class TestRPGFrameworks: + """Tests for RPG framework library.""" + + def test_rpg_frameworks_exist(self): + """Test RPG frameworks.""" + from opus_orchestrator.nonfiction.rpg_frameworks import RPG_FRAMEWORKS + + assert "core_rulebook" in RPG_FRAMEWORKS + assert "adventure_module" in RPG_FRAMEWORKS + assert "monster_manual" in RPG_FRAMEWORKS + + +# ============================================================================= +# TEST TEXTBOOK FRAMEWORKS +# ============================================================================= + +class TestTextbookFrameworks: + """Tests for textbook framework library.""" + + def test_textbook_frameworks(self): + """Test textbook frameworks.""" + from opus_orchestrator.nonfiction.textbook_frameworks import TEXTBOOK_FRAMEWORKS + + assert "comprehensive_textbook" in TEXTBOOK_FRAMEWORKS + assert "online_course" in TEXTBOOK_FRAMEWORKS + + +# ============================================================================= +# TEST ACADEMIC PAPERS +# ============================================================================= + +class TestAcademicPapers: + """Tests for academic paper frameworks.""" + + def test_academic_papers(self): + """Test academic paper types.""" + from opus_orchestrator.nonfiction.academic_papers import ACADEMIC_PAPER_TYPES + + assert "empirical_paper" in ACADEMIC_PAPER_TYPES + assert "position_paper" in ACADEMIC_PAPER_TYPES + + +# ============================================================================= +# TEST INTAKE +# ============================================================================= + +class TestIntake: + """Tests for intake agent.""" + + def test_intake_input(self): + """Test IntakeInput dataclass.""" + from opus_orchestrator.nonfiction.intake import IntakeInput + + intake = IntakeInput( + concept="My book idea", + explicit_purpose="transform", + ) + + assert intake.concept == "My book idea" + assert intake.explicit_purpose == "transform" + + def test_intake_result(self): + """Test IntakeResult.""" + from opus_orchestrator.nonfiction.intake import IntakeResult, ReaderPurpose + + result = IntakeResult( + purpose=ReaderPurpose.TRANSFORM, + confidence=0.9, + category=None, + framework={}, + reasoning="Test", + source="explicit", + ) + + assert result.purpose == ReaderPurpose.TRANSFORM + assert result.confidence == 0.9 + + +# ============================================================================= +# TEST MULTI SOURCE INGEST +# ============================================================================= + +class TestMultiSourceIngest: + """Tests for multi-source ingestion.""" + + @pytest.mark.asyncio + async def test_ingest_single_source(self): + """Test ingesting from single source.""" + from opus_orchestrator.utils.multi_source_ingest import ContentSource, SourceType + + source = ContentSource( + source_type=SourceType.LOCAL, + local_path="/tmp/test", + ) + + assert source.source_type == SourceType.LOCAL + + def test_content_source(self): + """Test ContentSource creation.""" + from opus_orchestrator.utils.multi_source_ingest import ContentSource, SourceType + + source = ContentSource( + source_type=SourceType.GITHUB, + repo="test/repo", + ) + + assert source.repo == "test/repo" + + +# ============================================================================= +# TEST UTILS +# ============================================================================= + +class TestUtils: + """Tests for utility functions.""" + + def test_safe_imports(self): + """Test all main modules import.""" + from opus_orchestrator.nonfiction import ( + PurposeClassifier, + ReaderPurpose, + IntakeAgent, + select_framework, + get_critique_criteria, + ) + + assert PurposeClassifier is not None + assert ReaderPurpose is not None + + +# ============================================================================= +# RUN TESTS +# ============================================================================= + +if __name__ == "__main__": + pytest.main([__file__, "-v"])