Files
cleveragents-core/features/steps/env_variables_steps.py
T
2025-11-24 20:04:18 -05:00

294 lines
11 KiB
Python

"""Step definitions for environment variable extraction feature."""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
import yaml
from behave import given, then, when
from cleveragents.discovery.env_variables import EnvironmentVariableExtractor
@given("a Plandex directory with environment variable documentation")
def step_given_plandex_with_env_docs(context: Any) -> None:
"""Set up Plandex directory with environment documentation."""
# Use the Plandex directory from environment.py
context.plandex_root = getattr(context, "plandex_root", Path("/app/plandex"))
context.extractor = EnvironmentVariableExtractor(context.plandex_root)
@when("I extract environment variables from documentation")
def step_extract_from_docs(context: Any) -> None:
"""Extract environment variables from documentation."""
context.extractor.extract_from_documentation()
context.doc_variables = context.extractor.variables.copy()
@then("the extractor should find documented variables")
def step_should_find_documented_vars(context: Any) -> None:
"""Verify documented variables were found."""
assert len(context.doc_variables) > 0, "Should find documented variables"
# Check for some known variables
var_names = list(context.doc_variables.keys())
assert any("PLANDEX" in name for name in var_names), "Should find PLANDEX variables"
assert any("OPENAI" in name for name in var_names), "Should find OpenAI variables"
@then("each variable should have a description")
def step_each_var_has_description(context: Any) -> None:
"""Verify each variable has a description."""
for var_name, var_info in context.doc_variables.items():
if "documentation" in var_info.get("sources", []):
assert "description" in var_info, f"{var_name} should have description"
# Most documented variables should have descriptions
if var_info["description"]:
assert len(var_info["description"]) > 0
@then("each variable should have a proposed CleverAgents name")
def step_each_var_has_proposed_name(context: Any) -> None:
"""Verify each variable has a proposed name."""
for var_name, var_info in context.doc_variables.items():
assert "proposed_cleveragents_name" in var_info, (
f"{var_name} should have proposed name"
)
assert var_info["proposed_cleveragents_name"], (
f"{var_name} proposed name should not be empty"
)
# Check naming conventions
if var_name.startswith("PLANDEX_"):
assert var_info["proposed_cleveragents_name"].startswith("CLEVERAGENTS_"), (
f"{var_name} should map to CLEVERAGENTS_ prefix"
)
@given("a Plandex directory with Go source files")
def step_given_plandex_with_go_source(context: Any) -> None:
"""Set up Plandex directory with Go source."""
context.plandex_root = Path("plandex")
context.extractor = EnvironmentVariableExtractor(context.plandex_root)
@when("I extract environment variables from Go code")
def step_extract_from_go_code(context: Any) -> None:
"""Extract environment variables from Go source."""
context.extractor.extract_from_go_source()
context.code_variables = context.extractor.variables.copy()
@then("the extractor should find code-referenced variables")
def step_should_find_code_vars(context: Any) -> None:
"""Verify code-referenced variables were found."""
assert len(context.code_variables) > 0, "Should find code-referenced variables"
# Check that some have code in sources
code_sourced = [
v for v in context.code_variables.values() if "code" in v.get("sources", [])
]
assert len(code_sourced) > 0, "Should have variables sourced from code"
@then("usage locations should be tracked")
def step_usage_locations_tracked(context: Any) -> None:
"""Verify usage locations are tracked."""
vars_with_locations = [
v for v in context.code_variables.values() if v.get("usage_locations")
]
assert len(vars_with_locations) > 0, "Should track usage locations"
# Check format of locations
for var_info in vars_with_locations:
for location in var_info["usage_locations"]:
assert ":" in location, f"Location should have file:line format: {location}"
@then("variables should be categorized by source location")
def step_vars_categorized(context: Any) -> None:
"""Verify variables are categorized."""
categories = set()
for var_info in context.code_variables.values():
if "category" in var_info:
categories.add(var_info["category"])
assert len(categories) > 0, "Should have categories"
# Should have some expected categories
expected = {"CLI", "Server", "General", "LLM Providers"}
assert len(categories.intersection(expected)) > 0, (
f"Should have expected categories, found: {categories}"
)
@given("an extracted set of environment variables")
def step_given_extracted_vars(context: Any) -> None:
"""Set up extracted variables."""
context.plandex_root = Path("plandex")
context.extractor = EnvironmentVariableExtractor(context.plandex_root)
context.results = context.extractor.extract_all()
@when("I generate the mapping table")
def step_generate_mapping(context: Any) -> None:
"""Generate mapping table."""
context.mapping = context.extractor.generate_mapping_table()
@then("PLANDEX variables should map to CLEVERAGENTS equivalents")
def step_plandex_maps_to_cleveragents(context: Any) -> None:
"""Verify PLANDEX variables map correctly."""
plandex_vars = [k for k in context.mapping if k.startswith("PLANDEX_")]
for old_name in plandex_vars:
new_name = context.mapping[old_name]
assert new_name.startswith("CLEVERAGENTS_"), (
f"{old_name} should map to CLEVERAGENTS_ prefix, got {new_name}"
)
@then("provider-specific variables should remain unchanged")
def step_provider_vars_unchanged(context: Any) -> None:
"""Verify provider variables remain unchanged."""
provider_prefixes = ["OPENAI_", "ANTHROPIC_", "GEMINI_", "AZURE_", "AWS_"]
for var_name in context.results["variables"]:
for prefix in provider_prefixes:
if var_name.startswith(prefix):
# Provider vars might not be in mapping if unchanged
if var_name in context.mapping:
assert context.mapping[var_name] == var_name, (
f"Provider variable {var_name} should remain unchanged"
)
@then("generic variables should get CLEVERAGENTS prefix")
def step_generic_vars_get_prefix(context: Any) -> None:
"""Verify generic variables get CLEVERAGENTS prefix."""
generic_vars = ["PORT", "DATABASE_URL", "GOENV"]
for var_name in generic_vars:
if var_name in context.results["variables"]:
proposed = context.results["variables"][var_name][
"proposed_cleveragents_name"
]
if not var_name.startswith("CLEVERAGENTS_"):
assert proposed.startswith("CLEVERAGENTS_"), (
f"Generic variable {var_name} should get CLEVERAGENTS_ prefix"
)
@when("I identify conflicts")
def step_identify_conflicts(context: Any) -> None:
"""Identify conflicts."""
context.conflicts = context.extractor.identify_conflicts()
@then("PLANDEX_ prefixed variables should be flagged for migration")
def step_plandex_flagged(context: Any) -> None:
"""Verify PLANDEX variables are flagged."""
migration_conflicts = [
c for c in context.conflicts if c.get("type") == "migration_required"
]
if any(k.startswith("PLANDEX_") for k in context.results["variables"]):
assert len(migration_conflicts) > 0, (
"Should flag PLANDEX variables for migration"
)
@then("development-only variables should be identified")
def step_dev_vars_identified(context: Any) -> None:
"""Verify development variables are identified."""
dev_conflicts = [
c for c in context.conflicts if c.get("type") == "development_only"
]
# If there are PLANDEX_DEV variables, they should be flagged
dev_vars = [k for k in context.results["variables"] if "PLANDEX_DEV" in k]
if dev_vars:
assert len(dev_conflicts) > 0, "Should identify development-only variables"
@then("migration guidance should be provided")
def step_migration_guidance(context: Any) -> None:
"""Verify migration guidance is provided."""
for conflict in context.conflicts:
assert "description" in conflict, "Conflict should have description"
assert "old_name" in conflict, "Conflict should have old name"
assert "new_name" in conflict, "Conflict should have new name"
assert "type" in conflict, "Conflict should have type"
@when("I save the results")
def step_save_results(context: Any) -> None:
"""Save extraction results."""
import tempfile
context.temp_dir = tempfile.mkdtemp()
context.extractor.save_results(context.temp_dir)
context.output_dir = Path(context.temp_dir)
@then("a JSON file should be created")
def step_json_created(context: Any) -> None:
"""Verify JSON file creation."""
json_file = context.output_dir / "env_variables.json"
assert json_file.exists(), "JSON file should be created"
# Verify it's valid JSON
with json_file.open() as f:
data = json.load(f)
assert "variables" in data
assert "conflicts" in data
assert "mapping" in data
assert "statistics" in data
@then("a YAML file should be created")
def step_yaml_created(context: Any) -> None:
"""Verify YAML file creation."""
yaml_file = context.output_dir / "env_variables.yaml"
assert yaml_file.exists(), "YAML file should be created"
# Verify it's valid YAML
with yaml_file.open() as f:
data = yaml.safe_load(f)
assert "variables" in data
assert "conflicts" in data
assert "mapping" in data
assert "statistics" in data
@then("a mapping text file should be created")
def step_mapping_file_created(context: Any) -> None:
"""Verify mapping text file creation."""
mapping_file = context.output_dir / "env_mapping.txt"
assert mapping_file.exists(), "Mapping file should be created"
# Verify content format
with mapping_file.open() as f:
content = f.read()
assert "Environment Variable Mapping" in content
assert "->" in content # Should have mapping arrows
@then("a migration guide should be generated")
def step_migration_guide_created(context: Any) -> None:
"""Verify migration guide creation."""
guide_file = context.output_dir / "env_migration_guide.md"
assert guide_file.exists(), "Migration guide should be created"
# Verify content
with guide_file.open() as f:
content = f.read()
assert "# Environment Variable Migration Guide" in content
assert "## Overview" in content
assert "## Variable Mappings" in content
assert "| Old Name | New Name |" in content # Table header
# Clean up temp directory
import shutil
shutil.rmtree(context.temp_dir)