Files
OpenBB/cli/tests/test_controllers_script_parser.py
Danglewood 9a30186174 [V5] CLI 2.0 - Pluggable Backends, Non-TTY Default, HTTP Dispatcher, Spec Files + Codegen (#7433)
* stash some changes

* add more robust testing

* mypy

* point PR at V5

* introduce spec file

* codespell

* test fix

* fix workflow environment setup

* fix workflow environment setup

* fix workflow environment setup

* add pyyaml to dependencies

* split lint jobs

* fix workflow environment setup

* fix workflow environment setup

* workflow env setup

* workflow env setup

* clean up code comments

* add auth hook entrypoints

* codespell

* add codegen feature

* codespell

* move _unpack into dispatchers for consistency with codegen packages

* surface nested models in the response

* fix missing coverage in CI

* socrata updates

* test fix

* detect plotly output

* add --include and --exclude flags from generate-extension command

* cap test matrix at python 3.14

* no useless comments

* platform controller command description split

* merge URL overloads from path params

* exclude none and unset from model dump

---------

Co-authored-by: deeleeramone <>
Co-authored-by: Copilot <copilot@github.com>
2026-06-01 19:14:38 +03:00

372 lines
13 KiB
Python

"""Test Script parser."""
from datetime import datetime, timedelta
import pytest
from openbb_cli.controllers.script_parser import (
match_and_return_openbb_keyword_date,
parse_openbb_script,
)
@pytest.mark.parametrize(
"command, expected",
[
("reset", True),
("r", True),
("r\n", True),
("restart", False),
],
)
def test_is_reset(command, expected):
"""Test the is_reset function."""
from openbb_cli.controllers.script_parser import is_reset
assert is_reset(command) == expected
def test_match_and_return_openbb_keyword_date():
"""Test the match_and_return_openbb_keyword_date function."""
keyword = "$LASTFRIDAY"
result = match_and_return_openbb_keyword_date(keyword)
expected = ""
if keyword == "$LASTFRIDAY":
today = datetime.now()
expected = today - timedelta(days=(today.weekday() + 3) % 7)
if expected >= today:
expected -= timedelta(days=7)
expected = expected.strftime("%Y-%m-%d")
assert result == expected
def test_parse_openbb_script_basic():
"""Test the parse_openbb_script function."""
raw_lines = ["echo 'Hello World'"]
error, script = parse_openbb_script(raw_lines)
assert error == ""
assert script == "/echo 'Hello World'"
def test_parse_openbb_script_with_variable():
"""Test the parse_openbb_script function."""
raw_lines = ["$VAR = 2022-01-01", "echo $VAR"]
error, script = parse_openbb_script(raw_lines)
assert error == ""
assert script == "/echo 2022-01-01"
def test_parse_openbb_script_with_foreach_loop():
"""Test the parse_openbb_script function."""
raw_lines = ["foreach $$DATE in 2022-01-01,2022-01-02", "echo $$DATE", "end"]
error, script = parse_openbb_script(raw_lines)
assert error == ""
assert script == "/echo 2022-01-01/echo 2022-01-02"
def test_parse_openbb_script_with_error():
"""Test the parse_openbb_script function."""
raw_lines = ["$VAR = ", "echo $VAR"]
error, script = parse_openbb_script(raw_lines)
assert "Variable $VAR not given" in error
@pytest.mark.parametrize(
"line, expected",
[
(
"foreach $$VAR in 2022-01-01",
"[red]The script has a foreach loop that doesn't terminate. Add the keyword 'end' to explicitly terminate loop[/red]", # noqa: E501
),
("echo Hello World", ""),
(
"end",
"[red]The script has a foreach loop that terminates before it gets started. Add the keyword 'foreach' to explicitly start loop[/red]", # noqa: E501
),
],
)
def test_parse_openbb_script_foreach_errors(line, expected):
"""Test the parse_openbb_script function."""
error, script = parse_openbb_script([line])
assert error == expected
def test_date_keyword_last_friday():
"""Test the match_and_return_openbb_keyword_date function."""
today = datetime.now()
last_friday = today - timedelta(days=(today.weekday() - 4 + 7) % 7)
if last_friday >= today:
last_friday -= timedelta(days=7)
expected_date = last_friday.strftime("%Y-%m-%d")
assert match_and_return_openbb_keyword_date("$LASTFRIDAY") == expected_date
def test_date_keyword_last_january_returns_last_year_jan():
"""``$LASTJANUARY`` resolves to the most recent January 1st (this year or last)."""
result = match_and_return_openbb_keyword_date("$LASTJANUARY")
assert result.endswith("-01-01")
def test_date_keyword_last_december_returns_a_december_date():
"""``$LASTDECEMBER`` resolves to a Dec 1 date (this year or last depending on now)."""
result = match_and_return_openbb_keyword_date("$LASTDECEMBER")
assert result.endswith("-12-01")
def test_date_keyword_next_january_returns_a_january_date():
"""``$NEXTJANUARY`` returns a January 1 date (this year or next)."""
result = match_and_return_openbb_keyword_date("$NEXTJANUARY")
assert result.endswith("-01-01")
def test_date_keyword_next_friday_returns_a_future_date():
"""``$NEXTFRIDAY`` returns the next Friday's date (or today if Friday and equal)."""
result = match_and_return_openbb_keyword_date("$NEXTFRIDAY")
parsed = datetime.strptime(result, "%Y-%m-%d")
assert parsed.weekday() == 4
def test_date_keyword_relative_days_ago():
"""``$1DAYSAGO`` resolves to (now - 1 day) — handled by the past regex (index 0)."""
today = datetime.now()
yesterday = (today - timedelta(days=1)).strftime("%Y-%m-%d")
result = match_and_return_openbb_keyword_date("$1DAYSAGO")
assert result == yesterday
def test_date_keyword_relative_days_from_now():
"""``$2DAYSFROMNOW`` resolves to (now + 2 days) — past regex doesn't match, future does."""
today = datetime.now()
expected = (today + timedelta(days=2)).strftime("%Y-%m-%d")
result = match_and_return_openbb_keyword_date("$2DAYSFROMNOW")
assert result == expected
def test_date_keyword_no_unit_match_returns_empty():
"""A bare ``$1DAYS`` (no AGO/FROMNOW suffix) does not match either regex → empty string."""
assert match_and_return_openbb_keyword_date("$1DAYS") == ""
def test_date_keyword_last_sunday_falls_back_seven_days():
"""``$LASTSUNDAY`` exercises the ``today.weekday() <= target`` branch.
Sunday is weekday 6 — no day of the week strictly exceeds it, so the
weekday comparison always picks the second branch (subtracting 7 days
before stepping to the requested weekday). That branch is the one
coverage flagged previously.
"""
today = datetime.now()
result = match_and_return_openbb_keyword_date("$LASTSUNDAY")
parsed = datetime.strptime(result, "%Y-%m-%d")
# The result is a Sunday, and it is strictly in the past (not today
# even when today is Sunday — the branch always subtracts a week).
assert parsed.weekday() == 6
assert parsed.date() < today.date()
def test_parse_openbb_script_multi_dollar_variable_warning(capsys):
"""Multi-dollar declarations emit a console warning but still set the variable."""
from unittest.mock import patch
with patch("openbb_cli.controllers.script_parser.session") as sess:
error, script = parse_openbb_script(["$$VAR = hello", "echo $VAR"])
assert error == ""
assert script == "/echo hello"
sess.console.print.assert_called()
def test_parse_openbb_script_index_zero_access():
"""``$VAR[0]`` accesses the first element of a list-valued variable."""
error, script = parse_openbb_script(
["$DATES = 2022-01-01,2022-02-01", "echo $DATES[0]"]
)
assert error == ""
assert script == "/echo 2022-01-01"
def test_parse_openbb_script_index_out_of_bounds():
"""Accessing ``$VAR[N]`` past the end produces a structured error."""
error, _ = parse_openbb_script(["$X = a,b", "echo $X[5]"])
assert "only has 2 elements" in error or "5 elements" in error or "index 5" in error
def test_parse_openbb_script_undeclared_variable_with_index():
"""Indexing an undeclared variable returns a 'not given' error."""
error, _ = parse_openbb_script(["echo $MISSING[0]"])
assert "not given" in error
def test_parse_openbb_script_undeclared_variable_with_index_nonzero():
"""``$MISSING[3]`` (non-zero index) on undeclared variable returns 'not given'."""
error, _ = parse_openbb_script(["echo $MISSING[3]"])
assert "not given" in error
def test_parse_openbb_script_slice_works_for_lists():
"""``$VAR[0:2]`` slices the list and joins by comma."""
error, script = parse_openbb_script(["$VAR = a,b,c", "echo $VAR[0:2]"])
assert error == ""
assert "a,b" in script
def test_parse_openbb_script_slice_negative_index_returns_error():
"""Negative index without slicing → red error."""
error, _ = parse_openbb_script(["$VAR = a,b", "echo $VAR[-5]"])
assert "Negative index" in error or "not a value" in error
def test_parse_openbb_script_non_digit_index_returns_error():
"""Non-numeric index like ``$VAR[abc]`` → 'not a value' error."""
error, _ = parse_openbb_script(["$VAR = a,b", "echo $VAR[abc]"])
assert "not a value" in error
def test_parse_openbb_script_unknown_variable_resolves_via_keyword_date():
"""Undeclared variable that matches an OpenBB date keyword resolves to a date."""
error, script = parse_openbb_script(["echo $LASTFRIDAY"])
assert error == ""
assert script != ""
def test_parse_openbb_script_unterminated_foreach_returns_error():
"""``foreach`` without ``end`` returns the dedicated red error."""
error, _ = parse_openbb_script(
[
"foreach $$VAR in 2024-01-01,2024-02-01",
"echo $$VAR",
]
)
assert "foreach loop that doesn't terminate" in error
def test_parse_openbb_script_value_replaces_string_value():
"""Single-string variable expansion (not a list)."""
error, script = parse_openbb_script(["$NAME = world", "echo $NAME"])
assert error == ""
assert "world" in script
def test_parse_openbb_script_skips_comments():
"""Lines containing ``#`` are stripped before parsing."""
error, script = parse_openbb_script(["# a comment", "echo hello"])
assert error == ""
assert "hello" in script
def test_parse_openbb_script_skips_reset_lines():
"""``reset`` / ``r`` lines are filtered out as preprocessing."""
error, script = parse_openbb_script(["reset", "echo done"])
assert error == ""
assert "done" in script
assert "/reset" not in script.split("/")[1] if "/" in script else True
def test_parse_openbb_script_foreach_with_unused_loop_variable_warns():
"""``foreach $$VAR in ...`` with body that doesn't reference $$VAR prints a warning."""
from unittest.mock import patch
with patch("openbb_cli.controllers.script_parser.session") as sess:
error, script = parse_openbb_script(
[
"foreach $$DATE in 2024-01-01,2024-02-01",
"echo nothing",
"end",
]
)
assert error == ""
sess.console.print.assert_called()
def test_parse_openbb_script_foreach_with_mismatched_var_returns_error():
"""``foreach $$X in ...`` followed by body using ``$$Y`` returns a red error."""
error, script = parse_openbb_script(
[
"foreach $$DATE in 2024-01-01",
"echo $$WRONG",
"end",
]
)
assert "another var name" in error or script == ""
def test_parse_openbb_script_leading_slash_in_first_command():
"""A first command starting with ``/`` survives the home-prepending rule."""
error, script = parse_openbb_script(["/equity"])
assert error == ""
assert script.startswith("/equity") or "equity" in script
def test_parse_openbb_script_strips_trailing_home():
"""A trailing ``/home/`` placeholder gets trimmed at the end of the assembly."""
error, script = parse_openbb_script(["/equity", "/"])
assert error == ""
assert not script.endswith("/home/")
def test_parse_openbb_script_with_routines_args_replaces_argv():
"""``$ARGV[0]`` is replaced by the first script_inputs entry at parse time."""
error, script = parse_openbb_script(["echo $ARGV[0]"], script_inputs=["AAPL"])
assert error == ""
assert "AAPL" in script
def test_date_keyword_last_monday_when_today_is_tuesday(freezer):
"""``$LASTMONDAY`` on a Tuesday returns the prior day."""
freezer.move_to("2024-01-09")
result = match_and_return_openbb_keyword_date("$LASTMONDAY")
assert result == "2024-01-08"
def test_date_keyword_next_december_when_today_is_january(freezer):
"""``$NEXTDECEMBER`` from January resolves to **this year**'s December."""
freezer.move_to("2024-01-15")
result = match_and_return_openbb_keyword_date("$NEXTDECEMBER")
assert result == "2024-12-01"
def test_date_keyword_next_monday_when_today_is_tuesday(freezer):
"""``$NEXTMONDAY`` on Tuesday resolves to next week's Monday."""
freezer.move_to("2024-01-09")
result = match_and_return_openbb_keyword_date("$NEXTMONDAY")
assert result == "2024-01-15"
def test_date_keyword_next_friday_when_today_is_monday(freezer):
"""``$NEXTFRIDAY`` on Monday resolves to this week's Friday."""
freezer.move_to("2024-01-08")
result = match_and_return_openbb_keyword_date("$NEXTFRIDAY")
assert result == "2024-01-12"
def test_parse_openbb_script_string_var_index_zero():
"""``$VAR[0]`` for a non-list string variable substitutes the whole string."""
error, script = parse_openbb_script(["$DATE = 2022-01-01", "echo $DATE[0]"])
assert error == ""
assert "2022-01-01" in script
def test_parse_openbb_script_index_valid_nonzero():
"""``$VAR[N]`` for a valid in-bounds N replaces with that element."""
error, script = parse_openbb_script(["$VAR = a,b,c", "echo $VAR[2]"])
assert error == ""
assert script == "/echo c"
def test_parse_openbb_script_slice_yields_empty_returns_error():
"""``$VAR[5:10]`` over a 2-elem list yields an empty slice → red error."""
error, _ = parse_openbb_script(["$VAR = a,b", "echo $VAR[5:10]"])
assert "foreach loop cannot run" in error
def test_parse_openbb_script_minus_nondigit_index_returns_error():
"""``$VAR[-abc]`` (minus + non-digit) hits the inner non-digit branch."""
error, _ = parse_openbb_script(["$VAR = a,b", "echo $VAR[-abc]"])
assert "not a value" in error
def test_parse_openbb_script_list_var_plain_reference_joins():
"""Plain ``$VAR`` reference where VAR is a list joins with comma."""
error, script = parse_openbb_script(["$LIST = a,b,c", "echo $LIST"])
assert error == ""
assert script == "/echo a,b,c"