Skip to content

Commit 1fd5cb6

Browse files
committed
return cli tests
1 parent 50d87a3 commit 1fd5cb6

13 files changed

+1370
-0
lines changed

tests/cli/__init__.py

Whitespace-only changes.

tests/cli/conftest.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from typing import Callable
2+
import pytest
3+
import os
4+
from _pytest.pytester import Testdir, RunResult
5+
6+
7+
@pytest.fixture
8+
def run(testdir: Testdir) -> Callable[..., RunResult]:
9+
def do_run(*args):
10+
args = ["rasa"] + list(args)
11+
return testdir.run(*args)
12+
13+
return do_run
14+
15+
16+
@pytest.fixture
17+
def run_with_stdin(testdir: Testdir) -> Callable[..., RunResult]:
18+
def do_run(*args, stdin):
19+
args = ["rasa"] + list(args)
20+
return testdir.run(*args, stdin=stdin)
21+
22+
return do_run
23+
24+
25+
@pytest.fixture
26+
def run_in_default_project(testdir: Testdir) -> Callable[..., RunResult]:
27+
os.environ["LOG_LEVEL"] = "ERROR"
28+
testdir.run("rasa", "init", "--no-prompt")
29+
30+
def do_run(*args):
31+
args = ["rasa"] + list(args)
32+
return testdir.run(*args)
33+
34+
return do_run

tests/cli/test_cli.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import pytest
2+
from typing import Callable
3+
from _pytest.pytester import RunResult
4+
5+
6+
def test_cli_start(run: Callable[..., RunResult]):
7+
"""
8+
Measures an average startup time and checks that it
9+
does not deviate more than x seconds from 5.
10+
"""
11+
import time
12+
13+
durations = []
14+
15+
for i in range(5):
16+
start = time.time()
17+
run("--help")
18+
end = time.time()
19+
20+
durations.append(end - start)
21+
22+
avg_duration = sum(durations) / len(durations)
23+
24+
# When run in parallel, it takes a little longer
25+
assert avg_duration - 5 <= 2
26+
27+
28+
def test_data_convert_help(run: Callable[..., RunResult]):
29+
output = run("--help")
30+
31+
help_text = """usage: rasa [-h] [--version]
32+
{init,run,shell,train,interactive,test,visualize,data,x} ..."""
33+
34+
lines = help_text.split("\n")
35+
36+
for i, line in enumerate(lines):
37+
assert output.outlines[i] == line

tests/cli/test_rasa_data.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import os
2+
import pytest
3+
from collections import namedtuple
4+
from typing import Callable
5+
from _pytest.pytester import RunResult
6+
from rasa.cli import data
7+
8+
9+
def test_data_split_nlu(run_in_default_project: Callable[..., RunResult]):
10+
run_in_default_project(
11+
"data", "split", "nlu", "-u", "data/nlu.md", "--training-fraction", "0.75"
12+
)
13+
14+
assert os.path.exists("train_test_split")
15+
assert os.path.exists(os.path.join("train_test_split", "test_data.md"))
16+
assert os.path.exists(os.path.join("train_test_split", "training_data.md"))
17+
18+
19+
def test_data_convert_nlu(run_in_default_project: Callable[..., RunResult]):
20+
run_in_default_project(
21+
"data",
22+
"convert",
23+
"nlu",
24+
"--data",
25+
"data/nlu.md",
26+
"--out",
27+
"out_nlu_data.json",
28+
"-f",
29+
"json",
30+
)
31+
32+
assert os.path.exists("out_nlu_data.json")
33+
34+
35+
def test_data_split_help(run: Callable[..., RunResult]):
36+
output = run("data", "split", "nlu", "--help")
37+
38+
help_text = """usage: rasa data split nlu [-h] [-v] [-vv] [--quiet] [-u NLU]
39+
[--training-fraction TRAINING_FRACTION]
40+
[--random-seed RANDOM_SEED] [--out OUT]"""
41+
42+
lines = help_text.split("\n")
43+
44+
for i, line in enumerate(lines):
45+
assert output.outlines[i] == line
46+
47+
48+
def test_data_convert_help(run: Callable[..., RunResult]):
49+
output = run("data", "convert", "nlu", "--help")
50+
51+
help_text = """usage: rasa data convert nlu [-h] [-v] [-vv] [--quiet] --data DATA --out OUT
52+
[-l LANGUAGE] -f {json,md}"""
53+
54+
lines = help_text.split("\n")
55+
56+
for i, line in enumerate(lines):
57+
assert output.outlines[i] == line
58+
59+
60+
def test_data_validate_help(run: Callable[..., RunResult]):
61+
output = run("data", "validate", "--help")
62+
63+
help_text = """usage: rasa data validate [-h] [-v] [-vv] [--quiet] [--fail-on-warnings]
64+
[-d DOMAIN] [--data DATA]"""
65+
66+
lines = help_text.split("\n")
67+
68+
for i, line in enumerate(lines):
69+
assert output.outlines[i] == line
70+
71+
72+
def test_validate_files_exit_early():
73+
with pytest.raises(SystemExit) as pytest_e:
74+
args = {"domain": "data/test_domains/duplicate_intents.yml", "data": None}
75+
data.validate_files(namedtuple("Args", args.keys())(*args.values()))
76+
77+
assert pytest_e.type == SystemExit
78+
assert pytest_e.value.code == 1

