Source code for ramble.test.cmd.repo

# Copyright 2022-2026 The Ramble Authors
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.
import os
import shutil

import pytest

from ramble.error import RambleCommandError
from ramble.main import RambleCommand
from ramble.repository import BadRepoError

import spack.util.spack_yaml as syaml

repo = RambleCommand("repo")


[docs] def test_help_option(): with pytest.raises(SystemExit): repo("--help") assert repo.returncode in (None, 0)
[docs] def test_create_add_list_remove(mutable_config, tmpdir): # Create a new repository and check that the expected # files are there repo("create", str(tmpdir), "mockrepo") assert os.path.exists(os.path.join(str(tmpdir), "repo.yaml")) assert os.path.exists(os.path.join(str(tmpdir), "applications")) # Add the new repository and check it appears in the list output repo("add", "-t", "applications", "--scope=site", str(tmpdir)) output = repo("list", "--scope=site", output=str) assert "mockrepo" in output # Then remove it and check it's not there repo("remove", "--scope=site", str(tmpdir)) output = repo("list", "--scope=site", output=str) assert "mockrepo" not in output
[docs] @pytest.mark.parametrize("subdir", ["applications", "", "foo"]) def test_create_add_list_remove_flags(mutable_config, tmpdir, subdir): # Create a new repository and check that the expected # files are there repo("create", str(tmpdir), "mockrepo", "-d", subdir) assert os.path.exists(os.path.join(str(tmpdir), "repo.yaml")) assert os.path.exists(os.path.join(str(tmpdir), subdir)) # Add the new repository and check it appears in the list output repo("add", "-t", "applications", "--scope=site", str(tmpdir)) output = repo("list", "--scope=site", output=str) assert "mockrepo" in output # Then remove it and check it's not there repo("remove", "--scope=site", str(tmpdir)) output = repo("list", "--scope=site", output=str) assert "mockrepo" not in output
[docs] def test_add_behavior(mutable_config, tmpdir): # Create an app-only repo repo("create", str(tmpdir), "mockrepo", "-t", "applications") assert os.path.exists(os.path.join(str(tmpdir), "application_repo.yaml")) assert os.path.exists(os.path.join(str(tmpdir), "applications")) # Complains when specified repo type is not found with pytest.raises( BadRepoError, match="Failed to find valid repo with type ObjectTypes.modifiers" ): repo("add", "-t", "modifiers", "--scope=site", str(tmpdir)) output = repo("list", "--scope=site", output=str) assert "mockrepo" not in output # Do not complain when type is not specified repo("add", "--scope=site", str(tmpdir)) output = repo("list", "--scope=site", output=str) assert "mockrepo" in output # Complains if the given path contains no valid repo for all object types shutil.rmtree(os.path.join(tmpdir, "applications")) with pytest.raises(BadRepoError, match="not a valid repo for any object types"): repo("add", "--scope=site", str(tmpdir))
[docs] def test_remove_from_any_scope(mutable_config, tmpdir): """Tests that 'repo rm' without a scope removes from the correct scope.""" repo_path = str(tmpdir.join("test_repo")) repo_name = "test_repo_namespace" # Create a new repository repo("create", repo_path, repo_name) # Add the new repository to the 'site' scope repo("add", "-t", "applications", "--scope=site", repo_path) apps_repos_in_site = mutable_config.get("repos", scope="site") assert repo_path in apps_repos_in_site, "Repo should be in site config after add." # Check that it's in the list (merged scopes) output = repo("list", output=str) assert repo_name in output print(output) # Then remove it without specifying a scope repo("remove", "--scope=site", repo_path) # Check it's not in the site scope list anymore output = repo("list", "--scope=site", output=str) assert repo_name not in output print(output) apps_repos_in_site_after_remove = mutable_config.get("repos", scope="site") assert ( repo_path not in apps_repos_in_site_after_remove ), "Repo should be removed from site config."
[docs] def test_add_with_any_type_registers_all_types(mutable_config, tmpdir): """ Tests that `repo add` with `type=any` (the default) correctly registers only the repo types that are actually present in the repository. """ repo_path = str(tmpdir) repo_name = "partial_repo" # Create a repository with both applications and modifiers repo("create", repo_path, repo_name) assert os.path.exists(os.path.join(str(tmpdir), "repo.yaml")) assert os.path.exists(os.path.join(str(tmpdir), "applications")) print(repo_path) # Make the repo incomplete by removing the modifiers directory shutil.rmtree(os.path.join(repo_path, "modifiers")) # Add the repository without specifying a type (defaults to `any`) repo("add", "--scope=site", repo_path) # Check the configuration to ensure all types were registered site_repos = mutable_config.get("repos", scope="site") assert repo_path in site_repos assert repo_path in site_repos # The repo name should appear in the overall list output = repo("list", output=str) assert repo_name in output
[docs] def test_add_non_existent_repo(mutable_config, tmpdir): """Tests that `repo add` with a non-existent path dies.""" non_existent_path = str(tmpdir.join("non_existent_repo")) with pytest.raises(RambleCommandError) as e: repo("add", non_existent_path) # Check that the exception message contains the expected output expected_error_message = f"No such file or directory: {non_existent_path}" assert expected_error_message in str(e.value) # Ensure the command indeed exited with code 1 assert "Command exited with code 1" in str(e.value)
[docs] def test_add_repo_missing_config_file(mutable_config, tmpdir): """Tests that `repo add` with a directory missing repo.yaml raises BadRepoError directly.""" # Create a directory that will serve as our "repo" repo_path = tmpdir.mkdir("empty_repo") # This is normally created by `repo create`, but we are testing the case # where it's missing (or invalid) # Create a dummy repo.yaml and then remove it to simulate the missing file repo_config_file = os.path.join(str(repo_path), "repo.yaml") with open(repo_config_file, "w") as f: syaml.dump({"repo": {"namespace": "test_namespace"}}, f) os.remove(repo_config_file) with pytest.raises(BadRepoError) as e: # Catch BadRepoError directly repo("add", str(repo_path)) expected_error_message = f"No valid config file found in '{repo_path}'" assert expected_error_message in str(e.value)