Skip to content

Commit 1a877dd

Browse files
committed
✨ feat: Make test cli
1 parent bdb4bcd commit 1a877dd

File tree

7 files changed

+188
-72
lines changed

7 files changed

+188
-72
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ order_fixtures = "cli.order_fixtures:order_fixtures"
102102
evm_bytes = "cli.evm_bytes:cli"
103103
hasher = "cli.hasher:main"
104104
env_init = "config.env:create_default_config"
105-
make_test = "cli.make.cli:test"
105+
make = "cli.make.cli:make"
106106

107107
[tool.setuptools.packages.find]
108108
where = ["src"]

src/cli/make/cli.py

Lines changed: 24 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,35 @@
11
"""
2-
CLI interface for generating blockchain test scripts.
2+
The `make` CLI streamlines the process of scaffolding tasks, such as generating new test files,
3+
enabling developers to concentrate on the core aspects of specification testing.
34
4-
It extracts a specified transaction and its required state from a blockchain network
5-
using the transaction hash and generates a Python test script based on that information.
6-
"""
7-
8-
import os
95
10-
import jinja2
6+
The module verifies the presence of a valid subcommand and calls the appropriate
7+
function for the subcommand. If an invalid subcommand is present, it throws an error
8+
and shows a list of valid subcommands. If no subcommand is present, it shows a list
9+
of valid subcommands to choose from.
10+
"""
1111

12-
from cli.input import input_select, input_text
12+
import click
1313

14-
template_loader = jinja2.PackageLoader("cli.make")
15-
template_env = jinja2.Environment(loader=template_loader, keep_trailing_newline=True)
14+
from cli.make.commands import test
1615

1716

18-
def test():
17+
@click.group()
18+
def make():
1919
"""
20-
Scaffold a test file from the command line interface (CLI).
21-
22-
This function guides the user through a series of prompts to generate a test file
23-
for Ethereum execution specifications. The user is prompted to select the type of test,
24-
the fork to use, and to provide the EIP number and name. Based on the inputs, a test file
25-
is created in the appropriate directory with a rendered template.
26-
27-
Prompts:
28-
- Choose the type of test to generate (State or Blockchain)
29-
- Select the fork to use (Prague or Osaka)
30-
- Enter the EIP number
31-
- Enter the EIP name
32-
33-
The generated test file is saved in the following format:
34-
`tests/{fork}/{eip_number}_{eip_name}/test_{eip_name}.py`
35-
36-
Example:
37-
If the user selects "State" as the test type, "Prague" as the fork,
38-
enters "1234" as the EIP number,
39-
and "Sample EIP" as the EIP name, the generated file will be:
40-
`tests/prague/1234_sample_eip/test_sample_eip.py`
41-
42-
The function uses Jinja2 templates to render the content of the test file.
43-
44-
Raises:
45-
- FileNotFoundError: If the template file does not exist.
46-
- IOError: If there is an error writing the file.
20+
The `make` CLI command helps you get started with new writing tests.
4721
"""
48-
test_type = input_select(
49-
"Choose the type of test to generate", choices=["State", "Blockchain"]
50-
)
51-
# TODO: Get forks from a config called `UPCOMING_FORKS`
52-
fork = input_select("Select the fork to use", choices=["Prague", "Osaka"])
22+
pass
5323

54-
eip_number = input_text("Enter the EIP number")
5524

56-
# TODO: Perhaps get the EIP name from the number using an API?
57-
eip_name = input_text("Enter the EIP name")
58-
59-
file_name = f"test_{eip_name.lower().replace(' ', '_')}.py"
60-
61-
directory_path = f"tests/{fork.lower()}/{eip_number}-{eip_name.lower().replace(' ', '_')}"
62-
file_path = f"{directory_path}/{file_name}"
63-
64-
# Create directories if they don't exist
65-
os.makedirs(directory_path, exist_ok=True)
66-
67-
template = template_env.get_template(f"{test_type.lower()}_test.py.j2")
68-
rendered_template = template.render(
69-
fork=fork,
70-
eip_number=eip_number,
71-
eip_name=eip_name,
72-
test_name=eip_name.lower().replace(" ", "_"),
73-
)
74-
75-
with open(file_path, "w") as file:
76-
file.write(rendered_template)
77-
78-
print(f"Test file created at: {file_path}")
25+
"""
26+
################################
27+
|| ||
28+
|| Command Registration ||
29+
|| ||
30+
################################
31+
32+
Register nested commands here. For more information, see Click documentation:
33+
https://click.palletsprojects.com/en/8.0.x/commands/#nested-handling-and-contexts
34+
"""
35+
make.add_command(test)

src/cli/make/commands/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""
2+
This subpackage holds subcommands for the make command. New subcommands must be created as
3+
modules and exported from this package, then registered under the make command in
4+
`cli.py`.
5+
"""
6+
7+
from .test import test
8+
9+
__all__ = ["test"]