tests/cli/test_rasa_init.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import os
2+
from typing import Callable
3+
from _pytest.pytester import RunResult
4+
5+
6+
def test_init(run: Callable[..., RunResult]):
7+
run("init", "--no-prompt", "--quiet")
8+
9+
assert os.path.exists("actions.py")
10+
assert os.path.exists("domain.yml")
11+
assert os.path.exists("config.yml")
12+
assert os.path.exists("credentials.yml")
13+
assert os.path.exists("endpoints.yml")
14+
assert os.path.exists("models")
15+
assert os.path.exists("data/nlu.md")
16+
assert os.path.exists("data/stories.md")
17+
18+
19+
def test_init_using_init_dir_option(run: Callable[..., RunResult]):
20+
os.makedirs("./workspace")
21+
run("init", "--no-prompt", "--quiet", "--init-dir", "./workspace")
22+
23+
assert os.path.exists("./workspace/actions.py")
24+
assert os.path.exists("./workspace/domain.yml")
25+
assert os.path.exists("./workspace/config.yml")
26+
assert os.path.exists("./workspace/credentials.yml")
27+
assert os.path.exists("./workspace/endpoints.yml")
28+
assert os.path.exists("./workspace/models")
29+
assert os.path.exists("./workspace/data/nlu.md")
30+
assert os.path.exists("./workspace/data/stories.md")
31+
32+
33+
def test_not_fount_init_path(run: Callable[..., RunResult]):
34+
output = run("init", "--no-prompt", "--quiet", "--init-dir", "./workspace")
35+
36+
assert (
37+
output.outlines[-1]
38+
== "\033[91mProject init path './workspace' not found.\033[0m"
39+
)
40+
41+
42+
def test_init_help(run: Callable[..., RunResult]):
43+
output = run("init", "--help")
44+
45+
assert (
46+
output.outlines[0]
47+
== "usage: rasa init [-h] [-v] [-vv] [--quiet] [--no-prompt] [--init-dir INIT_DIR]"
48+
)
49+
50+
51+
def test_user_asked_to_train_model(run_with_stdin: Callable[..., RunResult]):
52+
run_with_stdin("init", stdin=b"\nYN")
53+
assert not os.path.exists("models")

