Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 35 additions & 19 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,25 +159,6 @@
return safe


def render_progress_bar(
current: int, total: int, label: str, prefix: str = "🚀"
) -> None:
if not USE_COLORS or total == 0:
return

width = 20
progress = min(1.0, current / total)
filled = int(width * progress)
bar = "█" * filled + "░" * (width - filled)
percent = int(progress * 100)

# Use \033[K to clear line residue
sys.stderr.write(
f"\r\033[K{Colors.CYAN}{prefix} {label}: [{bar}] {percent}% ({current}/{total}){Colors.ENDC}"
)
sys.stderr.flush()


def countdown_timer(seconds: int, message: str = "Waiting") -> None:
"""Shows a countdown timer if strictly in a TTY, otherwise just sleeps."""
if not USE_COLORS:
Expand Down Expand Up @@ -1033,6 +1014,40 @@
return folder_success


def print_dry_run_plan(plan_entry: Dict[str, Any]) -> None:

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Missing function or method docstring Warning

Missing function or method docstring

Check warning

Code scanning / Pylint (reported by Codacy)

Missing function docstring Warning

Missing function docstring
profile = sanitize_for_log(plan_entry["profile"])
folders = plan_entry["folders"]

if USE_COLORS:
print(
f"\n{Colors.HEADER}📝 Dry Run Plan for Profile: {Colors.BOLD}{profile}{Colors.ENDC}"
)
print(
f"{Colors.CYAN} The following folders will be created/replaced:{Colors.ENDC}"
)
else:
print(f"\n📝 Dry Run Plan for Profile: {profile}")
print(" The following folders will be created/replaced:")

if not folders:
print(
f" {Colors.WARNING}(No folders found to sync){Colors.ENDC}"
if USE_COLORS
else " (No folders found to sync)"
)

for folder in folders:
safe_name = sanitize_for_log(folder["name"])
safe_rules = sanitize_for_log(folder["rules"])
if USE_COLORS:
print(
f" • {Colors.BOLD}{safe_name}{Colors.ENDC}: {safe_rules} rules"
)
else:
print(f" - {safe_name}: {safe_rules} rules")
print("")


# --------------------------------------------------------------------------- #
# 4. Main workflow
# --------------------------------------------------------------------------- #
Expand Down Expand Up @@ -1129,6 +1144,7 @@
plan_accumulator.append(plan_entry)

if dry_run:
print_dry_run_plan(plan_entry)
log.info("Dry-run complete: no API calls were made.")
return True

Expand Down
89 changes: 89 additions & 0 deletions tests/test_palette_ux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import sys

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Missing module docstring Warning test

Missing module docstring

Check warning

Code scanning / Pylint (reported by Codacy)

Missing module docstring Warning test

Missing module docstring
from unittest.mock import MagicMock
import main

def test_print_dry_run_plan_with_colors(monkeypatch):
"""Verify print_dry_run_plan output with colors enabled."""
# Force colors on
monkeypatch.setattr(main, "USE_COLORS", True)

# Since Colors class is evaluated at import time, we need to manually set color codes
# if the module was imported in a non-TTY environment
for attr, code in {
"HEADER": "\033[95m",

Check warning

Code scanning / Pylint (reported by Codacy)

Wrong hanging indentation before block (add 4 spaces). Warning test

Wrong hanging indentation before block (add 4 spaces).
"BLUE": "\033[94m",

Check warning

Code scanning / Pylint (reported by Codacy)

Wrong hanging indentation before block (add 4 spaces). Warning test

Wrong hanging indentation before block (add 4 spaces).
"CYAN": "\033[96m",

Check warning

Code scanning / Pylint (reported by Codacy)

Wrong hanging indentation before block (add 4 spaces). Warning test

Wrong hanging indentation before block (add 4 spaces).
"GREEN": "\033[92m",

Check warning

Code scanning / Pylint (reported by Codacy)

Wrong hanging indentation before block (add 4 spaces). Warning test

Wrong hanging indentation before block (add 4 spaces).
"WARNING": "\033[93m",

Check warning

Code scanning / Pylint (reported by Codacy)

Wrong hanging indentation before block (add 4 spaces). Warning test

Wrong hanging indentation before block (add 4 spaces).
"FAIL": "\033[91m",

Check warning

Code scanning / Pylint (reported by Codacy)

Wrong hanging indentation before block (add 4 spaces). Warning test

Wrong hanging indentation before block (add 4 spaces).
"ENDC": "\033[0m",

Check warning

Code scanning / Pylint (reported by Codacy)

Wrong hanging indentation before block (add 4 spaces). Warning test

Wrong hanging indentation before block (add 4 spaces).
"BOLD": "\033[1m",

Check warning

Code scanning / Pylint (reported by Codacy)

Wrong hanging indentation before block (add 4 spaces). Warning test

Wrong hanging indentation before block (add 4 spaces).
"UNDERLINE": "\033[4m",

Check warning

Code scanning / Pylint (reported by Codacy)

Wrong hanging indentation before block (add 4 spaces). Warning test

Wrong hanging indentation before block (add 4 spaces).
}.items():
monkeypatch.setattr(main.Colors, attr, code)
Comment on lines +8 to +23

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current method of enabling colors for this test is a bit brittle. By manually setting each color code, the test can become outdated if the Colors class in main.py is updated. A more robust approach is to simulate a TTY environment and then reload the main module. This ensures that USE_COLORS is set correctly and the Colors class is initialized with the right values, just as it would be in a real TTY environment. You will need to add import importlib to the file's imports.

    # Force a TTY-like environment to test color output correctly
    monkeypatch.setattr(sys.stdout, "isatty", lambda: True)
    monkeypatch.setattr(sys.stderr, "isatty", lambda: True)
    monkeypatch.delenv("NO_COLOR", raising=False)

    # Reload the main module to re-evaluate USE_COLORS and the Colors class
    # based on the mocked environment.
    import importlib
    importlib.reload(main)


