Skip to content
Merged
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name = "pyplots"
version = "0.1.0"
description = "AI-powered Python plotting examples"
authors = [{ name = "Markus Neusinger", email = "admin@pyplots.ai" }]
requires-python = ">=3.14"
requires-python = ">=3.13"
readme = "README.md"
license = "MIT"
keywords = [
Expand Down
14 changes: 9 additions & 5 deletions tests/unit/api/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,20 +159,24 @@ class TestCORSMiddleware:
"""Tests for CORS configuration."""

def test_cors_allows_localhost(self, client: TestClient) -> None:
"""CORS should allow localhost origins."""
"""CORS should allow localhost origins for preflight requests."""
response = client.options(
"/", headers={"Origin": "http://localhost:3000", "Access-Control-Request-Method": "GET"}
)

# Should not be blocked
assert response.status_code in [200, 204, 400]
# Preflight should succeed with 200 or 204 (not 400 which indicates an error)
assert response.status_code in [200, 204], f"Preflight failed with status {response.status_code}"

def test_cors_headers_present(self, client: TestClient) -> None:
"""CORS headers should be present in response."""
"""CORS headers should be present in response for cross-origin requests."""
response = client.get("/", headers={"Origin": "http://localhost:3000"})

# The response should include CORS headers
assert response.status_code == 200
# Verify CORS headers are present in the response
cors_header = response.headers.get("access-control-allow-origin")
assert cors_header is not None, "Missing Access-Control-Allow-Origin header"
# Should allow the requesting origin or use wildcard
assert cors_header in ["http://localhost:3000", "*"], f"Unexpected CORS origin: {cors_header}"


class TestAppConfiguration:
Expand Down
22 changes: 17 additions & 5 deletions tests/unit/prompts/test_prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,26 @@ def test_no_placeholder_pattern(self, pattern: str) -> None:

def test_no_empty_sections(self) -> None:
"""No empty sections (## Header followed by another ## or end of file)."""
empty_section_pattern = re.compile(r"^## .+\n\s*(?=^## |\Z)", re.MULTILINE)
# Find all level-2 headers and their positions
header_pattern = re.compile(r"^## .+$", re.MULTILINE)

for filepath in self._get_all_prompt_files():
content = filepath.read_text()
matches = empty_section_pattern.findall(content)
# Filter out intentionally minimal sections
real_empty = [m for m in matches if len(m.strip()) < 20]
assert not real_empty, f"Found empty sections in {filepath.name}: {real_empty}"
headers = list(header_pattern.finditer(content))

empty_sections = []
for i, match in enumerate(headers):
header = match.group()
start = match.end()
# End is either the next header or end of content
end = headers[i + 1].start() if i + 1 < len(headers) else len(content)
section_content = content[start:end].strip()

# Check if section content is empty (only whitespace)
if not section_content:
empty_sections.append(header)

assert not empty_sections, f"Found empty sections in {filepath.name}: {empty_sections}"


class TestCrossReferences:
Expand Down
27 changes: 18 additions & 9 deletions tests/unit/workflows/test_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,22 @@ def test_workflow_name_matches_file(self) -> None:
file_words -= common_words
name_words -= common_words

# At least one word should overlap (very lenient check)
# Check for at least some word overlap between filename and workflow name
# This is a lenient check using multiple strategies
if file_words and name_words:
# Strategy 1: Exact word overlap
overlap = file_words & name_words
# This is a soft check - just ensure there's some relation
assert len(overlap) >= 0, f"Workflow name unrelated to file: {filepath.name}"

# Strategy 2: Substring matching (e.g., "plottest" contains "plot")
has_substring_match = any(
fw in nw or nw in fw
for fw in file_words
for nw in name_words
if len(fw) >= 3 and len(nw) >= 3 # Only match words with 3+ chars
)

has_relation = len(overlap) > 0 or has_substring_match
assert has_relation, f"Workflow name '{name}' unrelated to file: {filepath.name}"


class TestWorkflowBestPractices:
Expand All @@ -284,12 +295,10 @@ def test_checkout_with_fetch_depth(self, filepath: Path) -> None:
"""Checkout actions should consider fetch-depth for history needs."""
content = filepath.read_text()

# This is informational - fetch-depth: 0 is needed for full history
# Just ensure it's explicitly set when used with git log/diff
if "git log" in content or "git diff" in content:
has_fetch_depth = "fetch-depth:" in content or "fetch-depth :" in content
# Soft check - it's good practice but not strictly required
assert has_fetch_depth or True # Always passes, but documents the practice
# Best practice: fetch-depth: 0 is needed for full git history
# When using git log/diff, workflows should set fetch-depth
# Note: This is advisory only - workflows may have valid reasons not to fetch full history
_ = content # Suppress unused variable warning; content is read for potential future validation

@pytest.mark.parametrize("filepath", get_all_workflow_files(), ids=lambda p: p.name)
def test_uses_environment_for_secrets(self, filepath: Path) -> None:
Expand Down
Loading