Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ In the interactive mode, you can use commands like:
- `/users 100` - Set the number of concurrent users
- `/run` - Start the load test

## Non-interactive mode

Run a saved YAML config directly without entering the interactive shell:

```bash
chakload config path/to/config.yaml run
chakload config path/to/config.yaml show
```

## Command Reference

- `/help` - Show available commands
Expand Down
7 changes: 7 additions & 0 deletions configs/example-httpbin-smoke.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
custom_params: {}
duration: 30
framework: simple
rampup: 10
target_url: https://httpbin.org/get
test_type: web-site
users: 50
64 changes: 48 additions & 16 deletions loadtester/cli.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""CLI interface for the loadtester tool."""
import typer
from typing import Optional
from pathlib import Path
import sys
import yaml
from rich.console import Console
from rich.text import Text
from rich.panel import Panel
Expand All @@ -16,6 +18,7 @@
from loadtester.frameworks.k6_runner import K6Runner
from loadtester.frameworks import get_available_frameworks
from loadtester.config import Config, load_default_config
from loadtester.views import print_config, print_saved_configs
from loadtester.utils.logger import logger

app = typer.Typer()
Expand Down Expand Up @@ -76,6 +79,30 @@ def interactive_command_selector():
return command
return None

@app.command("config", help="Load a config from a path and run/show it")
def config_cmd(
path: Path = typer.Argument(..., exists=True, dir_okay=False, readable=True,
help="Path to the YAML config file"),
action: str = typer.Argument("run", help="What to do with the config: run | show"),
):
"""Load a config directly from a file path and either run or show it."""
with open(path, "r", encoding="utf-8") as f:
data = yaml.safe_load(f)
config.from_dict(data)

action = action.lower()
if action == "show":
print_config(config)
return
if action == "run":
print_config(config)
handle_run_command()
return

typer.echo(f"Unknown action: {action}. Use: run | show")
raise typer.Exit(code=1)


@app.command("interactive", help="Start the interactive CLI interface")
def interactive_cmd():
"""Start the interactive CLI interface."""
Expand Down Expand Up @@ -353,22 +380,27 @@ def run_test_in_thread():

def handle_config_command(args):
"""Handle configuration commands."""
if not args:
typer.echo(f"Current config:")
typer.echo(f" Framework: {config.framework}")
typer.echo(f" Test Type: {config.test_type}")
typer.echo(f" URL: {config.target_url}")
typer.echo(f" Users: {config.users}")
typer.echo(f" Duration: {config.duration}s")
typer.echo(f" Ramp-up: {config.rampup}s")
elif args[0].lower() == 'show':
typer.echo(f"Current config:")
typer.echo(f" Framework: {config.framework}")
typer.echo(f" Test Type: {config.test_type}")
typer.echo(f" URL: {config.target_url}")
typer.echo(f" Users: {config.users}")
typer.echo(f" Duration: {config.duration}s")
typer.echo(f" Ramp-up: {config.rampup}s")
if not args or args[0].lower() == 'show':
print_config(config)
return

if args[0].lower() == 'save':
if len(args) < 2:
typer.echo("Usage: /config save <name>")
return
path = config.save_config(args[1])
typer.echo(f"✓ Config saved to: {path}")
elif args[0].lower() == 'load':
if len(args) < 2:
typer.echo("Usage: /config load <name>")
return
try:
config.load_config(args[1])
except FileNotFoundError:
typer.echo(f"❌ Config '{args[1]}' not found.")
return
typer.echo(f"✓ Config '{args[1]}' loaded.")
print_config(config)
else:
typer.echo(f"Config command '{args[0]}' not yet implemented in this MVP.")

Expand Down
4 changes: 4 additions & 0 deletions loadtester/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Presentation layer: formatting and printing helpers."""
from loadtester.views.config_view import print_config, print_saved_configs

__all__ = ["print_config", "print_saved_configs"]
Binary file not shown.
Binary file not shown.
23 changes: 23 additions & 0 deletions loadtester/views/config_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Printers for configuration-related output."""
import typer


def print_config(cfg):
"""Print the current configuration state."""
typer.echo("Current config:")
typer.echo(f" Framework: {cfg.framework}")
typer.echo(f" Test Type: {cfg.test_type}")
typer.echo(f" URL: {cfg.target_url}")
typer.echo(f" Users: {cfg.users}")
typer.echo(f" Duration: {cfg.duration}s")
typer.echo(f" Ramp-up: {cfg.rampup}s")


def print_saved_configs(names):
"""Print the list of saved configuration presets."""
if not names:
typer.echo("No saved configs. Use /config save <name>.")
return
typer.echo("Saved configs:")
for n in names:
typer.echo(f" - {n}")