@@ -536,3 +536,123 @@ def test_find_repo_by_name_no_priority_config(tmp_path):
536536 assert repo is not None
537537 # Should return one of them (order not guaranteed without priority)
538538 assert repo ["name" ] == "mongo-python-driver"
539+
540+
541+ # ---------------------------------------------------------------------------
542+ # list_repos flat mode tests
543+ # ---------------------------------------------------------------------------
544+
545+
546+ @pytest .fixture
547+ def flat_repos_dir (tmp_path ):
548+ """Create a flat-layout repos directory with repos from two groups."""
549+ base = tmp_path / "repos"
550+ base .mkdir ()
551+ for name in ["django" , "django-mongodb-backend" , "mongo-python-driver" ]:
552+ repo = base / name
553+ repo .mkdir ()
554+ (repo / ".git" ).mkdir ()
555+ return base
556+
557+
558+ def _flat_config (base_dir ):
559+ return {
560+ "repo" : {
561+ "base_dir" : str (base_dir ),
562+ "flat" : True ,
563+ "global_groups" : [],
564+ "groups" : {
565+ "django" : {
566+ "repos" : [
567+ "git@github.com:mongodb-forks/django.git" ,
568+ "git@github.com:mongodb-labs/django-mongodb-backend.git" ,
569+ ]
570+ },
571+ "pymongo" : {
572+ "repos" : ["git@github.com:mongodb/mongo-python-driver.git" ]
573+ },
574+ },
575+ }
576+ }
577+
578+
579+ def test_list_repos_flat_shows_group_headers (flat_repos_dir ):
580+ """Flat mode renders group names as tree headers."""
581+ config = _flat_config (flat_repos_dir )
582+ result = list_repos (flat_repos_dir , config = config )
583+
584+ assert result is not None
585+ assert "django/" in result
586+ assert "pymongo/" in result
587+
588+
589+ def test_list_repos_flat_repos_under_correct_group (flat_repos_dir ):
590+ """Repos appear under their config group in flat mode."""
591+ config = _flat_config (flat_repos_dir )
592+ result = list_repos (flat_repos_dir , config = config )
593+
594+ assert result is not None
595+ lines = result .splitlines ()
596+ django_idx = next (i for i , line in enumerate (lines ) if "django/" in line )
597+ pymongo_idx = next (i for i , line in enumerate (lines ) if "pymongo/" in line )
598+
599+ # django-mongodb-backend should appear between the django/ header and pymongo/ header
600+ dmb_idx = next (
601+ i for i , line in enumerate (lines ) if "django-mongodb-backend" in line
602+ )
603+ mpd_idx = next (i for i , line in enumerate (lines ) if "mongo-python-driver" in line )
604+
605+ assert django_idx < dmb_idx < pymongo_idx
606+ assert mpd_idx > pymongo_idx
607+
608+
609+ def test_list_repos_flat_cloned_status (flat_repos_dir ):
610+ """Cloned repos show ✓, available-only repos show ○ in flat mode."""
611+ config = _flat_config (flat_repos_dir )
612+ # Add a repo to config that isn't cloned
613+ config ["repo" ]["groups" ]["pymongo" ]["repos" ].append (
614+ "git@github.com:mongodb/specifications.git"
615+ )
616+ result = list_repos (flat_repos_dir , config = config )
617+
618+ assert result is not None
619+ assert "✓" in result # cloned repos
620+ assert "○" in result # specifications not cloned
621+
622+
623+ def test_list_repos_flat_unknown_repo (flat_repos_dir ):
624+ """Repos cloned but not in config show ? and appear ungrouped."""
625+ base = flat_repos_dir
626+ mystery = base / "mystery-repo"
627+ mystery .mkdir ()
628+ (mystery / ".git" ).mkdir ()
629+
630+ config = _flat_config (base )
631+ result = list_repos (base , config = config )
632+
633+ assert result is not None
634+ assert "mystery-repo" in result
635+ assert "?" in result
636+
637+
638+ def test_list_repos_flat_empty_base (tmp_path ):
639+ """Flat mode with no cloned repos but config groups returns a tree."""
640+ base = tmp_path / "repos"
641+ base .mkdir ()
642+ config = _flat_config (base )
643+ result = list_repos (base , config = config )
644+
645+ assert result is not None
646+ assert "django/" in result
647+ assert "pymongo/" in result
648+ assert "○" in result # all available, none cloned
649+
650+
651+ def test_list_repos_flat_tree_characters (flat_repos_dir ):
652+ """Flat mode output uses tree-drawing characters."""
653+ config = _flat_config (flat_repos_dir )
654+ result = list_repos (flat_repos_dir , config = config )
655+
656+ assert result is not None
657+ assert "├──" in result or "└──" in result
658+ assert "│" in result or " " in result
0 commit comments