Files
ServerKit/backend/tests/test_agent_features_honest.py
Juan DenisandClaude Opus 5 9d570834aa perf(tests): build the Flask app once per session, not once per test
The `app` fixture called create_app('testing') for every one of 3173 tests,
registering 90+ blueprints each time. Measured on a fixed 199-test sample
(pytest --splits 16 --group 5 --durations=0):

    setup     144.9s   89.5%
    call        8.8s    5.4%
    teardown    8.2s    5.0%

95% of the suite's time was preparing to test; 5% was testing. The app is now
built once per process and shared.

Same 794-test slice, before and after — identical results, 2.66x faster:

    before   4 failed, 773 passed, 17 skipped   593.30s
    after    4 failed, 773 passed, 17 skipped   223.11s

(the 4 are pre-existing locally: serverkit-wordpress isn't checked out here.)

Sharing one app surfaces three things a per-test app hid, all handled in the
fixture rather than patched test by test:

1. create_app() is not a pure constructor — it SEEDS the bundled flagship
   extension rows as a boot side effect (test_cloudflare_extraction asserts
   they are "seeded on boot"). The per-test drop_all wipes them, and a shared
   app never re-boots, so every test after the first saw no flagship and the
   extension routes answered 503. seed_flagship_extensions() is now re-run
   after each create_all. The session fixture also boots twice on purpose:
   once to create the schema, once to seed into a schema that exists.

2. Config mutation now leaks where it used to die with the app (e.g.
   test_demo_deploys sets DEMO_DEPLOYS_ENABLED=False and never restores it),
   so config is snapshotted and restored around every test.

3. Structure mutation cannot be undone at all — Flask has no
   unregister_blueprint, and refuses setup methods once an app has served a
   request. Those tests opt out via `pytestmark = pytest.mark.fresh_app` and
   get a private app. Three groups needed it: 13 modules calling
   register_blueprint/add_url_rule directly, 11 that install plugins (
   plugin_service hot-loads their blueprints onto the live app), and
   test_audit_log which adds routes with @app.route.

Tests using the `wp_extension` fixture are detected from the fixture graph
instead of being marked, since that fixture mounts blueprints on whatever app
it is handed — miss one marker and the failure lands in an unrelated test much
later in the run.

Plan 64 Phase 1. Phase 2 (savepoint rollback instead of create_all/drop_all)
is NOT included: it currently takes the slice from 4 failures to 100 failures
and 201 errors, and needs its own round.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 02:59:14 -04:00

72 lines
3.0 KiB
Python

"""
Regression tests for audit H2: the Agent Plugin and Server Template features
used to dispatch to a non-existent `get_agent_gateway()` (ImportError, swallowed)
and leave DB rows stuck in an in-progress state forever. The agent implements
none of these commands. These tests assert the operations now report honestly
instead of silently pretending to work.
"""
from app import db
from app.models.server import Server
from app.models.agent_plugin import AgentPlugin, AgentPluginInstall
from app.models.server_template import ServerTemplate, ServerTemplateAssignment
from app.services.agent_plugin_service import AgentPluginService
from app.services.server_template_service import ServerTemplateService
import pytest
# Installs plugins, and plugin_service hot-loads their blueprints onto the
# live app. Flask refuses register_blueprint once an app has served a
# request, so these need a private app (plan 64 Phase 1).
pytestmark = pytest.mark.fresh_app
def test_plugin_install_reports_unimplemented_not_stuck_installing(app):
with app.app_context():
plugin = AgentPluginService.create_plugin({
"name": "demo-plugin",
"display_name": "Demo Plugin",
"version": "1.0.0",
})
server = Server(name="srv", agent_id="agent-h2-1")
db.session.add(server)
db.session.commit()
install = AgentPluginService.install_plugin(plugin.id, server.id)
# Honest failure, not a permanent 'installing' limbo.
assert install.status == AgentPluginInstall.STATUS_ERROR
assert install.status != AgentPluginInstall.STATUS_INSTALLING
assert "not implemented" in (install.error_message or "").lower()
def test_template_drift_check_reports_unimplemented_not_stuck_checking(app):
with app.app_context():
tmpl = ServerTemplate(name="base-template", version="1.0.0")
server = Server(name="srv2", agent_id="agent-h2-2")
db.session.add_all([tmpl, server])
db.session.commit()
assignment = ServerTemplateAssignment(template_id=tmpl.id, server_id=server.id)
db.session.add(assignment)
db.session.commit()
result = ServerTemplateService.check_drift(assignment.id)
assert result.status == ServerTemplateAssignment.STATUS_UNKNOWN
assert result.status != ServerTemplateAssignment.STATUS_CHECKING
assert "not implemented" in (result.drift_report.get("error") or "").lower()
def test_template_remediate_reports_unimplemented(app):
with app.app_context():
tmpl = ServerTemplate(name="base-template-2", version="1.0.0")
server = Server(name="srv3", agent_id="agent-h2-3")
db.session.add_all([tmpl, server])
db.session.commit()
assignment = ServerTemplateAssignment(template_id=tmpl.id, server_id=server.id)
db.session.add(assignment)
db.session.commit()
result = ServerTemplateService.remediate(assignment.id)
assert result.status == ServerTemplateAssignment.STATUS_UNKNOWN
assert "not implemented" in (result.drift_report.get("error") or "").lower()