src/cli/make/commands/quotes.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""
2+
This module contains a list of quotes related to system design.
3+
"""
4+
5+
import random
6+
import textwrap
7+
8+
make_something_great = [
9+
"🎨 Simplicity is the ultimate sophistication. - Leonardo D.",
10+
"🖌️ Simplicity is an acquired taste. - Katharine G.",
11+
"💡 To create a memorable design you need to start with a thought that’s worth remembering."
12+
" - Thomas M.",
13+
"✏️ Designers are crazy and yet sane enough to know where to draw the line. - Benjamin W.",
14+
"🌟 Creativity is piercing the mundane to find the marvelous. - Bill M.",
15+
"🔍 Mistakes are the portals of discovery. - James J.",
16+
"🧠 It’s extremely difficult to be simultaneously concerned with the end-user experience of"
17+
" whatever it is that you’re building and the architecture of the program that delivers that"
18+
"experience. - James H.",
19+
"🧠 Good design is a lot like clear thinking made visual. - Edward T.",
20+
"🚀 Innovation leads one to see the new in the old and distinguishes the ingenious from the"
21+
" ingenuous. - Paul R.",
22+
"🔮 The best way to predict the future is to invent it. - Alan K.",
23+
"🌟 Perfection is achieved, not when there is nothing more to add, but when there is nothing"
24+
" left to take away. - Antoine d.",
25+
"📏 You can’t improve what you don’t measure. - Tom D.",
26+
]
27+
28+
29+
def wrap_quote(quote, width=80):
30+
"""
31+
Wraps the quote text to the given width.
32+
"""
33+
return textwrap.fill(quote, width=width)
34+
35+
36+
def box_quote(quote):
37+
"""
38+
Returns a quote wrapped in a box with borders.
39+
"""
40+
# Wrap the quote first
41+
wrapped_quote = wrap_quote(quote)
42+
43+
# Calculate the width of the box
44+
box_width = max(len(line) for line in wrapped_quote.split("\n")) + 2 # +2 for side borders
45+
46+
# Create top and bottom border
47+
top_bottom_border = "+" + "-" * (box_width) + "+"
48+
49+
# Create the sides of the box
50+
lines = wrapped_quote.split("\n")
51+
boxed_lines = [f"{line.ljust(box_width - 2)}" for line in lines]
52+
53+
# Return the full boxed quote
54+
quote = "\n".join([top_bottom_border] + boxed_lines + [top_bottom_border])
55+
return f"\n {quote} \n"
56+
57+
58+
def get_quote():
59+
"""
60+
Returns a random inspirational quote related to system design formatted in a box.
61+
"""
62+
return box_quote(random.choice(make_something_great))

src/cli/make/commands/test.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""
2+
This module provides a CLI command to scaffold a test file.
3+
4+
The `test` command guides the user through a series of prompts to generate a test file
5+
based on the selected test type, fork, EIP number, and EIP name. The generated test file
6+
is saved in the appropriate directory with a rendered template using Jinja2.
7+
"""
8+
9+
import os
10+
11+
import click
12+
import jinja2
13+
14+
from cli.input import input_select, input_text
15+
16+
from .quotes import get_quote
17+
18+
template_loader = jinja2.PackageLoader("cli.make")
19+
template_env = jinja2.Environment(loader=template_loader, keep_trailing_newline=True)
20+
21+
22+
@click.command()
23+
def test():
24+
"""
25+
Create a new specification test file for an EIP.
26+
27+
This function guides the user through a series of prompts to generate a test file
28+
for Ethereum execution specifications. The user is prompted to select the type of test,
29+
the fork to use, and to provide the EIP number and name. Based on the inputs, a test file
30+
is created in the appropriate directory with a rendered template.
31+
32+
Prompts:
33+
- Choose the type of test to generate (State or Blockchain)
34+
- Select the fork to use (Prague or Osaka)
35+
- Enter the EIP number
36+
- Enter the EIP name
37+
38+
The generated test file is saved in the following format:
39+
`tests/{fork}/eip{eip_number}_{eip_name}/test_{eip_name}.py`
40+
41+
Example:
42+
If the user selects "State" as the test type, "Prague" as the fork,
43+
enters "1234" as the EIP number,
44+
and "Sample EIP" as the EIP name, the generated file will be:
45+
`tests/prague/eip1234_sample_eip/test_sample_eip.py`
46+
47+
The function uses Jinja2 templates to render the content of the test file.
48+
49+
Raises:
50+
- FileNotFoundError: If the template file does not exist.
51+
- IOError: If there is an error writing the file.
52+
"""
53+
test_type = input_select(
54+
"Choose the type of test to generate", choices=["State", "Blockchain"]
55+
)
56+
# TODO: Get forks from a config called `UPCOMING_FORKS`
57+
fork = input_select("Select the fork to use", choices=["Prague", "Osaka"])
58+
59+
eip_number = input_text("Enter the EIP number").strip()
60+
61+
# TODO: Perhaps get the EIP name from the number using an API?
62+
eip_name = input_text("Enter the EIP name").strip()
63+
64+
file_name = f"test_{eip_name.lower().replace(' ', '_')}.py"
65+
66+
directory_path = f"tests/{fork.lower()}/eip{eip_number}_{eip_name.lower().replace(' ', '_')}"
67+
file_path = f"{directory_path}/{file_name}"
68+
69+
# Create directories if they don't exist
70+
os.makedirs(directory_path, exist_ok=True)
71+
72+
template = template_env.get_template(f"{test_type.lower()}_test.py.j2")
73+
rendered_template = template.render(
74+
fork=fork,
75+
eip_number=eip_number,
76+
eip_name=eip_name,
77+
test_name=eip_name.lower().replace(" ", "_"),
78+
)
79+
80+
with open(file_path, "w") as file:
81+
file.write(rendered_template)
82+
83+
print(f"\n 🎉 Success! Test file created at: {file_path}")
84+
print(get_quote())

