@@ -49,5 +49,109 @@ def test_table_module_importable(self) -> None:
4949 self .assertTrue (callable (format_table ))
5050
5151
52+ class TestReorderArgv (unittest .TestCase ):
53+ """Verify _reorder_argv moves global flags before subcommands."""
54+
55+ def _reorder (self , argv : list [str ]) -> list [str ]:
56+ from roboflow .cli import _reorder_argv
57+
58+ return _reorder_argv (argv )
59+
60+ def test_no_flags (self ) -> None :
61+ self .assertEqual (self ._reorder (["project" , "list" ]), ["project" , "list" ])
62+
63+ def test_empty (self ) -> None :
64+ self .assertEqual (self ._reorder ([]), [])
65+
66+ def test_bool_flag_after_subcommand (self ) -> None :
67+ result = self ._reorder (["project" , "list" , "--json" ])
68+ self .assertEqual (result , ["--json" , "project" , "list" ])
69+
70+ def test_bool_flag_already_first (self ) -> None :
71+ result = self ._reorder (["--json" , "project" , "list" ])
72+ self .assertEqual (result , ["--json" , "project" , "list" ])
73+
74+ def test_short_bool_flag (self ) -> None :
75+ result = self ._reorder (["project" , "list" , "-j" ])
76+ self .assertEqual (result , ["-j" , "project" , "list" ])
77+
78+ def test_value_flag_after_subcommand (self ) -> None :
79+ result = self ._reorder (["project" , "list" , "--api-key" , "abc123" ])
80+ self .assertEqual (result , ["--api-key" , "abc123" , "project" , "list" ])
81+
82+ def test_short_value_flag (self ) -> None :
83+ result = self ._reorder (["project" , "list" , "-k" , "abc123" ])
84+ self .assertEqual (result , ["-k" , "abc123" , "project" , "list" ])
85+
86+ def test_multiple_flags_mixed (self ) -> None :
87+ result = self ._reorder (["project" , "list" , "--json" , "-w" , "my-ws" ])
88+ self .assertEqual (result , ["--json" , "-w" , "my-ws" , "project" , "list" ])
89+
90+ def test_value_flag_at_end_without_value (self ) -> None :
91+ """A value flag at the very end with no following arg should still be moved."""
92+ result = self ._reorder (["project" , "list" , "--api-key" ])
93+ self .assertEqual (result , ["--api-key" , "project" , "list" ])
94+
95+ def test_non_global_flags_preserved (self ) -> None :
96+ """Flags not in the global set stay in place."""
97+ result = self ._reorder (["image" , "upload" , "--project" , "my-proj" , "--json" ])
98+ self .assertEqual (result , ["--json" , "image" , "upload" , "--project" , "my-proj" ])
99+
100+ def test_quiet_and_version_flags (self ) -> None :
101+ result = self ._reorder (["project" , "list" , "--quiet" , "--version" ])
102+ self .assertEqual (result , ["--quiet" , "--version" , "project" , "list" ])
103+
104+ def test_workspace_flag (self ) -> None :
105+ result = self ._reorder (["project" , "list" , "--workspace" , "ws-1" ])
106+ self .assertEqual (result , ["--workspace" , "ws-1" , "project" , "list" ])
107+
108+ def test_preserves_subcommand_positional_args (self ) -> None :
109+ result = self ._reorder (["version" , "download" , "ws/proj/3" , "--json" , "-f" , "yolov8" ])
110+ self .assertEqual (result , ["--json" , "version" , "download" , "ws/proj/3" , "-f" , "yolov8" ])
111+
112+
113+ class TestAliases (unittest .TestCase ):
114+ """Verify top-level aliases parse correctly and delegate to the right handler."""
115+
116+ def _parse (self , argv : list [str ]):
117+ from roboflow .cli import build_parser
118+
119+ parser = build_parser ()
120+ return parser .parse_args (argv )
121+
122+ def test_login_alias_exists (self ) -> None :
123+ args = self ._parse (["login" ])
124+ self .assertIsNotNone (args .func )
125+
126+ def test_whoami_alias_exists (self ) -> None :
127+ args = self ._parse (["whoami" ])
128+ self .assertIsNotNone (args .func )
129+
130+ def test_upload_alias_exists (self ) -> None :
131+ args = self ._parse (["upload" , "img.jpg" , "-p" , "my-project" ])
132+ self .assertIsNotNone (args .func )
133+ self .assertEqual (args .path , "img.jpg" )
134+ self .assertEqual (args .project , "my-project" )
135+
136+ def test_import_alias_exists (self ) -> None :
137+ args = self ._parse (["import" , "/data/images" , "-p" , "my-project" ])
138+ self .assertIsNotNone (args .func )
139+ self .assertEqual (args .path , "/data/images" )
140+ self .assertEqual (args .project , "my-project" )
141+
142+ def test_download_alias_parses_url (self ) -> None :
143+ """Regression: download alias must use url_or_id as dest, not datasetUrl."""
144+ args = self ._parse (["download" , "my-ws/my-proj/3" ])
145+ self .assertIsNotNone (args .func )
146+ self .assertEqual (args .url_or_id , "my-ws/my-proj/3" )
147+
148+ def test_download_alias_delegates_to_version_download (self ) -> None :
149+ """The download alias should use the same handler as 'version download'."""
150+ from roboflow .cli .handlers .version import _download
151+
152+ args = self ._parse (["download" , "my-ws/my-proj/3" ])
153+ self .assertIs (args .func , _download )
154+
155+
52156if __name__ == "__main__" :
53157 unittest .main ()
0 commit comments