"""Tests for text formula functions (R1).""" from __future__ import annotations from agentkit.bitable.formula.functions.text import TEXT_FUNCTIONS # ── Registry ──────────────────────────────────────────── def test_registry_has_17_functions() -> None: assert len(TEXT_FUNCTIONS) == 17 expected = { "CONCAT", "LEN", "FIND", "MID", "LEFT", "RIGHT", "REPLACE", "SUBSTITUTE", "UPPER", "LOWER", "TRIM", "REPT", "TEXT", "VALUE", "STARTSWITH", "ENDSWITH", "SPLIT", } assert set(TEXT_FUNCTIONS.keys()) == expected # ── migrated functions ────────────────────────────────── def test_concat() -> None: assert TEXT_FUNCTIONS["CONCAT"]("a", "b", "c") == "abc" assert TEXT_FUNCTIONS["CONCAT"]("a", None, "c") == "ac" def test_len() -> None: assert TEXT_FUNCTIONS["LEN"]("hello") == 5 assert TEXT_FUNCTIONS["LEN"](None) == 0 assert TEXT_FUNCTIONS["LEN"](123) == 3 # ── find / extract ────────────────────────────────────── def test_find() -> None: assert TEXT_FUNCTIONS["FIND"]("hello world", "world") == 6 assert TEXT_FUNCTIONS["FIND"]("hello", "xyz") == -1 assert TEXT_FUNCTIONS["FIND"]("hello", "") == 0 def test_mid() -> None: assert TEXT_FUNCTIONS["MID"]("hello", 1, 3) == "ell" assert TEXT_FUNCTIONS["MID"]("hello", 0, 2) == "he" assert TEXT_FUNCTIONS["MID"]("hello", -1, 2) == "he" # negative clamped def test_left() -> None: assert TEXT_FUNCTIONS["LEFT"]("hello", 3) == "hel" assert TEXT_FUNCTIONS["LEFT"]("hello") == "h" # default n=1 assert TEXT_FUNCTIONS["LEFT"]("hello", 0) == "" def test_right() -> None: assert TEXT_FUNCTIONS["RIGHT"]("hello", 3) == "llo" assert TEXT_FUNCTIONS["RIGHT"]("hello") == "o" # default n=1 # ── replace / substitute ──────────────────────────────── def test_replace() -> None: # REPLACE("hello", 1, 3, "XYZ") → "h" + "XYZ" + "o" = "hXYZo" # Replaces 3 chars starting at index 1: "ell" → "XYZ" assert TEXT_FUNCTIONS["REPLACE"]("hello", 1, 3, "XYZ") == "hXYZo" def test_substitute_all() -> None: assert TEXT_FUNCTIONS["SUBSTITUTE"]("a-b-c", "-", "+") == "a+b+c" def test_substitute_nth() -> None: # Replace only the 2nd occurrence assert TEXT_FUNCTIONS["SUBSTITUTE"]("a-b-c", "-", "+", 2) == "a-b+c" # ── case / trim ───────────────────────────────────────── def test_upper_lower() -> None: assert TEXT_FUNCTIONS["UPPER"]("hello") == "HELLO" assert TEXT_FUNCTIONS["LOWER"]("HELLO") == "hello" def test_trim() -> None: assert TEXT_FUNCTIONS["TRIM"](" hello world ") == "hello world" # ── repeat / format / convert ─────────────────────────── def test_rept() -> None: assert TEXT_FUNCTIONS["REPT"]("ab", 3) == "ababab" assert TEXT_FUNCTIONS["REPT"]("ab", 0) == "" def test_text_format() -> None: assert TEXT_FUNCTIONS["TEXT"](3.14159, ".2f") == "3.14" assert TEXT_FUNCTIONS["TEXT"](42) == "42" def test_value() -> None: assert TEXT_FUNCTIONS["VALUE"]("42") == 42 assert TEXT_FUNCTIONS["VALUE"]("3.14") == 3.14 assert TEXT_FUNCTIONS["VALUE"]("not a number") is None assert TEXT_FUNCTIONS["VALUE"](None) is None # ── predicates / split ────────────────────────────────── def test_startswith() -> None: assert TEXT_FUNCTIONS["STARTSWITH"]("hello world", "hello") is True assert TEXT_FUNCTIONS["STARTSWITH"]("hello world", "world") is False def test_endswith() -> None: assert TEXT_FUNCTIONS["ENDSWITH"]("hello world", "world") is True assert TEXT_FUNCTIONS["ENDSWITH"]("hello world", "hello") is False def test_split() -> None: assert TEXT_FUNCTIONS["SPLIT"]("a,b,c", ",") == ["a", "b", "c"] assert TEXT_FUNCTIONS["SPLIT"]("hello", ",") == ["hello"]