"""Tests for logical formula functions (R1).""" from __future__ import annotations from agentkit.bitable.formula.functions.logical import LOGICAL_FUNCTIONS def test_registry_has_15_functions() -> None: assert len(LOGICAL_FUNCTIONS) == 15 expected = { "IF", "IFS", "SWITCH", "IFERROR", "COALESCE", "AND", "OR", "NOT", "ISBLANK", "ISNUMBER", "ISTEXT", "ISDATE", "TRUE", "FALSE", "NULL", } assert set(LOGICAL_FUNCTIONS.keys()) == expected # ── IF (migrated) ─────────────────────────────────────── def test_if_true() -> None: assert LOGICAL_FUNCTIONS["IF"](1, "yes", "no") == "yes" def test_if_false() -> None: assert LOGICAL_FUNCTIONS["IF"](0, "yes", "no") == "no" def test_if_default_false() -> None: assert LOGICAL_FUNCTIONS["IF"](0, "yes") is None # ── IFS ───────────────────────────────────────────────── def test_ifs_first_match() -> None: assert LOGICAL_FUNCTIONS["IFS"](1, "a", 0, "b", 1, "c") == "a" def test_ifs_second_match() -> None: assert LOGICAL_FUNCTIONS["IFS"](0, "a", 1, "b", 0, "c") == "b" def test_ifs_no_match() -> None: assert LOGICAL_FUNCTIONS["IFS"](0, "a", 0, "b") is None # ── SWITCH ────────────────────────────────────────────── def test_switch_match() -> None: assert LOGICAL_FUNCTIONS["SWITCH"]("b", "a", 1, "b", 2, "c", 3) == 2 def test_switch_default() -> None: assert LOGICAL_FUNCTIONS["SWITCH"]("x", "a", 1, "b", 2, "default") == "default" def test_switch_no_match_no_default() -> None: assert LOGICAL_FUNCTIONS["SWITCH"]("x", "a", 1, "b", 2) is None # ── IFERROR ───────────────────────────────────────────── def test_iferror_passes_non_error() -> None: assert LOGICAL_FUNCTIONS["IFERROR"](42, "default") == 42 def test_iferror_returns_default_for_error() -> None: error_dict = {"__error": "division by zero"} assert LOGICAL_FUNCTIONS["IFERROR"](error_dict, "default") == "default" # ── COALESCE ──────────────────────────────────────────── def test_coalesce_first_non_empty() -> None: assert LOGICAL_FUNCTIONS["COALESCE"]("a", "b", "c") == "a" def test_coalesce_skips_none_and_empty() -> None: assert LOGICAL_FUNCTIONS["COALESCE"](None, "", "c") == "c" def test_coalesce_all_empty() -> None: assert LOGICAL_FUNCTIONS["COALESCE"](None, "", None) is None # ── Boolean operators ─────────────────────────────────── def test_and() -> None: assert LOGICAL_FUNCTIONS["AND"](1, 1, 1) is True assert LOGICAL_FUNCTIONS["AND"](1, 0, 1) is False def test_or() -> None: assert LOGICAL_FUNCTIONS["OR"](0, 0, 1) is True assert LOGICAL_FUNCTIONS["OR"](0, 0, 0) is False def test_not() -> None: assert LOGICAL_FUNCTIONS["NOT"](1) is False assert LOGICAL_FUNCTIONS["NOT"](0) is True # ── Predicates ────────────────────────────────────────── def test_isblank() -> None: assert LOGICAL_FUNCTIONS["ISBLANK"](None) is True assert LOGICAL_FUNCTIONS["ISBLANK"]("") is True assert LOGICAL_FUNCTIONS["ISBLANK"]("x") is False def test_isnumber() -> None: assert LOGICAL_FUNCTIONS["ISNUMBER"](42) is True assert LOGICAL_FUNCTIONS["ISNUMBER"](3.14) is True assert LOGICAL_FUNCTIONS["ISNUMBER"]("42") is False assert LOGICAL_FUNCTIONS["ISNUMBER"](True) is False # bool excluded def test_istext() -> None: assert LOGICAL_FUNCTIONS["ISTEXT"]("hello") is True assert LOGICAL_FUNCTIONS["ISTEXT"](42) is False def test_isdate() -> None: assert LOGICAL_FUNCTIONS["ISDATE"]("2026-07-06") is True assert LOGICAL_FUNCTIONS["ISDATE"]("2026-07-06T10:30:00") is True assert LOGICAL_FUNCTIONS["ISDATE"]("not a date") is False assert LOGICAL_FUNCTIONS["ISDATE"](42) is False # ── Constants ─────────────────────────────────────────── def test_true_false_null() -> None: assert LOGICAL_FUNCTIONS["TRUE"]() is True assert LOGICAL_FUNCTIONS["FALSE"]() is False assert LOGICAL_FUNCTIONS["NULL"]() is None