""" Level 2 Scenarios — Two-step fixes. """ from __future__ import annotations from typing import List from scenarios.registry import Scenario def _success_port_freed(output: str) -> bool: """Check if the port conflict was resolved.""" o = output.lower() return "server running" in o or "running on" in o or "killed" in o or "no process" in o def _success_env_var_set(output: str) -> bool: """Check if the DATABASE_URL env var is set and app runs.""" o = output.lower() return "connected to database" in o or "success" in o or "postgresql://" in o def _success_requirements_fixed(output: str) -> bool: """Check if requirements.txt was fixed and packages installed.""" o = output.lower() return "successfully installed" in o or "requirement already satisfied" in o def get_level2_scenarios() -> List[Scenario]: """Return all Level 2 (two-step fix) scenarios.""" return [ Scenario( id="port_conflict", level=2, description="Flask app fails because port 5000 is already in use.", initial_state={"files": {}, "env_vars": {}, "processes": []}, success_condition=_success_port_freed, hint_commands=["lsof -t -i:5000 | xargs kill -9", "python /app/server.py &"], error_fingerprint=r"Address already in use|EADDRINUSE", setup_commands=[ "pip install flask -q", "mkdir -p /app", "cat > /app/server.py << 'PYEOF'\nfrom flask import Flask\napp = Flask(__name__)\n@app.route('/')\ndef hello():\n return 'Hello World'\nif __name__ == '__main__':\n app.run(host='0.0.0.0', port=5000)\nPYEOF", "python -c \"import socket,time; s=socket.socket(); s.bind(('',5000)); s.listen(); time.sleep(3600)\" &", ], initial_error_log=( "$ python /app/server.py\n" "Traceback (most recent call last):\n" " File \"/app/server.py\", line 7, in \n" " app.run(host='0.0.0.0', port=5000)\n" "OSError: [Errno 98] Address already in use\n" ), verification_command="curl -s http://localhost:5000 || echo 'port freed'", ), Scenario( id="missing_env_var", level=2, description="App crashes because DATABASE_URL environment variable is not set.", initial_state={"files": {}, "env_vars": {}, "processes": []}, success_condition=_success_env_var_set, hint_commands=["export DATABASE_URL=postgresql://localhost:5432/mydb", "python /app/db_app.py"], error_fingerprint=r"KeyError.*DATABASE_URL", setup_commands=[ "mkdir -p /app", "cat > /app/db_app.py << 'PYEOF'\nimport os\ndb_url = os.environ['DATABASE_URL']\nprint(f'Connected to database at: {db_url}')\nprint('Success')\nPYEOF", ], initial_error_log=( "$ python /app/db_app.py\n" "Traceback (most recent call last):\n" " File \"/app/db_app.py\", line 2, in \n" " db_url = os.environ['DATABASE_URL']\n" "KeyError: 'DATABASE_URL'\n" ), verification_command="DATABASE_URL=postgresql://localhost:5432/mydb python /app/db_app.py", ), Scenario( id="broken_requirements", level=2, description="requirements.txt has a conflicting version pin.", initial_state={"files": {}, "env_vars": {}, "processes": []}, success_condition=_success_requirements_fixed, hint_commands=["sed -i 's/werkzeug==1.0.0/werkzeug>=2.3.0/' /app/requirements.txt", "pip install -r /app/requirements.txt"], error_fingerprint=r"version.*conflict|ResolutionImpossible", setup_commands=[ "mkdir -p /app", "cat > /app/requirements.txt << 'PYEOF'\nflask==2.3.0\nwerkzeug==1.0.0\njinja2>=3.0\nPYEOF", ], initial_error_log=( "$ pip install -r /app/requirements.txt\n" "ERROR: Cannot install flask==2.3.0 and werkzeug==1.0.0\n" "ERROR: ResolutionImpossible\n" ), verification_command="pip install -r /app/requirements.txt", ), ]