mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-29 21:42:22 +00:00
[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>
This commit is contained in:
@@ -3,13 +3,12 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from openbb_cli.controllers.script_parser import (
|
||||
match_and_return_openbb_keyword_date,
|
||||
parse_openbb_script,
|
||||
)
|
||||
|
||||
# pylint: disable=import-outside-toplevel, unused-variable, line-too-long
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"command, expected",
|
||||
@@ -100,3 +99,273 @@ def test_date_keyword_last_friday():
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user