Skip to content

Commit f0c0b17

Browse files
committed
add test coverage
1 parent b2090ae commit f0c0b17

File tree

3 files changed

+127
-4
lines changed

3 files changed

+127
-4
lines changed

.generator/cli.py

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import yaml
2626
from datetime import datetime
2727
from pathlib import Path
28-
from typing import Dict, List
28+
from typing import Dict, List, Optional
2929
from distutils.core import run_setup
3030

3131
import tomli
@@ -549,25 +549,78 @@ def _verify_library_namespace(library_id: str, repo: str):
549549
)
550550

551551

552-
def _get_setup_dist_name(library_id: str, repo: str):
552+
def _get_setup_dist_name(library_id: str, repo: str) -> Optional[str]:
553+
"""Gets the library distribution name from a `setup.py` file.
554+
555+
Tries to execute `run_setup` on the file and extract the 'name'
556+
property. Logs and returns None on any exception.
557+
558+
Args:
559+
library_id(str): id of the library.
560+
repo(str): This directory will contain all directories that make up a
561+
library, the .librarian folder, and any global file declared in
562+
the config.yaml.
563+
564+
Returns:
565+
Optional[str]: The library distribution name if found and parsed, otherwise None.
566+
"""
553567
try:
554568
dist = run_setup(f"{repo}/packages/{library_id}/setup.py")
555569
return dist.get_name()
556570
except Exception as e:
571+
logger.debug(
572+
f"failed to get distribution name for `{library_id}` from `setup.py` : {e}",
573+
exc_info=True,
574+
)
557575
return None
558576

559577

560-
def _get_toml_dist_name(library_id: str, repo: str):
578+
def _get_toml_dist_name(library_id: str, repo: str) -> Optional[str]:
579+
"""Gets the library distribution name from a `pyproject.toml` file.
580+
581+
Parses the TOML file and safely accesses the name from the
582+
[project] table using `.get()`. Logs and returns None on any
583+
exception (e.g., FileNotFoundError, TOMLDecodeError).
584+
585+
Args:
586+
library_id(str): id of the library.
587+
repo(str): This directory will contain all directories that make up a
588+
library, the .librarian folder, and any global file declared in
589+
the config.yaml.
590+
591+
Returns:
592+
Optional[str]: The library distribution name if found and parsed, otherwise None.
593+
"""
561594
try:
562595
pyproject_toml_file = Path(f"{repo}/packages/{library_id}/pyproject.toml")
563596
with open(pyproject_toml_file, "rb") as f:
564597
data = tomli.load(f)
565598
return data.get("project", {}).get("name")
566599
except Exception as e:
600+
logger.debug(
601+
f"failed to get distribution name for `{library_id}` from `pyproject.toml` : {e}",
602+
exc_info=True,
603+
)
567604
return None
568605

569606

570607
def _verify_library_dist_name(library_id: str, repo: str):
608+
"""Verifies the library distribution name against its config files.
609+
610+
This function ensures that:
611+
1. At least one of `setup.py` or `pyproject.toml` exists and is valid.
612+
2. Any existing config file's 'name' property matches the `library_id`.
613+
614+
Args:
615+
library_id: id of the library.
616+
repo: This directory will contain all directories that make up a
617+
library, the .librarian folder, and any global file declared in
618+
the config.yaml.
619+
620+
Raises:
621+
ValueError: If no valid config file is found, or if a name
622+
in an existing file does not match the `library_id`.
623+
"""
571624
setup_dist_name = _get_setup_dist_name(library_id, repo)
572625
toml_dist_name = _get_toml_dist_name(library_id, repo)
573626
if setup_dist_name is None and toml_dist_name is None:

.generator/requirements-test.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ pytest-cov
1717
pytest-mock
1818
gcp-synthtool @ git+https://github.com/googleapis/synthtool@5aa438a342707842d11fbbb302c6277fbf9e4655
1919
starlark-pyo3>=2025.1
20+
# TODO(https://github.com/googleapis/google-cloud-python/issues/14443): tomli is part of the standard library for Python 3.11. Remove once we use 3.11+ for librarian.
21+
tomli

