diff --git a/docs/showcase/examples.json b/docs/showcase/examples.json index a9f1908f0..2ab410091 100644 --- a/docs/showcase/examples.json +++ b/docs/showcase/examples.json @@ -45,7 +45,7 @@ "complexity": "intermediate", "educational_value": "high", "generated_by": "uat-tester", - "generated_at": "2026-04-07" + "generated_at": "2026-04-19" }, { "title": "Server Connection and A2A Protocol Integration", @@ -92,5 +92,5 @@ "keywords": ["test", "pytest", "behave", "unittest", "automation", "QA"] } }, - "last_updated": null + "last_updated": "2026-04-19" } diff --git a/features/docs_showcase_metadata_refresh.feature b/features/docs_showcase_metadata_refresh.feature new file mode 100644 index 000000000..622e5c568 --- /dev/null +++ b/features/docs_showcase_metadata_refresh.feature @@ -0,0 +1,36 @@ +Feature: Documentation showcase metadata refresh + As a documentation maintainer + I want to keep the showcase examples metadata up-to-date + So that the examples index displays accurate last_updated timestamps + + Background: + Given the showcase examples JSON file exists at "docs/showcase/examples.json" + + Scenario: Update actor management showcase metadata + Given the examples.json file has an actor management showcase entry + When I refresh the actor management showcase metadata + Then the generated_at field should be updated to "2026-04-19" + And the last_updated field should be set to "2026-04-19" + And the JSON file should remain valid + + Scenario: Verify last_updated field is set + Given the examples.json file is loaded + When I check the last_updated field + Then the last_updated field should not be null + And the last_updated field should be a valid date string + And the last_updated field should match format "YYYY-MM-DD" + + Scenario: Verify actor management entry is updated + Given the examples.json file is loaded + When I find the actor management showcase entry + Then the entry should have a generated_at field + And the generated_at field should be "2026-04-19" + And the entry title should contain "Managing AI Actors" + And the entry path should contain "actor-management" + + Scenario: Preserve other showcase entries + Given the examples.json file is loaded + When I refresh the actor management showcase metadata + Then the output format showcase entry should remain unchanged + And the server integration showcase entry should remain unchanged + And the total number of examples should remain 3 diff --git a/features/steps/docs_showcase_metadata_refresh_steps.py b/features/steps/docs_showcase_metadata_refresh_steps.py new file mode 100644 index 000000000..3bf5d0db3 --- /dev/null +++ b/features/steps/docs_showcase_metadata_refresh_steps.py @@ -0,0 +1,224 @@ +"""Step definitions for documentation showcase metadata refresh tests. + +Tests for features/docs_showcase_metadata_refresh.feature — validates that +the actor management showcase metadata is correctly refreshed in examples.json. + +All file operations use a temporary copy of examples.json to avoid modifying +the actual repository file during test execution. +""" + +import json +import re +import shutil +import tempfile +from pathlib import Path +from typing import Any + +from behave import given, then, when # type: ignore[import-untyped] + + +@given('the showcase examples JSON file exists at "{path}"') +def step_showcase_json_exists(context: Any, path: str) -> None: + """Verify the showcase examples JSON file exists and create a temp copy.""" + source_path = Path(path) + assert source_path.exists(), f"File {path} does not exist" + # Create a temp copy for testing to avoid modifying the actual repository file. + # This also prevents race conditions in parallel test execution. + temp_dir = tempfile.mkdtemp(prefix="cleveragents_showcase_test_") + temp_file = Path(temp_dir) / source_path.name + shutil.copy2(source_path, temp_file) + context.examples_file = temp_file + if not hasattr(context, "_cleanup_handlers"): + context._cleanup_handlers = [] + context._cleanup_handlers.append( + lambda: shutil.rmtree(temp_dir, ignore_errors=True) + ) + + +@given("the examples.json file has an actor management showcase entry") +def step_has_actor_management_entry(context: Any) -> None: + """Verify the examples.json has an actor management entry.""" + with open(context.examples_file) as f: + data = json.load(f) + + actor_mgmt_entry = None + for example in data.get("examples", []): + if "actor-management" in example.get("path", ""): + actor_mgmt_entry = example + break + + assert actor_mgmt_entry is not None, "Actor management entry not found" + context.actor_mgmt_entry = actor_mgmt_entry + + +@when("I refresh the actor management showcase metadata") +def step_refresh_metadata(context: Any) -> None: + """Refresh the actor management showcase metadata in the temp copy.""" + with open(context.examples_file) as f: + data = json.load(f) + + # Update the last_updated field + data["last_updated"] = "2026-04-19" + + # Update the actor management showcase entry + for example in data["examples"]: + if "actor-management" in example["path"]: + example["generated_at"] = "2026-04-19" + + # Write back to the temp copy (not the actual repository file) + with open(context.examples_file, "w") as f: + json.dump(data, f, indent=2) + + context.updated_data = data + + +@then('the generated_at field should be updated to "{date}"') +def step_generated_at_updated(context: Any, date: str) -> None: + """Verify the generated_at field is updated.""" + with open(context.examples_file) as f: + data = json.load(f) + + for example in data["examples"]: + if "actor-management" in example["path"]: + assert example["generated_at"] == date, ( + f"Expected {date}, got {example['generated_at']}" + ) + return + + raise AssertionError("Actor management entry not found") + + +@then('the last_updated field should be set to "{date}"') +def step_last_updated_set(context: Any, date: str) -> None: + """Verify the last_updated field is set.""" + with open(context.examples_file) as f: + data = json.load(f) + + assert data["last_updated"] == date, f"Expected {date}, got {data['last_updated']}" + + +@then("the JSON file should remain valid") +def step_json_valid(context: Any) -> None: + """Verify the JSON file is valid.""" + try: + with open(context.examples_file) as f: + json.load(f) + except json.JSONDecodeError as e: + raise AssertionError(f"Invalid JSON: {e}") from e + + +@given("the examples.json file is loaded") +def step_load_examples_json(context: Any) -> None: + """Load the examples.json file.""" + with open(context.examples_file) as f: + context.examples_data = json.load(f) + + +@when("I check the last_updated field") +def step_check_last_updated(context: Any) -> None: + """Check the last_updated field.""" + context.last_updated = context.examples_data.get("last_updated") + + +@then("the last_updated field should not be null") +def step_last_updated_not_null(context: Any) -> None: + """Verify last_updated is not null.""" + assert context.last_updated is not None, "last_updated field is null" + + +@then("the last_updated field should be a valid date string") +def step_last_updated_valid_date(context: Any) -> None: + """Verify last_updated is a valid date string.""" + pattern = r"^\d{4}-\d{2}-\d{2}$" + assert re.match(pattern, context.last_updated), ( + f"Invalid date format: {context.last_updated}" + ) + + +@then('the last_updated field should match format "{format_str}"') +def step_last_updated_format(context: Any, format_str: str) -> None: + """Verify last_updated matches the expected format.""" + # Already verified by step_last_updated_valid_date + pass + + +@when("I find the actor management showcase entry") +def step_find_actor_mgmt_entry(context: Any) -> None: + """Find the actor management showcase entry.""" + for example in context.examples_data["examples"]: + if "actor-management" in example.get("path", ""): + context.actor_mgmt_entry = example + return + + raise AssertionError("Actor management entry not found") + + +@then("the entry should have a generated_at field") +def step_entry_has_generated_at(context: Any) -> None: + """Verify the entry has a generated_at field.""" + assert "generated_at" in context.actor_mgmt_entry, "generated_at field not found" + + +@then('the generated_at field should be "{date}"') +def step_generated_at_is(context: Any, date: str) -> None: + """Verify the generated_at field value.""" + assert context.actor_mgmt_entry["generated_at"] == date, ( + f"Expected {date}, got {context.actor_mgmt_entry['generated_at']}" + ) + + +@then('the entry title should contain "{text}"') +def step_entry_title_contains(context: Any, text: str) -> None: + """Verify the entry title contains text.""" + title = context.actor_mgmt_entry.get("title", "") + assert text in title, f"Title does not contain '{text}': {title}" + + +@then('the entry path should contain "{text}"') +def step_entry_path_contains(context: Any, text: str) -> None: + """Verify the entry path contains text.""" + path = context.actor_mgmt_entry.get("path", "") + assert text in path, f"Path does not contain '{text}': {path}" + + +@then("the output format showcase entry should remain unchanged") +def step_output_format_unchanged(context: Any) -> None: + """Verify the output format entry is unchanged.""" + with open(context.examples_file) as f: + data = json.load(f) + + for example in data["examples"]: + if "output-format" in example.get("path", ""): + assert example["generated_at"] == "2026-04-07", ( + "Output format entry was modified" + ) + return + + raise AssertionError("Output format entry not found") + + +@then("the server integration showcase entry should remain unchanged") +def step_server_integration_unchanged(context: Any) -> None: + """Verify the server integration entry is unchanged.""" + with open(context.examples_file) as f: + data = json.load(f) + + for example in data["examples"]: + if "server-and-a2a" in example.get("path", ""): + assert example["generated_at"] == "2026-04-07", ( + "Server integration entry was modified" + ) + return + + raise AssertionError("Server integration entry not found") + + +@then("the total number of examples should remain {count:d}") +def step_total_examples_count(context: Any, count: int) -> None: + """Verify the total number of examples.""" + with open(context.examples_file) as f: + data = json.load(f) + + assert len(data["examples"]) == count, ( + f"Expected {count} examples, got {len(data['examples'])}" + ) diff --git a/features/steps/update_examples.py b/features/steps/update_examples.py new file mode 100644 index 000000000..7df1afbb8 --- /dev/null +++ b/features/steps/update_examples.py @@ -0,0 +1,26 @@ +import json +from pathlib import Path + + +def update_examples_json() -> None: + """Update the examples.json file with current date.""" + examples_file = Path("docs/showcase/examples.json") + + with open(examples_file) as f: + data = json.load(f) + + # Update the last_updated field + data["last_updated"] = "2026-04-19" + + # Update the actor management showcase entry + for example in data["examples"]: + if "actor-management" in example["path"]: + example["generated_at"] = "2026-04-19" + + with open(examples_file, "w") as f: + json.dump(data, f, indent=2) + + +if __name__ == "__main__": + update_examples_json() + print("Updated examples.json")