src/cli/make/templates/blockchain_test.py.j2

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
A blockchain test for EIP-{{eip_number}} [fork: `{{fork}}`].
2+
A blockchain test for [EIP-{{eip_number}} {{eip_name}}](https://eips.ethereum.org/EIPS/eip-{eip_number}}).
33
"""
44

55
import pytest
@@ -10,7 +10,9 @@ from ethereum_test_tools import Alloc, Environment, BlockchainTestFiller, Transa
1010
@pytest.mark.valid_from("{{fork}}")
1111
def test_{{test_name}}(blockchain_test: BlockchainTestFiller, pre: Alloc):
1212
"""
13-
Test type 1 transaction.
13+
TODO: Enter a one-line test summary here.
14+
15+
TODO: (Optional) Enter a more detailed test function description here.
1416
"""
1517
env = Environment()
1618

src/cli/make/templates/state_test.py.j2

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
A state test for EIP-{{eip_number}} [fork: `{{fork}}`].
2+
A state test for [EIP-{{eip_number}} {{eip_name}}](https://eips.ethereum.org/EIPS/eip-{eip_number}}).
33
"""
44

55
import pytest
@@ -10,7 +10,9 @@ from ethereum_test_tools import Alloc, Environment, StateTestFiller, Transaction
1010
@pytest.mark.valid_from("{{fork}}")
1111
def test_{{test_name}}(state_test: StateTestFiller, pre: Alloc):
1212
"""
13-
Test type 1 transaction.
13+
TODO: Enter a one-line test summary here.
14+
15+
TODO: (Optional) Enter a more detailed test function description here.
1416
"""
1517
env = Environment()
1618

0 commit comments

Comments
 (0)