PromptGarage / tests /test_garage_cli.py
smlflg's picture
Initial public upload from Projekte/PromptGarage
77c25ef verified
Raw History Blame Contribute Delete
12.8 kB
"""Tests for garage.py — Click CLI commands."""
import json
import sqlite3
from unittest.mock import patch, MagicMock
import pytest
from click.testing import CliRunner
from garage import cli
@pytest.fixture
def runner():
return CliRunner()
def test_add_and_list(runner):
result = runner.invoke(cli, ["add", "Test Prompt", "-c", "This is test content"])
assert result.exit_code == 0
assert "Added prompt" in result.output
result = runner.invoke(cli, ["list"])
assert result.exit_code == 0
assert "Test Prompt" in result.output
def test_search(runner):
runner.invoke(cli, ["add", "Python Tips", "-c", "Use list comprehensions"])
result = runner.invoke(cli, ["search", "python"])
assert result.exit_code == 0
assert "Python Tips" in result.output
def test_show(runner):
runner.invoke(cli, ["add", "Show Test", "-c", "Detailed content here", "-t", "test,demo"])
result = runner.invoke(cli, ["show", "1"])
assert result.exit_code == 0
assert "Show Test" in result.output
assert "test,demo" in result.output
def test_star(runner):
runner.invoke(cli, ["add", "Star Test", "-c", "content"])
result = runner.invoke(cli, ["star", "1"])
assert result.exit_code == 0
assert "starred" in result.output
def test_rate(runner):
runner.invoke(cli, ["add", "Rate Test", "-c", "content"])
result = runner.invoke(cli, ["rate", "1", "4"])
assert result.exit_code == 0
assert "****" in result.output
def test_stats(runner):
runner.invoke(cli, ["add", "A", "-c", "aaa", "-t", "python"])
runner.invoke(cli, ["add", "B", "-c", "bbb", "-t", "coding"])
result = runner.invoke(cli, ["stats"])
assert result.exit_code == 0
assert "Total: 2" in result.output
def test_export_json(runner):
runner.invoke(cli, ["add", "Export", "-c", "export test"])
result = runner.invoke(cli, ["export"])
assert result.exit_code == 0
data = json.loads(result.output)
assert len(data) == 1
assert data[0]["title"] == "Export"
def test_export_md(runner):
runner.invoke(cli, ["add", "MD Export", "-c", "markdown test"])
result = runner.invoke(cli, ["export", "--format", "md"])
assert result.exit_code == 0
assert "## MD Export" in result.output
def test_delete(runner):
runner.invoke(cli, ["add", "Del", "-c", "delete me"])
result = runner.invoke(cli, ["delete", "1", "--yes"])
assert result.exit_code == 0
assert "deleted" in result.output
def test_list_no_prompts(runner):
result = runner.invoke(cli, ["list"])
assert result.exit_code == 0
assert "No prompts found" in result.output
def test_import_codex_command(runner, tmp_path):
history = tmp_path / "history.jsonl"
history.write_text(json.dumps({"text": "This Codex prompt should be imported through the CLI"}) + "\n")
result = runner.invoke(cli, ["import-codex", str(history)])
assert result.exit_code == 0
assert "Scanned: 1" in result.output
assert "Imported: 1" in result.output
def test_import_hermes_command(runner, tmp_path):
db_path = tmp_path / "state.db"
conn = sqlite3.connect(db_path)
conn.executescript(
"""
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
source TEXT NOT NULL,
model TEXT
);
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
role TEXT NOT NULL,
content TEXT,
timestamp REAL NOT NULL
);
"""
)
conn.execute(
"INSERT INTO sessions (id, source, model) VALUES (?, ?, ?)",
("s1", "cli", "MiniMax-M2.7"),
)
conn.execute(
"INSERT INTO messages (session_id, role, content, timestamp) VALUES (?, ?, ?, ?)",
("s1", "user", "This Hermes prompt should be imported through the CLI", 1),
)
conn.commit()
conn.close()
result = runner.invoke(cli, ["import-hermes", str(db_path)])
assert result.exit_code == 0
assert "Scanned: 1" in result.output
assert "Imported: 1" in result.output
def test_show_nonexistent(runner):
result = runner.invoke(cli, ["show", "999"])
assert result.exit_code == 0
assert "not found" in result.output
# --- use / copy / random / pick ---
def test_use_copies_and_increments(runner):
"""use command copies to clipboard and increments use_count."""
runner.invoke(cli, ["add", "Use Test", "-c", "use content"])
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
result = runner.invoke(cli, ["use", "1"])
assert result.exit_code == 0
mock_run.assert_called_once_with(["wl-copy", "use content"], check=True)
assert "Copied #1 to clipboard." in result.output
assert "use_count: 1" in result.output
def test_use_nonexistent(runner):
result = runner.invoke(cli, ["use", "999"])
assert result.exit_code == 0
assert "not found" in result.output
def test_copy_copies_without_increment(runner):
"""copy command copies to clipboard but does NOT increment use_count."""
runner.invoke(cli, ["add", "Copy Test", "-c", "copy content"])
with patch("subprocess.run") as mock_run, \
patch("garage.increment_use") as mock_inc:
mock_run.return_value = MagicMock(returncode=0)
result = runner.invoke(cli, ["copy", "1"])
assert result.exit_code == 0
mock_run.assert_called_once_with(["wl-copy", "copy content"], check=True)
mock_inc.assert_not_called()
assert "Copied #1 to clipboard." in result.output
def test_copy_nonexistent(runner):
result = runner.invoke(cli, ["copy", "999"])
assert result.exit_code == 0
assert "not found" in result.output
def test_random_shows_prompt(runner):
"""random command picks and displays a prompt."""
runner.invoke(cli, ["add", "Random Prompt", "-c", "random content"])
result = runner.invoke(cli, ["random"])
assert result.exit_code == 0
assert "Random Prompt" in result.output
assert "random content" in result.output
def test_random_no_prompts(runner):
result = runner.invoke(cli, ["random"])
assert result.exit_code == 0
assert "No prompts found." in result.output
def test_random_with_copy_flag(runner):
"""random --copy also invokes wl-copy."""
runner.invoke(cli, ["add", "Rand Copy", "-c", "rand copy content"])
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
result = runner.invoke(cli, ["random", "--copy"])
assert result.exit_code == 0
mock_run.assert_called_once_with(["wl-copy", "rand copy content"], check=True)
assert "Copied #" in result.output
def test_pick_selects_and_copies(runner):
"""pick command shows numbered list, reads choice, copies and increments."""
runner.invoke(cli, ["add", "Pick Prompt", "-c", "pick content"])
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
# Simulate user typing "1\n"
result = runner.invoke(cli, ["pick", "Pick"], input="1\n")
assert result.exit_code == 0
mock_run.assert_called_once_with(["wl-copy", "pick content"], check=True)
assert "Copied #1 to clipboard." in result.output
def test_pick_no_matches(runner):
result = runner.invoke(cli, ["pick", "zzznomatch"])
assert result.exit_code == 0
assert "No matches." in result.output
def test_pick_invalid_choice(runner):
runner.invoke(cli, ["add", "Pick2", "-c", "content2"])
with patch("subprocess.run"):
result = runner.invoke(cli, ["pick", "Pick2"], input="99\n")
assert result.exit_code == 0
assert "Invalid choice." in result.output
# --- browse / tag / untag / top / auto-tag ---
def test_browse_shows_and_stars(runner):
"""browse shows list and stars selected entries."""
runner.invoke(cli, ["add", "Browse A", "-c", "content a"])
runner.invoke(cli, ["add", "Browse B", "-c", "content b"])
# user selects entry 1 to star
result = runner.invoke(cli, ["browse"], input="1\n")
assert result.exit_code == 0
assert "Browse A" in result.output
assert "Browse B" in result.output
assert "Toggled star on" in result.output
def test_browse_quit(runner):
"""browse quits cleanly when user enters 'q'."""
runner.invoke(cli, ["add", "Quit Browse", "-c", "content"])
result = runner.invoke(cli, ["browse"], input="q\n")
assert result.exit_code == 0
assert "Toggled" not in result.output
def test_browse_no_prompts(runner):
result = runner.invoke(cli, ["browse"])
assert result.exit_code == 0
assert "No prompts found." in result.output
def test_tag_adds_tags(runner):
runner.invoke(cli, ["add", "Tag Test", "-c", "content"])
result = runner.invoke(cli, ["tag", "1", "python,testing"])
assert result.exit_code == 0
assert "Tagged #1" in result.output
assert "python" in result.output
assert "testing" in result.output
def test_tag_appends_without_duplicates(runner):
runner.invoke(cli, ["add", "Tag Dup", "-c", "content", "-t", "python"])
result = runner.invoke(cli, ["tag", "1", "python,docker"])
assert result.exit_code == 0
# python should appear only once in tag list
output_tags_part = result.output.split("[")[1].split("]")[0]
tag_list = [t.strip() for t in output_tags_part.split(",")]
assert tag_list.count("python") == 1
assert "docker" in tag_list
def test_tag_nonexistent(runner):
result = runner.invoke(cli, ["tag", "999", "python"])
assert result.exit_code == 0
assert "not found" in result.output
def test_untag_removes_tag(runner):
runner.invoke(cli, ["add", "Untag Test", "-c", "content", "-t", "python,docker"])
result = runner.invoke(cli, ["untag", "1", "docker"])
assert result.exit_code == 0
assert "removed 'docker'" in result.output
def test_untag_missing_tag(runner):
runner.invoke(cli, ["add", "Untag Miss", "-c", "content", "-t", "python"])
result = runner.invoke(cli, ["untag", "1", "docker"])
assert result.exit_code == 0
assert "not found on #1" in result.output
def test_untag_nonexistent_prompt(runner):
result = runner.invoke(cli, ["untag", "999", "python"])
assert result.exit_code == 0
assert "not found" in result.output
def test_top_shows_most_used(runner):
"""top shows prompts sorted by use_count."""
runner.invoke(cli, ["add", "Top Prompt", "-c", "top content"])
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
runner.invoke(cli, ["use", "1"])
runner.invoke(cli, ["use", "1"])
result = runner.invoke(cli, ["top"])
assert result.exit_code == 0
assert "Top Prompt" in result.output
assert "used 2x" in result.output
def test_top_no_used_prompts(runner):
runner.invoke(cli, ["add", "Unused", "-c", "content"])
result = runner.invoke(cli, ["top"])
assert result.exit_code == 0
assert "No used prompts found." in result.output
def test_auto_tag_dry_run(runner):
"""auto-tag --dry-run shows what would be tagged without applying."""
runner.invoke(cli, ["add", "Python Guide", "-c", "Write python code with pytest"])
result = runner.invoke(cli, ["auto-tag", "--dry-run"])
assert result.exit_code == 0
assert "would add" in result.output
assert "python" in result.output
# verify tags were NOT applied — show still reports Tags: -
show = runner.invoke(cli, ["show", "1"])
assert "Tags: -" in show.output
def test_auto_tag_applies(runner):
"""auto-tag without --dry-run actually updates tags."""
runner.invoke(cli, ["add", "Docker Setup", "-c", "Use docker to containerize"])
result = runner.invoke(cli, ["auto-tag"])
assert result.exit_code == 0
assert "tagged" in result.output
assert "docker" in result.output
# verify tags were applied
show = runner.invoke(cli, ["show", "1"])
assert "docker" in show.output
def test_auto_tag_skips_already_tagged(runner):
"""auto-tag skips prompts that already have tags."""
runner.invoke(cli, ["add", "Already Tagged", "-c", "python script", "-t", "existing"])
result = runner.invoke(cli, ["auto-tag"])
assert result.exit_code == 0
# should report 0 tagged (already has tag, not in untagged list)
assert "Tagged 0 prompts" in result.output
def test_auto_tag_summary_counts(runner):
"""auto-tag prints correct summary."""
runner.invoke(cli, ["add", "Python Doc", "-c", "python code"])
runner.invoke(cli, ["add", "No Keywords", "-c", "write a poem"])
result = runner.invoke(cli, ["auto-tag"])
assert result.exit_code == 0
# 1 matched + 1 no-match
assert "Tagged 1 prompts, skipped 1 (no matches)" in result.output