Skip to content

Commit

Permalink
fix: CLI crash on schemas with operation names longer than the curren…
Browse files Browse the repository at this point in the history
…t terminal width

Ref: #990
  • Loading branch information
Stranger6667 committed Jan 2, 2021
1 parent 5f14f45 commit f1566f7
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 6 deletions.
2 changes: 2 additions & 0 deletions docs/changelog.rst
Expand Up @@ -42,6 +42,7 @@ Changelog
- A possibility to override ``APIStateMachine.step``. `#970`_
- ``TypeError`` on nullable parameters during Open API specific serialization. `#980`_
- Invalid types in ``x-examples``. `#982`_
- CLI crash on schemas with operation names longer than the current terminal width. `#990`_

**Performance**

Expand Down Expand Up @@ -1631,6 +1632,7 @@ Deprecated
.. _0.3.0: https://github.com/schemathesis/schemathesis/compare/v0.2.0...v0.3.0
.. _0.2.0: https://github.com/schemathesis/schemathesis/compare/v0.1.0...v0.2.0

.. _#990: https://github.com/schemathesis/schemathesis/issues/990
.. _#987: https://github.com/schemathesis/schemathesis/issues/987
.. _#982: https://github.com/schemathesis/schemathesis/issues/982
.. _#980: https://github.com/schemathesis/schemathesis/issues/980
Expand Down
15 changes: 11 additions & 4 deletions src/schemathesis/cli/output/default.py
Expand Up @@ -55,12 +55,12 @@ def display_execution_result(context: ExecutionContext, event: events.AfterExecu

def display_percentage(context: ExecutionContext, event: events.AfterExecution) -> None:
"""Add the current progress in % to the right side of the current line."""
padding = 1
endpoints_count = cast(int, context.endpoints_count) # is already initialized via `Initialized` event
current_percentage = get_percentage(context.endpoints_processed, endpoints_count)
styled = click.style(current_percentage, fg="cyan")
# Total length of the message, so it will fill to the right border of the terminal minus padding
length = get_terminal_width() - context.current_line_length + len(styled) - len(current_percentage) - padding
# Total length of the message, so it will fill to the right border of the terminal.
# Padding is already taken into account in `context.current_line_length`
length = get_terminal_width() - context.current_line_length + len(styled) - len(current_percentage)
template = f"{{:>{length}}}"
click.echo(template.format(styled))

Expand Down Expand Up @@ -302,13 +302,20 @@ def handle_initialized(context: ExecutionContext, event: events.Initialized) ->
click.echo()


TRUNCATION_PLACEHOLDER = "[...]"


def handle_before_execution(context: ExecutionContext, event: events.BeforeExecution) -> None:
"""Display what method / endpoint will be tested next."""
message = f"{event.method} {event.path} "
# We should display execution result + percentage in the end. For example:
max_length = get_terminal_width() - len(" . [XXX%]") - len(TRUNCATION_PLACEHOLDER)
message = f"{event.method} {event.path}"
if event.recursion_level > 0:
message = f"{' ' * event.recursion_level}-> {message}"
# This value is not `None` - the value is set in runtime before this line
context.endpoints_count += 1 # type: ignore

message = message[:max_length] + (message[max_length:] and "[...]") + " "
context.current_line_length = len(message)
click.echo(message, nl=False)

Expand Down
5 changes: 3 additions & 2 deletions test/cli/output/test_default.py
Expand Up @@ -158,8 +158,9 @@ def test_display_percentage(
# When percentage is displayed
default.display_percentage(execution_context, after_execution)
out = capsys.readouterr().out
# Then the whole line fits precisely to the terminal width
assert len(click.unstyle(out)) + current_line_length == default.get_terminal_width()
# Then the whole line fits precisely to the terminal width. Note `-1` is padding, that is calculated in a
# different place when the line is printed
assert len(click.unstyle(out)) + current_line_length - 1 == default.get_terminal_width()
# And the percentage displayed as expected in cyan color
assert out.strip() == strip_style_win32(click.style(percentage, fg="cyan"))

Expand Down
24 changes: 24 additions & 0 deletions test/cli/test_commands.py
Expand Up @@ -1686,3 +1686,27 @@ def test_base_url_not_required_for_dry_run(testdir, cli, openapi_version, empty_
schema_file = testdir.makefile(".yaml", schema=yaml.dump(empty_open_api_3_schema))
result = cli.run(str(schema_file), "--dry-run")
assert result.exit_code == ExitCode.OK, result.stdout


def test_long_operation_output(testdir, empty_open_api_3_schema):
# See GH-990
# When there is a narrow screen
# And the API schema contains an operation with a long name
empty_open_api_3_schema["paths"] = {
f"/{'a' * 100}": {
"get": {
"responses": {"200": {"description": "OK"}},
}
},
f"/{'a' * 10}": {
"get": {
"responses": {"200": {"description": "OK"}},
}
},
}
schema_file = testdir.makefile(".yaml", schema=yaml.dump(empty_open_api_3_schema))
result = testdir.run("schemathesis", "run", str(schema_file), "--dry-run")
# Then this operation name should be truncated
assert result.ret == ExitCode.OK
assert "GET /aaaaaaaaaa . [ 50%]" in result.outlines
assert "GET /aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[...] . [100%]" in result.outlines

0 comments on commit f1566f7

Please sign in to comment.