forked from Rockachopa/Timmy-time-dashboard
293 lines
8.9 KiB
Python
293 lines
8.9 KiB
Python
"""Tests for the gematria computation engine (issue #234).
|
||
|
||
Alexander Whitestone = 222 in Simple English Gematria.
|
||
This is not trivia. It is foundational.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from timmy.gematria import (
|
||
CIPHERS,
|
||
NOTABLE_NUMBERS,
|
||
analyze_number,
|
||
compute_all,
|
||
compute_value,
|
||
factorize,
|
||
gematria,
|
||
letter_breakdown,
|
||
reduce_number,
|
||
)
|
||
|
||
# ── Core cipher computation ──────────────────────────────────────────────────
|
||
|
||
|
||
class TestSimpleCipher:
|
||
"""Simple English: A=1, B=2, ..., Z=26."""
|
||
|
||
def test_single_letter_a(self):
|
||
assert compute_value("A", "simple") == 1
|
||
|
||
def test_single_letter_z(self):
|
||
assert compute_value("Z", "simple") == 26
|
||
|
||
def test_alexander_whitestone(self):
|
||
"""The foundational identity — Alexander Whitestone = 222."""
|
||
assert compute_value("Alexander Whitestone", "simple") == 222
|
||
|
||
def test_case_insensitive(self):
|
||
assert compute_value("hello", "simple") == compute_value("HELLO", "simple")
|
||
|
||
def test_ignores_non_alpha(self):
|
||
assert compute_value("A-B!C", "simple") == compute_value("ABC", "simple")
|
||
|
||
def test_empty_string(self):
|
||
assert compute_value("", "simple") == 0
|
||
|
||
def test_spaces_only(self):
|
||
assert compute_value(" ", "simple") == 0
|
||
|
||
def test_numbers_in_text_ignored(self):
|
||
assert compute_value("ABC123", "simple") == compute_value("ABC", "simple")
|
||
|
||
|
||
class TestReductionCipher:
|
||
"""Full Reduction: each letter reduced to single digit."""
|
||
|
||
def test_a_is_1(self):
|
||
assert compute_value("A", "reduction") == 1
|
||
|
||
def test_j_is_1(self):
|
||
# J=10 → 1+0=1
|
||
assert compute_value("J", "reduction") == 1
|
||
|
||
def test_s_is_1(self):
|
||
# S=19 → 1+9=10 → 1+0=1
|
||
assert compute_value("S", "reduction") == 1
|
||
|
||
def test_z_is_8(self):
|
||
# Z=26 → 2+6=8
|
||
assert compute_value("Z", "reduction") == 8
|
||
|
||
|
||
class TestReverseCipher:
|
||
"""Reverse Ordinal: A=26, B=25, ..., Z=1."""
|
||
|
||
def test_a_is_26(self):
|
||
assert compute_value("A", "reverse") == 26
|
||
|
||
def test_z_is_1(self):
|
||
assert compute_value("Z", "reverse") == 1
|
||
|
||
def test_m_is_14(self):
|
||
# M is 13th letter → reverse = 27-13 = 14
|
||
assert compute_value("M", "reverse") == 14
|
||
|
||
|
||
class TestSumerianCipher:
|
||
"""Sumerian: Simple × 6."""
|
||
|
||
def test_a_is_6(self):
|
||
assert compute_value("A", "sumerian") == 6
|
||
|
||
def test_z_is_156(self):
|
||
assert compute_value("Z", "sumerian") == 156
|
||
|
||
def test_is_six_times_simple(self):
|
||
text = "Alexander Whitestone"
|
||
assert compute_value(text, "sumerian") == compute_value(text, "simple") * 6
|
||
|
||
|
||
class TestHebrewCipher:
|
||
"""Hebrew-mapped: traditional Hebrew values on Latin alphabet."""
|
||
|
||
def test_a_is_1(self):
|
||
assert compute_value("A", "hebrew") == 1
|
||
|
||
def test_j_is_10(self):
|
||
assert compute_value("J", "hebrew") == 10
|
||
|
||
def test_k_is_20(self):
|
||
assert compute_value("K", "hebrew") == 20
|
||
|
||
def test_t_is_200(self):
|
||
assert compute_value("T", "hebrew") == 200
|
||
|
||
|
||
class TestUnknownCipher:
|
||
def test_raises_on_unknown(self):
|
||
with pytest.raises(ValueError, match="Unknown cipher"):
|
||
compute_value("test", "klingon")
|
||
|
||
|
||
# ── compute_all ──────────────────────────────────────────────────────────────
|
||
|
||
|
||
class TestComputeAll:
|
||
def test_returns_all_ciphers(self):
|
||
result = compute_all("ABC")
|
||
assert set(result.keys()) == set(CIPHERS.keys())
|
||
|
||
def test_values_are_ints(self):
|
||
result = compute_all("test")
|
||
for v in result.values():
|
||
assert isinstance(v, int)
|
||
|
||
|
||
# ── letter_breakdown ─────────────────────────────────────────────────────────
|
||
|
||
|
||
class TestLetterBreakdown:
|
||
def test_simple_breakdown(self):
|
||
result = letter_breakdown("AB", "simple")
|
||
assert result == [("A", 1), ("B", 2)]
|
||
|
||
def test_strips_non_alpha(self):
|
||
result = letter_breakdown("A B!", "simple")
|
||
assert result == [("A", 1), ("B", 2)]
|
||
|
||
def test_unknown_cipher_raises(self):
|
||
with pytest.raises(ValueError):
|
||
letter_breakdown("test", "nonexistent")
|
||
|
||
|
||
# ── reduce_number ────────────────────────────────────────────────────────────
|
||
|
||
|
||
class TestReduceNumber:
|
||
def test_single_digit(self):
|
||
assert reduce_number(7) == 7
|
||
|
||
def test_double_digit_to_master(self):
|
||
# 29 → 2+9=11 → master number, preserved
|
||
assert reduce_number(29) == 11
|
||
|
||
def test_double_digit_non_master(self):
|
||
# 28 → 2+8=10 → 1+0=1
|
||
assert reduce_number(28) == 1
|
||
|
||
def test_222_reduces_to_6(self):
|
||
assert reduce_number(222) == 6 # 2+2+2=6
|
||
|
||
def test_master_11(self):
|
||
assert reduce_number(11) == 11
|
||
|
||
def test_master_22(self):
|
||
assert reduce_number(22) == 22
|
||
|
||
def test_master_33(self):
|
||
assert reduce_number(33) == 33
|
||
|
||
def test_zero(self):
|
||
assert reduce_number(0) == 0
|
||
|
||
def test_negative(self):
|
||
assert reduce_number(-42) == 6 # abs(-42)=42 → 4+2=6
|
||
|
||
def test_large_number(self):
|
||
assert reduce_number(9999) == 9 # 9+9+9+9=36 → 3+6=9
|
||
|
||
|
||
# ── factorize ────────────────────────────────────────────────────────────────
|
||
|
||
|
||
class TestFactorize:
|
||
def test_prime(self):
|
||
assert factorize(7) == [7]
|
||
|
||
def test_composite(self):
|
||
assert factorize(12) == [2, 2, 3]
|
||
|
||
def test_222(self):
|
||
assert factorize(222) == [2, 3, 37]
|
||
|
||
def test_one(self):
|
||
assert factorize(1) == [1]
|
||
|
||
def test_zero(self):
|
||
assert factorize(0) == []
|
||
|
||
def test_large_prime(self):
|
||
assert factorize(997) == [997]
|
||
|
||
|
||
# ── analyze_number ───────────────────────────────────────────────────────────
|
||
|
||
|
||
class TestAnalyzeNumber:
|
||
def test_222(self):
|
||
result = analyze_number(222)
|
||
assert result["value"] == 222
|
||
assert result["numerological_reduction"] == 6
|
||
assert result["prime_factors"] == [2, 3, 37]
|
||
assert result["is_prime"] is False
|
||
assert result["significance"] == NOTABLE_NUMBERS[222]
|
||
|
||
def test_prime_detection(self):
|
||
assert analyze_number(17)["is_prime"] is True
|
||
assert analyze_number(18)["is_prime"] is False
|
||
|
||
def test_perfect_square(self):
|
||
assert analyze_number(144)["is_perfect_square"] is True
|
||
assert analyze_number(143)["is_perfect_square"] is False
|
||
|
||
def test_triangular(self):
|
||
assert analyze_number(6)["is_triangular"] is True # 1+2+3
|
||
assert analyze_number(7)["is_triangular"] is False
|
||
|
||
def test_angel_number(self):
|
||
assert analyze_number(333).get("angel_number") is True
|
||
assert analyze_number(123).get("angel_number") is None
|
||
|
||
def test_master_number(self):
|
||
assert analyze_number(22).get("master_number") is True
|
||
|
||
|
||
# ── gematria tool function (the main interface) ─────────────────────────────
|
||
|
||
|
||
class TestGematriaTool:
|
||
"""Test the main gematria() tool function that Timmy calls."""
|
||
|
||
def test_phrase_mode(self):
|
||
result = gematria("Alexander Whitestone")
|
||
assert "222" in result
|
||
assert "Simple" in result
|
||
|
||
def test_number_mode(self):
|
||
result = gematria("222")
|
||
assert "Analysis of 222" in result
|
||
assert "Alexander Whitestone" in result
|
||
|
||
def test_compare_mode(self):
|
||
result = gematria("compare: ABC, DEF")
|
||
assert "Comparison" in result
|
||
assert "ABC" in result
|
||
assert "DEF" in result
|
||
|
||
def test_compare_needs_two(self):
|
||
result = gematria("compare: alone")
|
||
assert "at least two" in result.lower()
|
||
|
||
def test_empty_input(self):
|
||
result = gematria("123 456")
|
||
assert "No alphabetic" in result
|
||
|
||
def test_whitespace_handling(self):
|
||
result = gematria(" ABC ")
|
||
assert "Simple" in result
|
||
|
||
def test_letter_breakdown_in_output(self):
|
||
result = gematria("ABC")
|
||
assert "Breakdown" in result
|
||
assert "A(1)" in result
|
||
|
||
def test_notable_number_flagged(self):
|
||
result = gematria("Alexander Whitestone")
|
||
assert "★" in result or "222" in result
|
||
|
||
def test_numerological_root_shown(self):
|
||
result = gematria("ABC")
|
||
assert "root" in result.lower() or "reduction" in result.lower()
|