414 lines
13 KiB
Python
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() |