Skip to content

Commit

Permalink
fix: support the multiple file outputs of goal clerk split (#346)
Browse files Browse the repository at this point in the history
  • Loading branch information
neilcampbell authored Nov 10, 2023
1 parent 573d521 commit fd9cd54
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 23 deletions.
60 changes: 38 additions & 22 deletions src/algokit/core/goal.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,40 +41,56 @@ def list_files_in_volume(volume_path: Path) -> list[str]:

def preprocess_command_args(
command: list[str], volume_mount_path_local: Path, docker_mount_path_local: Path
) -> tuple[list[str], list[Path], list[str]]:
input_filenames = []
output_filenames = []
) -> tuple[list[Path], list[Path], list[str]]:
input_files = []
output_files = []
try:
for i, arg in enumerate(command):
if is_path_or_filename(arg):
arg_path = Path(arg)
arg_changed = docker_mount_path_local.joinpath(arg_path.name)
absolute_arg_path = Path(arg).expanduser().absolute()
arg_changed = docker_mount_path_local.joinpath(absolute_arg_path.name)
command[i] = str(arg_changed)

file_exists = arg_path.exists() or Path.cwd().joinpath(arg_path.name).exists()
is_output_arg = i > 0 and command[i - 1] in ["-o", "--outdir", "--outfile"]
file_exists = absolute_arg_path.exists()
is_output_arg = i > 0 and command[i - 1] in [
"-o",
"--outdir",
"--outfile",
"--out",
"--result-out",
"--lsig-out",
]
if file_exists and not is_output_arg:
input_filenames.append(arg_path.name)
shutil.copy(arg_path, volume_mount_path_local)
elif is_output_arg: # it is an output file that is not exist now
output_filenames.append(arg_path)
input_files.append(absolute_arg_path)
shutil.copy(absolute_arg_path, volume_mount_path_local)
elif is_output_arg: # it is an output file that doesn't exist yet
output_files.append(absolute_arg_path)
else:
raise FileNotFoundError(f"{arg} does not exist.")
except Exception as e:
logger.error(e)
raise e
return input_filenames, output_filenames, command
return input_files, output_files, command


def post_process(input_filenames: list, output_filenames: list[Path], volume_mount_path_local: Path) -> None:
for input_filename in input_filenames:
delete_files_from_volume_mount(input_filename, volume_mount_path_local)
def post_process(input_files: list[Path], output_files: list[Path], volume_mount_path_local: Path) -> None:
for input_file in input_files:
delete_files_from_volume_mount(input_file.name, volume_mount_path_local)

