Files
HeurAMS/tests/test_reactor.py
2025-11-02 12:19:31 +08:00

414 lines
13 KiB
Python

"""
Unit tests for reactor modules: Phaser, Procession, Fission, States
"""
import pytest
from datetime import datetime, timezone
from enum import Enum
from src.heurams.kernel.reactor.phaser import Phaser
from src.heurams.kernel.reactor.procession import Procession
from src.heurams.kernel.reactor.fission import Fission
from src.heurams.kernel.reactor.states import States
from src.heurams.kernel.particles.atom import Atom
from src.heurams.kernel.particles.nucleon import Nucleon
from src.heurams.kernel.particles.electron import Electron
from src.heurams.kernel.particles.orbital import Orbital
class TestStates:
"""Test cases for States enum."""
def test_states_enum_values(self):
"""Test that States enum has correct values."""
assert States.IDLE.value == "idle"
assert States.LEARNING.value == "learning"
assert States.REVIEW.value == "review"
assert States.FINISHED.value == "finished"
def test_states_enum_membership(self):
"""Test States enum membership."""
assert isinstance(States.IDLE, Enum)
assert States.LEARNING in States
assert States.REVIEW in States
assert States.FINISHED in States
class TestPhaser:
"""Test cases for Phaser class."""
def test_phaser_creation(self):
"""Test Phaser creation."""
phaser = Phaser()
assert phaser.current_state == States.IDLE
assert phaser.atom is None
assert phaser.puzzle is None
def test_phaser_initialize(self):
"""Test Phaser initialization with atom."""
phaser = Phaser()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
phaser.initialize(atom)
assert phaser.atom == atom
assert phaser.current_state == States.LEARNING
def test_phaser_transition_to_review(self):
"""Test transition to review state."""
phaser = Phaser()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
phaser.initialize(atom)
phaser.transition_to_review()
assert phaser.current_state == States.REVIEW
def test_phaser_transition_to_finished(self):
"""Test transition to finished state."""
phaser = Phaser()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
phaser.initialize(atom)
phaser.transition_to_finished()
assert phaser.current_state == States.FINISHED
def test_phaser_reset(self):
"""Test Phaser reset."""
phaser = Phaser()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
phaser.initialize(atom)
phaser.transition_to_review()
phaser.reset()
assert phaser.current_state == States.IDLE
assert phaser.atom is None
assert phaser.puzzle is None
def test_phaser_set_puzzle(self):
"""Test setting puzzle in Phaser."""
phaser = Phaser()
test_puzzle = {"question": "Test?", "answer": "Test", "type": "test"}
phaser.set_puzzle(test_puzzle)
assert phaser.puzzle == test_puzzle
def test_phaser_validation(self):
"""Test input validation for Phaser."""
phaser = Phaser()
# Test initialize with None
with pytest.raises(TypeError):
phaser.initialize(None)
# Test initialize with invalid type
with pytest.raises(TypeError):
phaser.initialize("not an atom")
class TestProcession:
"""Test cases for Procession class."""
def test_procession_creation(self):
"""Test Procession creation."""
procession = Procession()
assert procession.queue == []
assert procession.current_index == 0
def test_procession_add_atom(self):
"""Test adding atom to procession."""
procession = Procession()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
procession.add_atom(atom)
assert len(procession.queue) == 1
assert procession.queue[0] == atom
def test_procession_add_multiple_atoms(self):
"""Test adding multiple atoms to procession."""
procession = Procession()
atoms = []
for i in range(3):
nucleon = Nucleon(content=f"Test{i}", answer=f"Answer{i}")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
atoms.append(atom)
for atom in atoms:
procession.add_atom(atom)
assert len(procession.queue) == 3
assert procession.queue == atoms
def test_procession_get_current_atom(self):
"""Test getting current atom."""
procession = Procession()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
procession.add_atom(atom)
current_atom = procession.get_current_atom()
assert current_atom == atom
def test_procession_get_current_atom_empty(self):
"""Test getting current atom from empty procession."""
procession = Procession()
current_atom = procession.get_current_atom()
assert current_atom is None
def test_procession_move_next(self):
"""Test moving to next atom."""
procession = Procession()
atoms = []
for i in range(3):
nucleon = Nucleon(content=f"Test{i}", answer=f"Answer{i}")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
atoms.append(atom)
procession.add_atom(atom)
# Start at first atom
assert procession.get_current_atom() == atoms[0]
assert procession.current_index == 0
# Move to next
procession.move_next()
assert procession.get_current_atom() == atoms[1]
assert procession.current_index == 1
# Move to next again
procession.move_next()
assert procession.get_current_atom() == atoms[2]
assert procession.current_index == 2
# Move beyond end
procession.move_next()
assert procession.get_current_atom() is None
assert procession.current_index == 3
def test_procession_has_next(self):
"""Test checking if there are more atoms."""
procession = Procession()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
procession.add_atom(atom)
# Initially has next (current is first, can move to next)
assert procession.has_next() is True
# Move to next (beyond the only atom)
procession.move_next()
assert procession.has_next() is False
def test_procession_is_empty(self):
"""Test checking if procession is empty."""
procession = Procession()
assert procession.is_empty() is True
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
procession.add_atom(atom)
assert procession.is_empty() is False
def test_procession_clear(self):
"""Test clearing procession."""
procession = Procession()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
procession.add_atom(atom)
procession.clear()
assert procession.queue == []
assert procession.current_index == 0
def test_procession_validation(self):
"""Test input validation for Procession."""
procession = Procession()
# Test add_atom with None
with pytest.raises(TypeError):
procession.add_atom(None)
# Test add_atom with invalid type
with pytest.raises(TypeError):
procession.add_atom("not an atom")
class TestFission:
"""Test cases for Fission class."""
def test_fission_creation(self):
"""Test Fission creation."""
fission = Fission()
assert fission.phaser is not None
assert isinstance(fission.phaser, Phaser)
def test_fission_initialize(self):
"""Test Fission initialization."""
fission = Fission()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
fission.initialize(atom)
assert fission.phaser.atom == atom
assert fission.phaser.current_state == States.LEARNING
def test_fission_generate_learning_puzzle_cloze(self):
"""Test generating learning puzzle with cloze content."""
fission = Fission()
nucleon = Nucleon(
content="The capital of {{c1::France}} is Paris.",
answer="France"
)
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
fission.initialize(atom)
puzzle = fission.generate_learning_puzzle()
assert puzzle is not None
assert "question" in puzzle
assert "answer" in puzzle
assert "type" in puzzle
def test_fission_generate_learning_puzzle_mcq(self):
"""Test generating learning puzzle with MCQ content."""
fission = Fission()
nucleon = Nucleon(
content="What is the capital of France?",
answer="Paris"
)
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
fission.initialize(atom)
puzzle = fission.generate_learning_puzzle()
assert puzzle is not None
assert "question" in puzzle
assert "options" in puzzle
assert "correct_index" in puzzle
assert "type" in puzzle
def test_fission_generate_review_puzzle(self):
"""Test generating review puzzle."""
fission = Fission()
nucleon = Nucleon(
content="What is the capital of France?",
answer="Paris"
)
electron = Electron(interval=10, repetitions=5) # In review phase
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
fission.initialize(atom)
fission.phaser.transition_to_review()
puzzle = fission.generate_review_puzzle()
assert puzzle is not None
assert "question" in puzzle
def test_fission_process_answer_correct(self):
"""Test processing correct answer."""
fission = Fission()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
fission.initialize(atom)
result = fission.process_answer("Answer")
assert "success" in result
assert "quality" in result
assert "next_state" in result
assert result["success"] is True
def test_fission_process_answer_incorrect(self):
"""Test processing incorrect answer."""
fission = Fission()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
fission.initialize(atom)
result = fission.process_answer("Wrong")
assert result["success"] is False
def test_fission_get_current_state(self):
"""Test getting current state."""
fission = Fission()
nucleon = Nucleon(content="Test", answer="Answer")
electron = Electron()
orbital = Orbital()
atom = Atom(nucleon=nucleon, electron=electron, orbital=orbital)
fission.initialize(atom)
state = fission.get_current_state()
assert state == States.LEARNING
def test_fission_validation(self):
"""Test input validation for Fission."""
fission = Fission()
# Test initialize with None
with pytest.raises(TypeError):
fission.initialize(None)
# Test process_answer without initialization
with pytest.raises(RuntimeError):
fission.process_answer("test")
# Test generate_learning_puzzle without initialization
with pytest.raises(RuntimeError):
fission.generate_learning_puzzle()
# Test generate_review_puzzle without initialization
with pytest.raises(RuntimeError):
fission.generate_review_puzzle()