.generator/test_cli.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
_get_library_id,
4242
_get_libraries_to_prepare_for_release,
4343
_get_previous_version,
44+
_get_setup_dist_name,
45+
_get_toml_dist_name,
4446
_locate_and_extract_artifact,
4547
_process_changelog,
4648
_process_version_file,
@@ -52,6 +54,7 @@
5254
_update_changelog_for_library,
5355
_update_global_changelog,
5456
_update_version_for_library,
57+
_verify_library_dist_name,
5558
_verify_library_namespace,
5659
_write_json_file,
5760
_write_text_file,
@@ -525,7 +528,8 @@ def test_handle_build_success(caplog, mocker, mock_build_request_file):
525528
caplog.set_level(logging.INFO)
526529

527530
mocker.patch("cli._run_nox_sessions")
528-
mocker.patch("cli._verify_library_namespace", return_value=True)
531+
mocker.patch("cli._verify_library_namespace")
532+
mocker.patch("cli._verify_library_dist_name")
529533
handle_build()
530534

531535
assert "'build' command executed." in caplog.text
@@ -922,6 +926,70 @@ def test_determine_library_namespace_fails_not_subpath():
922926
_determine_library_namespace(gapic_parent_path, pkg_root_path)
923927

924928

929+
def test_get_setup_dist_name_exists(mocker):
930+
"""Tests that a valid library distribution name exists in `pyproject.toml`."""
931+
mock_dist = MagicMock()
932+
mock_dist.get_name.return_value = "my-lib"
933+
mocker.patch("cli.run_setup", return_value=mock_dist)
934+
assert _get_setup_dist_name("my-lib", "repo") == "my-lib"
935+
936+
937+
def test_get_setup_dist_name_file_not_found(caplog):
938+
"""Tests that distribution name is None if `setup.py` does not exist."""
939+
caplog.set_level(logging.DEBUG)
940+
assert _get_setup_dist_name("my-lib", "repo") is None
941+
assert len(caplog.records) == 1
942+
943+
944+
def test_get_toml_dist_name_exists(mocker):
945+
"""Tests that a valid library distribution name exists in `pyproject.toml`."""
946+
mock_data = {"project": {"name": "my-lib"}}
947+
mocker.patch("tomli.load", return_value=mock_data)
948+
mocker.patch("builtins.open", mocker.mock_open(read_data=b"fake toml data"))
949+
assert _get_toml_dist_name("my-lib", "repo") == "my-lib"
950+
951+
952+
def test_get_toml_dist_name_file_not_found(caplog):
953+
"""Tests that distribution name is None if `pyproject.toml` does not exist."""
954+
caplog.set_level(logging.DEBUG)
955+
assert _get_toml_dist_name("my-lib", "repo") is None
956+
assert len(caplog.records) == 1
957+
958+
959+
def test_verify_library_dist_name_setup_success(mocker):
960+
"""Tests success when a library distribution name in setup.py is valid."""
961+
mock_setup_file = mocker.patch("cli._get_setup_dist_name", return_value="my-lib")
962+
_verify_library_dist_name("my-lib", "repo")
963+
mock_setup_file.assert_called_once_with("my-lib", "repo")
964+
965+
966+
def test_verify_library_dist_name_setup_success(mocker):
967+
"""Tests success when a library distribution name in toml is valid."""
968+
mock_toml_file = mocker.patch("cli._get_toml_dist_name", return_value="my-lib")
969+
_verify_library_dist_name("my-lib", "repo")
970+
mock_toml_file.assert_called_once_with("my-lib", "repo")
971+
972+
973+
def test_verify_library_dist_name_fail():
974+
"""Tests failure when a library does not have a `setup.py` or `pyproject.toml`."""
975+
with pytest.raises(ValueError):
976+
_verify_library_dist_name("my-lib", "repo")
977+
978+
979+
def test_verify_library_dist_name_setup_fail(mocker):
980+
"""Tests failure when a library has an invalid distribution name in `setup.py`."""
981+
mocker.patch("cli._get_setup_dist_name", return_value="invalid-lib-name")
982+
with pytest.raises(ValueError):
983+
_verify_library_dist_name("my-lib", "repo")
984+
985+
986+
def test_verify_library_dist_name_toml_fail(mocker):
987+
"""Tests failure when a library has an invalid distribution name in `pyproject.toml`."""
988+
mocker.patch("cli._get_toml_dist_name", return_value="invalid-lib-name")
989+
with pytest.raises(ValueError):
990+
_verify_library_dist_name("my-lib", "repo")
991+
992+
925993
def test_verify_library_namespace_success_valid(mocker, mock_path_class):
926994
"""Tests success when a single valid namespace is found."""
927995
# 1. Get the mock instance from the mock class's return_value

0 commit comments

Comments
 (0)