files_in_volume_mount = {Path(file).name for file in list_files_in_volume(volume_mount_path_local)}
for output_filename in output_filenames:
if output_filename.name in files_in_volume_mount:
target_path = (
output_filename if output_filename.is_absolute() else Path.cwd().joinpath(output_filename.name)
files_in_volume_mount = {Path(file) for file in list_files_in_volume(volume_mount_path_local)}
for output_file in output_files:
stem = output_file.stem
ext = output_file.suffix

# Copy outputs split into multiple files. For example `goal clerk split -i ./input.gtxn -o ./output.txn`
# will produce a file (output-0.txn etc) for each transaction in the group being split.
r = re.compile(rf"^(?:{stem})(?:-[0-9]+)?(?:\{ext})$") if ext else re.compile(rf"^(?:{stem})(?:-[0-9]+)?$")

matched_files_in_volume_mount = filter(lambda f: (r.match(f.name)), files_in_volume_mount)

for matched_file_in_volume_mount in matched_files_in_volume_mount:
shutil.copy(
volume_mount_path_local.joinpath(matched_file_in_volume_mount.name),
output_file.parent.joinpath(matched_file_in_volume_mount.name),
)
shutil.copy(volume_mount_path_local.joinpath(output_filename.name), target_path)
delete_files_from_volume_mount(output_filename.name, volume_mount_path_local)
delete_files_from_volume_mount(matched_file_in_volume_mount.name, volume_mount_path_local)
45 changes: 44 additions & 1 deletion tests/goal/test_goal.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ def test_goal_postprocess_of_command_args(
result = invoke("goal clerk compile approval.teal -o approval.compiled", cwd=cwd)
assert result.exit_code == 0

# check if the output files is no longer in the goal_mount_path
# check if the output files are no longer in the goal_mount_path
assert not (mocked_goal_mount_path / "approval.compiled").exists()

# check if the input/output file is in the cwd
Expand All @@ -369,6 +369,49 @@ def test_goal_postprocess_of_command_args(
assert (mocked_goal_mount_path / "approval.group.sig.out").exists()


@pytest.mark.usefixtures("_setup_input_files", "_setup_latest_dummy_compose")
@pytest.mark.parametrize("_setup_input_files", [[{"name": "group.gtxn", "content": ""}]], indirect=True)
def test_goal_postprocess_of_single_output_arg_resulting_in_multiple_output_files(
proc_mock: ProcMock,
cwd: Path,
mocked_goal_mount_path: Path,
) -> None:
expected_arguments = [
"docker",
"exec",
"--interactive",
"--workdir",
"/root",
"algokit_algod",
"goal",
"clerk",
"split",
]

def dump_files(cwd: Path) -> None:
(cwd / "group-0.txn").touch()
(cwd / "group-1.txn").touch()

proc_mock.set_output(
expected_arguments,
output=["Wrote transaction"],
side_effect=dump_files,
side_effect_args={"cwd": mocked_goal_mount_path},
)

result = invoke("goal clerk split -i group.gtxn -o group.txn", cwd=cwd)
assert result.exit_code == 0

# check if the output files are no longer in the goal_mount_path
assert not (mocked_goal_mount_path / "group-0.txn").exists()
assert not (mocked_goal_mount_path / "group-1.txn").exists()

# check if the input/output file is in the cwd
assert (cwd / "group.gtxn").exists()
assert (cwd / "group-0.txn").exists()
assert (cwd / "group-1.txn").exists()


@pytest.mark.usefixtures("proc_mock")
def test_goal_compose_outdated(
cwd: Path,
Expand Down

1 comment on commit fd9cd54

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/algokit
   __init__.py15753%6–13, 17–24, 32–34
   __main__.py220%1–3
src/algokit/cli
   completions.py108298%83, 98
   deploy.py72790%44, 46, 89–91, 155, 179
   dispenser.py118199%75
   doctor.py48394%142–144
   generate.py62198%116
   goal.py39197%57
   init.py1901692%273–274, 324, 327–329, 340, 384, 410, 450, 459–461, 464–469, 482
   localnet.py93397%162, 183–184
src/algokit/cli/common
   utils.py26292%120, 123
src/algokit/cli/tasks
   assets.py821384%65–66, 72, 74–75, 105, 119, 125–126, 132, 134, 136–137
   ipfs.py541180%54, 84, 89–91, 96, 98–99, 109–111
   mint.py66494%48, 70, 91, 247
   send_transaction.py651085%52–53, 57, 89, 158, 170–174
   sign_transaction.py59886%21, 28–30, 71–72, 109, 123
   transfer.py37392%25, 89, 116
   utils.py994555%26–34, 40–43, 75–76, 100–101, 125–133, 152–162, 209, 258–259, 279–290, 297–299
   vanity_address.py561082%41, 45–48, 112, 114, 121–123
   wallet.py79495%21, 66, 136, 162
src/algokit/core
   bootstrap.py1612485%103–104, 126, 149, 214, 217, 223–237, 246–251
   conf.py54885%10, 24, 28, 36, 38, 71–73
   deploy.py691184%61–64, 73–75, 79, 84, 91–93
   dispenser.py2022687%91, 123–124, 141–149, 191–192, 198–200, 218–219, 259–260, 318, 332–334, 345–346, 356, 369, 384
   doctor.py65789%67–69, 92–94, 134
   generate.py41295%68, 86
   goal.py59395%27–28, 38
   log_handlers.py68790%50–51, 63, 112–116, 125
   proc.py45198%98
   sandbox.py1831592%100–107, 118, 285, 301, 316–318, 334
   typed_client_generation.py80594%55–57, 70, 75
   utils.py18289%27–28
   version_prompt.py73889%27–28, 40, 59–62, 80, 109
src/algokit/core/tasks
   ipfs.py57493%133, 137, 139, 145
   nfd.py491373%25, 31, 34–41, 70–72, 99–101
   vanity_address.py903462%49–50, 54, 59–75, 92–108, 128–131
   wallet.py71593%37, 129, 155–157
src/algokit/core/tasks/mint
   mint.py781087%123–133, 187
   models.py901188%50, 52, 57, 71–74, 85–88
TOTAL314634989% 

Tests Skipped Failures Errors Time
378 0 💤 0 ❌ 0 🔥 37.796s ⏱️

Please sign in to comment.