@@ -59,7 +59,7 @@ All code examples in this plan follow these guidelines and must be maintained th
5959# ## A. Enhance Exception Handling
6060
61611. **Create Specific Exception Types**
62- - Create a hierarchy of exceptions with specific subtypes in `src/vcspull/exc.py`:
62+ - ✓ Create a hierarchy of exceptions with specific subtypes in `src/vcspull/exc.py`:
6363 ```python
6464 import enum
6565 import typing as t
@@ -3676,3 +3676,278 @@ When implementing Pydantic models, follow these guidelines:
36763676 - Examples for all major features
36773677
36783678# # 3. Additional Tests to Add
3679+
3680+ # ## 11. Testing Pydantic Models and Validators
3681+
3682+ 1. **✓ Basic Model Validation Tests**
3683+ - ✓ Add tests for `RepositoryModel` validation:
3684+ ```python
3685+ import pytest
3686+ import typing as t
3687+
3688+ from vcspull.schemas import RepositoryModel
3689+
3690+ def test_repository_model_valid():
3691+ """Test valid repository model."""
3692+ # Create a valid model
3693+ repo = RepositoryModel(
3694+ vcs="git",
3695+ name="test-repo",
3696+ path="/path/to/repo",
3697+ url="https://github.com/user/repo",
3698+ )
3699+
3700+ # Verify basic attributes
3701+ assert repo.vcs == "git"
3702+ assert repo.name == "test-repo"
3703+ assert str(repo.path).endswith("/path/to/repo")
3704+ assert repo.url == "https://github.com/user/repo"
3705+
3706+ def test_repository_model_invalid_vcs():
3707+ """Test invalid VCS type."""
3708+ with pytest.raises(ValueError) as excinfo:
3709+ RepositoryModel(
3710+ vcs="invalid",
3711+ name="test-repo",
3712+ path="/path/to/repo",
3713+ url="https://github.com/user/repo",
3714+ )
3715+
3716+ # Verify error message
3717+ assert "Invalid VCS type" in str(excinfo.value)
3718+ ```
3719+
3720+ 2. **Pending: Path Validation Tests**
3721+ - Create tests for path validation and normalization:
3722+ ```python
3723+ import os
3724+ import pathlib
3725+
3726+ def test_repository_model_path_expansion():
3727+ """Test path expansion in repository model."""
3728+ # Test with environment variables
3729+ os.environ["TEST_PATH"] = "/test/path"
3730+ repo = RepositoryModel(
3731+ vcs="git",
3732+ name="test-repo",
3733+ path="${TEST_PATH}/repo",
3734+ url="https://github.com/user/repo",
3735+ )
3736+
3737+ # Verify path expansion
3738+ assert str(repo.path) == "/test/path/repo"
3739+
3740+ # Test with tilde expansion
3741+ repo = RepositoryModel(
3742+ vcs="git",
3743+ name="test-repo",
3744+ path="~/repo",
3745+ url="https://github.com/user/repo",
3746+ )
3747+
3748+ # Verify tilde expansion
3749+ assert str(repo.path) == str(pathlib.Path.home() / "repo")
3750+ ```
3751+
3752+ 3. **Pending: URL Validation Tests**
3753+ - Test different URL formats and validation:
3754+ ```python
3755+ def test_repository_model_url_validation():
3756+ """Test URL validation in repository model."""
3757+ # Test valid URLs
3758+ valid_urls = [
3759+ "https://github.com/user/repo",
3760+ "[email protected] :user/repo.git", 3761+ "file:///path/to/repo",
3762+ ]
3763+
3764+ for url in valid_urls:
3765+ repo = RepositoryModel(
3766+ vcs =" git" ,
3767+ name =" test-repo" ,
3768+ path =" /path/to/repo" ,
3769+ url =url,
3770+ )
3771+ assert repo.url == url
3772+
3773+ # Test invalid URLs
3774+ invalid_urls = [" " , " " ]
3775+
3776+ for url in invalid_urls:
3777+ with pytest.raises(ValueError) as excinfo:
3778+ RepositoryModel(
3779+ vcs =" git" ,
3780+ name =" test-repo" ,
3781+ path =" /path/to/repo" ,
3782+ url =url,
3783+ )
3784+ assert "URL cannot be empty" in str(excinfo.value)
3785+ ```
3786+
3787+ 4. **Pending: Configuration Dict Model Tests**
3788+ - Test the dictionary-like behavior of config models:
3789+ ```python
3790+ from vcspull.schemas import ConfigSectionDictModel, RepositoryModel
3791+
3792+ def test_config_section_dict_model():
3793+ """Test ConfigSectionDictModel behavior."""
3794+ # Create repository models
3795+ repo1 = RepositoryModel(
3796+ vcs =" git" ,
3797+ name =" repo1" ,
3798+ path =" /path/to/repo1" ,
3799+ url =" https://github.com/user/repo1" ,
3800+ )
3801+
3802+ repo2 = RepositoryModel(
3803+ vcs =" git" ,
3804+ name =" repo2" ,
3805+ path =" /path/to/repo2" ,
3806+ url =" https://github.com/user/repo2" ,
3807+ )
3808+
3809+ # Create section model
3810+ section = ConfigSectionDictModel(root={"repo1": repo1, "repo2": repo2})
3811+
3812+ # Test dictionary-like access
3813+ assert section["repo1"] == repo1
3814+ assert section["repo2"] == repo2
3815+
3816+ # Test keys, values, items
3817+ assert set(section.keys()) == {"repo1", "repo2"}
3818+ assert list(section.values()) == [repo1, repo2]
3819+ assert dict(section.items()) == {"repo1": repo1, "repo2": repo2}
3820+ ```
3821+
3822+ 5. **Pending: Raw to Validated Conversion Tests**
3823+ - Test conversion from raw to validated models:
3824+ ```python
3825+ from vcspull.schemas import (
3826+ RawConfigDictModel,
3827+ convert_raw_to_validated,
3828+ )
3829+
3830+ def test_convert_raw_to_validated():
3831+ """Test conversion from raw to validated models."""
3832+ # Create raw config
3833+ raw_config = RawConfigDictModel(root={
3834+ "section1": {
3835+ "repo1": {
3836+ "vcs": "git",
3837+ "name": "repo1",
3838+ "path": "/path/to/repo1",
3839+ "url": "https://github.com/user/repo1",
3840+ },
3841+ "repo2": "https://github.com/user/repo2", # Shorthand URL
3842+ }
3843+ })
3844+
3845+ # Convert to validated config
3846+ validated = convert_raw_to_validated(raw_config)
3847+
3848+ # Verify structure
3849+ assert "section1" in validated.root
3850+ assert "repo1" in validated["section1"].root
3851+ assert "repo2" in validated["section1"].root
3852+
3853+ # Verify expanded shorthand URL
3854+ assert validated["section1"]["repo2"].url == "https://github.com/user/repo2"
3855+ assert validated["section1"]["repo2"].name == "repo2"
3856+ ```
3857+
3858+ 6. **Pending: Integration with CLI Tests**
3859+ - Test CLI commands with Pydantic models:
3860+ ```python
3861+ def test_cli_with_pydantic_models(runner, tmp_path):
3862+ """Test CLI commands with Pydantic models."""
3863+ # Create a test config file with valid and invalid entries
3864+ config_file = tmp_path / "config.yaml"
3865+ config_file.write_text("""
3866+ section1:
3867+ repo1:
3868+ vcs: git
3869+ name: repo1
3870+ path: {tmp_path}/repo1
3871+ url: https://github.com/user/repo1
3872+ repo2:
3873+ vcs: invalid # Invalid VCS type
3874+ name: repo2
3875+ path: {tmp_path}/repo2
3876+ url: https://github.com/user/repo2
3877+ """.format(tmp_path=tmp_path))
3878+
3879+ # Run CLI command with the config file
3880+ result = runner.invoke(cli, ["sync", "--config", str(config_file)])
3881+
3882+ # Verify that the valid repository is processed
3883+ assert "Processing repository repo1" in result.output
3884+
3885+ # Verify that the invalid repository is reported with a Pydantic error
3886+ assert "Invalid VCS type: invalid" in result.output
3887+ ```
3888+
3889+ 7. **Pending: Error Handling in Models**
3890+ - Test error handling and error formatting:
3891+ ```python
3892+ from vcspull.validator import format_pydantic_errors
3893+ from pydantic import ValidationError
3894+
3895+ def test_format_pydantic_errors():
3896+ """Test formatting of Pydantic validation errors."""
3897+ try:
3898+ RepositoryModel(
3899+ vcs =" invalid" ,
3900+ name =" " , # Empty name
3901+ path =" " , # Empty path
3902+ url =" " , # Empty URL
3903+ )
3904+ except ValidationError as e:
3905+ # Format the error
3906+ error_msg = format_pydantic_errors(e)
3907+
3908+ # Verify formatted error message
3909+ assert "vcs: Invalid VCS type" in error_msg
3910+ assert "name: " in error_msg
3911+ assert "path: " in error_msg
3912+ assert "url: URL cannot be empty" in error_msg
3913+ ```
3914+
3915+ 8. **Pending: Advanced Validation Tests**
3916+ - Create tests for more complex validation scenarios:
3917+ ```python
3918+ def test_repository_model_with_remotes():
3919+ """Test repository model with Git remotes."""
3920+ from vcspull.schemas import GitRemote
3921+
3922+ # Create Git remotes
3923+ remotes = {
3924+ "origin": GitRemote(
3925+ name =" origin" ,
3926+ url =" https://github.com/user/repo" ,
3927+ fetch =" +refs/heads/*:refs/remotes/origin/*" ,
3928+ push =" refs/heads/*:refs/heads/*" ,
3929+ ),
3930+ "upstream": GitRemote(
3931+ name =" upstream" ,
3932+ url =" https://github.com/upstream/repo" ,
3933+ ),
3934+ }
3935+
3936+ # Create repository with remotes
3937+ repo = RepositoryModel(
3938+ vcs =" git" ,
3939+ name =" test-repo" ,
3940+ path =" /path/to/repo" ,
3941+ url =" https://github.com/user/repo" ,
3942+ remotes =remotes,
3943+ )
3944+
3945+ # Verify remotes
3946+ assert repo.remotes is not None
3947+ assert "origin" in repo.remotes
3948+ assert "upstream" in repo.remotes
3949+ assert repo.remotes["origin"].url == "https://github.com/user/repo"
3950+ assert repo.remotes["upstream"].url == "https://github.com/upstream/repo"
3951+ ```
3952+
3953+ # # 12. Performance Testing
0 commit comments