# Mock stdout to capture print output
mock_stdout = MagicMock()
monkeypatch.setattr(sys, "stdout", mock_stdout)

plan = {
"profile": "test_profile",
"folders": [
{"name": "Test Folder 1", "rules": 10},
{"name": "Test Folder 2", "rules": 20},
]
}

main.print_dry_run_plan(plan)

# Aggregate all writes
# print() typically calls write(string) and write('\n')
combined_output = "".join([str(args[0]) for args, _ in mock_stdout.write.call_args_list])

assert "📝 Dry Run Plan for Profile:" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
assert "test_profile" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
assert "Test Folder 1" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
assert "10 rules" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
# ANSI codes should be present (main.Colors.HEADER starts with \033[95m)
assert "\033[" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

def test_print_dry_run_plan_no_colors(monkeypatch):
"""Verify print_dry_run_plan output without colors."""
monkeypatch.setattr(main, "USE_COLORS", False)

mock_stdout = MagicMock()
monkeypatch.setattr(sys, "stdout", mock_stdout)

plan = {
"profile": "test_profile",
"folders": [
{"name": "Test Folder 1", "rules": 10},
]
}

main.print_dry_run_plan(plan)

combined_output = "".join([str(args[0]) for args, _ in mock_stdout.write.call_args_list])

assert "📝 Dry Run Plan for Profile:" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
assert "test_profile" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
assert "Test Folder 1" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
assert "10 rules" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
# No ANSI codes
assert "\033[" not in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment on lines +50 to +73
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test should also set the Colors class attributes to empty strings when USE_COLORS is False, similar to how test_print_dry_run_plan_with_colors manually sets color codes. If the module was imported in a TTY environment, the Colors attributes would still contain ANSI codes even after setting USE_COLORS to False, which could cause the test to fail.

The same pattern from test_print_dry_run_plan_with_colors should be applied here, setting all Colors attributes to empty strings:

  • HEADER, BLUE, CYAN, GREEN, WARNING, FAIL, ENDC, BOLD, UNDERLINE should all be set to ""

Copilot uses AI. Check for mistakes.

def test_print_dry_run_plan_empty_folders(monkeypatch):
"""Verify output when no folders are present."""
monkeypatch.setattr(main, "USE_COLORS", False)
mock_stdout = MagicMock()
monkeypatch.setattr(sys, "stdout", mock_stdout)

plan = {
"profile": "test_profile",
"folders": []
}

main.print_dry_run_plan(plan)

combined_output = "".join([str(args[0]) for args, _ in mock_stdout.write.call_args_list])
assert "(No folders found to sync)" in combined_output

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment on lines +75 to +89
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test should also set the Colors class attributes to empty strings when USE_COLORS is False. If the module was imported in a TTY environment, the Colors attributes would still contain ANSI codes even after setting USE_COLORS to False, which could cause the test to fail.

The same pattern from test_print_dry_run_plan_with_colors should be applied here, setting all Colors attributes to empty strings:

  • HEADER, BLUE, CYAN, GREEN, WARNING, FAIL, ENDC, BOLD, UNDERLINE should all be set to ""

Copilot uses AI. Check for mistakes.
Loading