@@ -1630,15 +1630,15 @@ def test_push_shows_up_to_date_for_unchanged_files(
16301630 assert "Updating 'main.py'" not in result .output
16311631 assert "Updating 'helper.py'" not in result .output
16321632
1633- def test_push_uploads_root_evals_folder_files (
1633+ def test_push_skips_root_evals_folder_files (
16341634 self ,
16351635 runner : CliRunner ,
16361636 temp_dir : str ,
16371637 project_details : ProjectDetails ,
16381638 mock_env_vars : dict [str , str ],
16391639 httpx_mock : HTTPXMock ,
16401640 ) -> None :
1641- """Test that files directly in the evals folder are uploaded during push."""
1641+ """Test that files in root evals folder are skipped during push."""
16421642 base_url = "https://cloud.uipath.com/organization"
16431643 project_id = "test-project-id"
16441644
@@ -1693,11 +1693,11 @@ def test_push_uploads_root_evals_folder_files(
16931693 os .makedirs ("evals/eval-sets" )
16941694 os .makedirs ("evals/evaluators" )
16951695
1696- # Create files in root evals folder (should be included )
1696+ # Create files in root evals folder (should be skipped )
16971697 with open ("evals/root_file1.json" , "w" ) as f :
1698- f .write ('{"should": "be included "}' )
1698+ f .write ('{"should": "be skipped "}' )
16991699 with open ("evals/root_file2.py" , "w" ) as f :
1700- f .write ("print('should be included ')" )
1700+ f .write ("print('should be skipped ')" )
17011701
17021702 # Create files in eval-sets (should be included)
17031703 with open ("evals/eval-sets/test_eval.json" , "w" ) as f :
@@ -1713,10 +1713,9 @@ def test_push_uploads_root_evals_folder_files(
17131713 result = runner .invoke (cli , ["push" , "./" , "--ignore-resources" ])
17141714 assert result .exit_code == 0
17151715
1716- # Verify root evals files are uploaded
1717- assert "Uploading 'root_file1.json'" in result .output
1718- assert "Uploading 'root_file2.py'" in result .output
1719- assert "Skipping" not in result .output
1716+ # Verify root evals files are skipped
1717+ assert "Skipping 'evals/root_file1.json' (evals folder)" in result .output
1718+ assert "Skipping 'evals/root_file2.py' (evals folder)" in result .output
17201719
17211720 # Verify eval-sets and evaluators files are uploaded
17221721 assert "Uploading 'test_eval.json'" in result .output
@@ -1725,6 +1724,169 @@ def test_push_uploads_root_evals_folder_files(
17251724 # Verify main.py is uploaded
17261725 assert "Uploading 'main.py'" in result .output
17271726
1727+ def test_push_skips_only_root_evals_files_not_subdirectories (
1728+ self ,
1729+ runner : CliRunner ,
1730+ temp_dir : str ,
1731+ project_details : ProjectDetails ,
1732+ mock_env_vars : dict [str , str ],
1733+ httpx_mock : HTTPXMock ,
1734+ ) -> None :
1735+ """Test that only files directly in evals folder are skipped, not files in subdirectories."""
1736+ base_url = "https://cloud.uipath.com/organization"
1737+ project_id = "test-project-id"
1738+
1739+ mock_structure = {
1740+ "id" : "root" ,
1741+ "name" : "root" ,
1742+ "folders" : [],
1743+ "files" : [
1744+ {
1745+ "id" : "123" ,
1746+ "name" : "pyproject.toml" ,
1747+ "isMain" : False ,
1748+ "fileType" : "1" ,
1749+ "isEntryPoint" : False ,
1750+ "ignoredFromPublish" : False ,
1751+ },
1752+ ],
1753+ "folderType" : "0" ,
1754+ }
1755+
1756+ httpx_mock .add_response (
1757+ url = f"{ base_url } /studio_/backend/api/Project/{ project_id } /FileOperations/Structure" ,
1758+ json = mock_structure ,
1759+ )
1760+
1761+ self ._mock_lock_retrieval (httpx_mock , base_url , project_id , times = 1 )
1762+ self ._mock_file_download (httpx_mock , "123" )
1763+
1764+ httpx_mock .add_response (
1765+ method = "POST" ,
1766+ url = f"{ base_url } /studio_/backend/api/Project/{ project_id } /FileOperations/StructuralMigration" ,
1767+ status_code = 200 ,
1768+ json = {"success" : True },
1769+ )
1770+
1771+ httpx_mock .add_response (
1772+ url = f"{ base_url } /studio_/backend/api/Project/{ project_id } /FileOperations/Structure" ,
1773+ json = mock_structure ,
1774+ )
1775+
1776+ with runner .isolated_filesystem (temp_dir = temp_dir ):
1777+ self ._create_required_files ()
1778+
1779+ with open ("pyproject.toml" , "w" ) as f :
1780+ f .write (project_details .to_toml ())
1781+
1782+ # Create evals folder with various subdirectories
1783+ os .makedirs ("evals/eval-sets" )
1784+ os .makedirs ("evals/evaluators" )
1785+ os .makedirs ("evals/custom-subfolder" )
1786+
1787+ # Files directly in evals (should be skipped)
1788+ with open ("evals/config.json" , "w" ) as f :
1789+ f .write ('{"root": "file"}' )
1790+
1791+ # Files in subdirectories (should be included)
1792+ with open ("evals/eval-sets/test.json" , "w" ) as f :
1793+ f .write ('{"eval": "set"}' )
1794+ with open ("evals/evaluators/eval.py" , "w" ) as f :
1795+ f .write ("def evaluate(): pass" )
1796+ with open ("evals/custom-subfolder/data.json" , "w" ) as f :
1797+ f .write ('{"custom": "data"}' )
1798+
1799+ configure_env_vars (mock_env_vars )
1800+ os .environ ["UIPATH_PROJECT_ID" ] = project_id
1801+
1802+ result = runner .invoke (cli , ["push" , "./" , "--ignore-resources" ])
1803+ assert result .exit_code == 0
1804+
1805+ # Verify root file is skipped
1806+ assert "Skipping 'evals/config.json' (evals folder)" in result .output
1807+
1808+ # Verify subdirectory files are included
1809+ assert "Uploading 'test.json'" in result .output
1810+ assert "Uploading 'eval.py'" in result .output
1811+ assert "Uploading 'data.json'" in result .output
1812+
1813+ def test_push_logs_skipped_files_count (
1814+ self ,
1815+ runner : CliRunner ,
1816+ temp_dir : str ,
1817+ project_details : ProjectDetails ,
1818+ mock_env_vars : dict [str , str ],
1819+ httpx_mock : HTTPXMock ,
1820+ ) -> None :
1821+ """Test that push logs the count of skipped files from root evals folder."""
1822+ base_url = "https://cloud.uipath.com/organization"
1823+ project_id = "test-project-id"
1824+
1825+ mock_structure = {
1826+ "id" : "root" ,
1827+ "name" : "root" ,
1828+ "folders" : [],
1829+ "files" : [
1830+ {
1831+ "id" : "123" ,
1832+ "name" : "pyproject.toml" ,
1833+ "isMain" : False ,
1834+ "fileType" : "1" ,
1835+ "isEntryPoint" : False ,
1836+ "ignoredFromPublish" : False ,
1837+ },
1838+ ],
1839+ "folderType" : "0" ,
1840+ }
1841+
1842+ httpx_mock .add_response (
1843+ url = f"{ base_url } /studio_/backend/api/Project/{ project_id } /FileOperations/Structure" ,
1844+ json = mock_structure ,
1845+ )
1846+
1847+ self ._mock_lock_retrieval (httpx_mock , base_url , project_id , times = 1 )
1848+ self ._mock_file_download (httpx_mock , "123" )
1849+
1850+ httpx_mock .add_response (
1851+ method = "POST" ,
1852+ url = f"{ base_url } /studio_/backend/api/Project/{ project_id } /FileOperations/StructuralMigration" ,
1853+ status_code = 200 ,
1854+ json = {"success" : True },
1855+ )
1856+
1857+ httpx_mock .add_response (
1858+ url = f"{ base_url } /studio_/backend/api/Project/{ project_id } /FileOperations/Structure" ,
1859+ json = mock_structure ,
1860+ )
1861+
1862+ with runner .isolated_filesystem (temp_dir = temp_dir ):
1863+ self ._create_required_files ()
1864+
1865+ with open ("pyproject.toml" , "w" ) as f :
1866+ f .write (project_details .to_toml ())
1867+
1868+ # Create evals folder with multiple files
1869+ os .makedirs ("evals" )
1870+
1871+ # Create 3 files in root evals
1872+ with open ("evals/file1.json" , "w" ) as f :
1873+ f .write ("{}" )
1874+ with open ("evals/file2.py" , "w" ) as f :
1875+ f .write ("pass" )
1876+ with open ("evals/file3.yaml" , "w" ) as f :
1877+ f .write ("key: value" )
1878+
1879+ configure_env_vars (mock_env_vars )
1880+ os .environ ["UIPATH_PROJECT_ID" ] = project_id
1881+
1882+ result = runner .invoke (cli , ["push" , "./" , "--ignore-resources" ])
1883+ assert result .exit_code == 0
1884+
1885+ # Verify individual files are skipped
1886+ assert "Skipping 'evals/file1.json' (evals folder)" in result .output
1887+ assert "Skipping 'evals/file2.py' (evals folder)" in result .output
1888+ assert "Skipping 'evals/file3.yaml' (evals folder)" in result .output
1889+
17281890 def test_push_preserves_remote_evals_when_no_local_evals (
17291891 self ,
17301892 runner : CliRunner ,
0 commit comments