tests/cli/test_rasa_interactive.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import argparse
2+
import pytest
3+
from typing import Callable, Text
4+
from unittest.mock import Mock
5+
6+
from _pytest.monkeypatch import MonkeyPatch
7+
from _pytest.pytester import RunResult
8+
9+
import rasa
10+
from rasa.cli import interactive, train
11+
12+
13+
def test_interactive_help(run: Callable[..., RunResult]):
14+
output = run("interactive", "--help")
15+
16+
help_text = """usage: rasa interactive [-h] [-v] [-vv] [--quiet] [--e2e] [-m MODEL]
17+
[--data DATA [DATA ...]] [--skip-visualization]
18+
[--endpoints ENDPOINTS] [-c CONFIG] [-d DOMAIN]
19+
[--out OUT] [--augmentation AUGMENTATION]
20+
[--debug-plots] [--dump-stories] [--force]
21+
[--persist-nlu-data]
22+
{core} ... [model-as-positional-argument]"""
23+
24+
lines = help_text.split("\n")
25+
26+
for i, line in enumerate(lines):
27+
assert output.outlines[i] == line
28+
29+
30+
def test_interactive_core_help(run: Callable[..., RunResult]):
31+
output = run("interactive", "core", "--help")
32+
33+
help_text = """usage: rasa interactive core [-h] [-v] [-vv] [--quiet] [-m MODEL] [-s STORIES]
34+
[--skip-visualization] [--endpoints ENDPOINTS]
35+
[-c CONFIG] [-d DOMAIN] [--out OUT]
36+
[--augmentation AUGMENTATION] [--debug-plots]
37+
[--dump-stories]
38+
[model-as-positional-argument]"""
39+
40+
lines = help_text.split("\n")
41+
42+
for i, line in enumerate(lines):
43+
assert output.outlines[i] == line
44+
45+
46+
def test_pass_arguments_to_rasa_train(
47+
default_stack_config: Text, monkeypatch: MonkeyPatch
48+
) -> None:
49+
# Create parser
50+
parser = argparse.ArgumentParser()
51+
sub_parser = parser.add_subparsers()
52+
interactive.add_subparser(sub_parser, [])
53+
54+
# Parse interactive command
55+
args = parser.parse_args(["interactive", "--config", default_stack_config])
56+
interactive._set_not_required_args(args)
57+
58+
# Mock actual training
59+
mock = Mock()
60+
monkeypatch.setattr(rasa, "train", mock.method)
61+
62+
# If the `Namespace` object does not have all required fields this will throw
63+
train.train(args)
64+
65+
# Assert `train` was actually called
66+
mock.method.assert_called_once()
67+
68+
69+
def test_train_called_when_no_model_passed(
70+
default_stack_config: Text, monkeypatch: MonkeyPatch
71+
) -> None:
72+
parser = argparse.ArgumentParser()
73+
sub_parser = parser.add_subparsers()
74+
interactive.add_subparser(sub_parser, [])
75+
76+
args = parser.parse_args(
77+
[
78+
"interactive",
79+
"--config",
80+
default_stack_config,
81+
"--data",
82+
"examples/moodbot/data",
83+
]
84+
)
85+
interactive._set_not_required_args(args)
86+
87+
# Mock actual training and interactive learning methods
88+
mock = Mock()
89+
monkeypatch.setattr(train, "train", mock.train_model)
90+
monkeypatch.setattr(
91+
interactive, "perform_interactive_learning", mock.perform_interactive_learning
92+
)
93+
94+
interactive.interactive(args)
95+
mock.train_model.assert_called_once()
96+
97+
98+
def test_train_core_called_when_no_model_passed_and_core(
99+
default_stack_config: Text, monkeypatch: MonkeyPatch
100+
) -> None:
101+
parser = argparse.ArgumentParser()
102+
sub_parser = parser.add_subparsers()
103+
interactive.add_subparser(sub_parser, [])
104+
105+
args = parser.parse_args(
106+
[
107+
"interactive",
108+
"core",
109+
"--config",
110+
default_stack_config,
111+
"--stories",
112+
"examples/moodbot/data/stories.md",
113+
"--domain",
114+
"examples/moodbot/domain.yml",
115+
]
116+
)
117+
interactive._set_not_required_args(args)
118+
119+
# Mock actual training and interactive learning methods
120+
mock = Mock()
121+
monkeypatch.setattr(train, "train_core", mock.train_core)
122+
monkeypatch.setattr(
123+
interactive, "perform_interactive_learning", mock.perform_interactive_learning
124+
)
125+
126+
interactive.interactive(args)
127+
mock.train_core.assert_called_once()
128+
129+
130+
def test_no_interactive_without_core_data(
131+
default_stack_config: Text, monkeypatch: MonkeyPatch
132+
) -> None:
133+
parser = argparse.ArgumentParser()
134+
sub_parser = parser.add_subparsers()
135+
interactive.add_subparser(sub_parser, [])
136+
137+
args = parser.parse_args(
138+
[
139+
"interactive",
140+
"--config",
141+
default_stack_config,
142+
"--data",
143+
"examples/moodbot/data/nlu.md",
144+
]
145+
)
146+
interactive._set_not_required_args(args)
147+
148+
mock = Mock()
149+
monkeypatch.setattr(train, "train", mock.train_model)
150+
monkeypatch.setattr(
151+
interactive, "perform_interactive_learning", mock.perform_interactive_learning
152+
)
153+
154+
with pytest.raises(SystemExit):
155+
interactive.interactive(args)
156+
157+
mock.train_model.assert_not_called()
158+
mock.perform_interactive_learning.assert_not_called()

0 commit comments

Comments
 (0)