mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-03 02:26:43 +09:00
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>
72 lines
3.0 KiB
Python
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()
|