mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-28 23:57:58 +09:00
Completes the §8 API consolidation: one admin surface drives a BackupPolicy for
ANY target — application, wordpress_site, database, files, server — by
(target_type, target_id):
GET/PUT /api/v1/backups/policies/<type>/<id> (PUT accepts
target_subtype + target_meta to describe database/files targets)
POST /api/v1/backups/policies/<type>/<id>/run
GET /api/v1/backups/policies/<type>/<id>/runs
POST .../runs/<run_id>/restore | /verify
DELETE .../runs/<run_id>
This is the only way to drive the database/files/server targets (which have no
per-resource page). The grant-aware per-resource mounts
(/apps/:id/backup-policy, /wordpress/sites/:id/backup-policy) are unchanged.
Deferred to §7 (WordPress-as-app): surfacing WP DatabaseSnapshot under
/apps/:id/db-snapshots, and the frontend Backups.jsx rewrite, both of which
depend on the app↔site unification.
Verified: 5 new tests; full backup/snapshot suite green (63 passed, 2 skipped).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
2.0 KiB
Python
52 lines
2.0 KiB
Python
"""§8 Phase B5 — generic backup-policy CRUD by (target_type, target_id).
|
|
|
|
One admin surface drives policies for every target type, including the
|
|
database/files/server targets that have no per-resource page. The existing
|
|
/apps/:id/backup-policy and /wordpress/sites/:id/backup-policy mounts are
|
|
unchanged.
|
|
"""
|
|
|
|
|
|
def test_put_then_get_database_policy(app, client, auth_headers):
|
|
body = {
|
|
'enabled': True,
|
|
'schedule_cron': '0 3 * * *',
|
|
'target_subtype': 'mysql',
|
|
'target_meta': {'db_name': 'shop', 'host': 'localhost'},
|
|
}
|
|
put = client.put('/api/v1/backups/policies/database/5', json=body, headers=auth_headers)
|
|
assert put.status_code == 200, put.get_json()
|
|
view = put.get_json()
|
|
assert view['policy']['target_type'] == 'database'
|
|
assert view['policy']['target_subtype'] == 'mysql'
|
|
assert view['policy']['target_meta']['db_name'] == 'shop'
|
|
assert view['policy']['enabled'] is True
|
|
|
|
get = client.get('/api/v1/backups/policies/database/5', headers=auth_headers)
|
|
assert get.status_code == 200
|
|
assert get.get_json()['policy']['schedule_cron'] == '0 3 * * *'
|
|
|
|
|
|
def test_get_creates_default_files_policy(app, client, auth_headers):
|
|
get = client.get('/api/v1/backups/policies/files/9', headers=auth_headers)
|
|
assert get.status_code == 200
|
|
assert get.get_json()['policy']['target_type'] == 'files'
|
|
assert get.get_json()['policy']['enabled'] is False
|
|
|
|
|
|
def test_invalid_target_type_rejected(app, client, auth_headers):
|
|
res = client.get('/api/v1/backups/policies/bogus/1', headers=auth_headers)
|
|
assert res.status_code == 400
|
|
|
|
|
|
def test_list_runs_empty_for_new_policy(app, client, auth_headers):
|
|
res = client.get('/api/v1/backups/policies/files/77/runs', headers=auth_headers)
|
|
assert res.status_code == 200
|
|
assert res.get_json()['runs'] == []
|
|
|
|
|
|
def test_generic_surface_requires_admin(app, client):
|
|
# No auth → rejected (matches the rest of /backups).
|
|
res = client.get('/api/v1/backups/policies/database/1')
|
|
assert res.status_code in (401, 422)
|