diff --git a/main.py b/main.py index c835ff1b..9ebbdb3e 100644 --- a/main.py +++ b/main.py @@ -628,7 +628,7 @@ def get_password( # --------------------------------------------------------------------------- # -# 2. Clients +# 2. Clients (configured with secure defaults) # --------------------------------------------------------------------------- # def _api_client() -> httpx.Client: return httpx.Client( @@ -2396,6 +2396,29 @@ def row(c): return f"{Colors.BOLD}│{Colors.ENDC} {c[0]:<{w[0]}} {Colors.BOLD} print(f"{line('└', '┴', '┘')}\n") +def print_success_message(profile_ids: List[str]) -> None: + """Prints a random success message and a link to the Control D dashboard.""" + if not USE_COLORS: + return + + success_msgs = [ + "✨ All synced!", + "🚀 Ready for liftoff!", + "🎨 Beautifully done!", + "💎 Smooth operation!", + "🌈 Perfect harmony!", + ] + print(f"\n{Colors.GREEN}{random.choice(success_msgs)}{Colors.ENDC}") + + # Construct dashboard URL + if profile_ids and len(profile_ids) == 1 and profile_ids[0] != "dry-run-placeholder": + dashboard_url = f"https://controld.com/dashboard/profiles/{profile_ids[0]}/filters" + print(f"{Colors.CYAN}👀 View your changes: {Colors.UNDERLINE}{dashboard_url}{Colors.ENDC}") + elif len(profile_ids) > 1: + dashboard_url = "https://controld.com/dashboard/profiles" + print(f"{Colors.CYAN}👀 View your changes: {Colors.UNDERLINE}{dashboard_url}{Colors.ENDC}") + + def parse_args() -> argparse.Namespace: """ Parses command-line arguments for the Control D sync tool. @@ -2721,15 +2744,8 @@ def make_col_separator(left, mid, right, horiz): print(make_col_separator(Box.BL, Box.B, Box.BR, Box.H)) # Success Delight - if all_success and USE_COLORS and not args.dry_run: - success_msgs = [ - "✨ All synced!", - "🚀 Ready for liftoff!", - "🎨 Beautifully done!", - "💎 Smooth operation!", - "🌈 Perfect harmony!", - ] - print(f"\n{Colors.GREEN}{random.choice(success_msgs)}{Colors.ENDC}") + if all_success and not args.dry_run: + print_success_message(profile_ids) # Dry Run Next Steps if args.dry_run: diff --git a/tests/test_ux.py b/tests/test_ux.py index bc1385e7..8de33326 100644 --- a/tests/test_ux.py +++ b/tests/test_ux.py @@ -82,3 +82,66 @@ def test_countdown_timer_no_colors_long(monkeypatch): assert mock_log.info.call_count == 2 mock_log.info.assert_any_call("LongWait: 15s remaining...") mock_log.info.assert_any_call("LongWait: 5s remaining...") + + +def test_print_success_message_single_profile(monkeypatch): + """Verify success message includes dashboard link for single profile.""" + # Force colors on + monkeypatch.setattr(main, "USE_COLORS", True) + # Monkeypatch Colors attributes because they are computed at import time + monkeypatch.setattr(main.Colors, "CYAN", "\033[96m") + monkeypatch.setattr(main.Colors, "UNDERLINE", "\033[4m") + monkeypatch.setattr(main.Colors, "ENDC", "\033[0m") + monkeypatch.setattr(main.Colors, "GREEN", "\033[92m") + + # Mock stdout + mock_stdout = MagicMock() + # print() writes to sys.stdout by default + monkeypatch.setattr(sys, "stdout", mock_stdout) + + profile_ids = ["123456"] + main.print_success_message(profile_ids) + + # Check calls + writes = [args[0] for args, _ in mock_stdout.write.call_args_list] + combined_output = "".join(writes) + + # Verify content + # Note: The output is ANSI colored, so exact string matching might fail if color codes are interspersed + # But "View your changes" should be there + assert "View your changes" in combined_output + assert "https://controld.com/dashboard/profiles/123456/filters" in combined_output + # Check for color codes presence (cyan or underline) + assert "\033[96m" in combined_output or "\033[4m" in combined_output + +def test_print_success_message_multiple_profiles(monkeypatch): + """Verify success message includes general dashboard link for multiple profiles.""" + monkeypatch.setattr(main, "USE_COLORS", True) + # Monkeypatch Colors attributes + monkeypatch.setattr(main.Colors, "CYAN", "\033[96m") + monkeypatch.setattr(main.Colors, "UNDERLINE", "\033[4m") + monkeypatch.setattr(main.Colors, "ENDC", "\033[0m") + monkeypatch.setattr(main.Colors, "GREEN", "\033[92m") + + mock_stdout = MagicMock() + monkeypatch.setattr(sys, "stdout", mock_stdout) + + profile_ids = ["123", "456"] + main.print_success_message(profile_ids) + + writes = [args[0] for args, _ in mock_stdout.write.call_args_list] + combined_output = "".join(writes) + + assert "View your changes" in combined_output + assert "https://controld.com/dashboard/profiles" in combined_output + assert "/123/filters" not in combined_output # Should not link to specific profile + +def test_print_success_message_no_colors(monkeypatch): + """Verify nothing is printed if colors are disabled.""" + monkeypatch.setattr(main, "USE_COLORS", False) + mock_stdout = MagicMock() + monkeypatch.setattr(sys, "stdout", mock_stdout) + + main.print_success_message(["123"]) + + mock_stdout.write.assert_not_called()