Skip to content

Commit 0531359

Browse files
authored
Implemented remaining backup methods: (#88)
* Support backup restoration. * Support uploading backup. Required refactoring backup tests.
1 parent 563940c commit 0531359

File tree

2 files changed

+102
-25
lines changed

2 files changed

+102
-25
lines changed

pocketbase/services/backups_service.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from pocketbase.models import Backup
3+
from pocketbase.models import Backup, FileUpload
44
from pocketbase.services.utils import BaseService
55

66

@@ -30,3 +30,9 @@ def download(self, key: str, file_token: str = None) -> bytes:
3030

3131
def delete(self, key: str):
3232
self.client.send("%s/%s" % (self.base_path(), key), {"method": "DELETE"})
33+
34+
def restore(self, key: str):
35+
self.client.send("%s/%s/restore" % (self.base_path(), key), {"method": "POST"})
36+
37+
def upload(self, file_upload: FileUpload):
38+
self.client.send(self.base_path() + "/upload", {"method": "POST", "body": {"file": file_upload}})

tests/integration/test_backups.py

Lines changed: 95 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,107 @@
11
import datetime
2+
import errno
3+
import http
4+
import time
5+
from typing import Iterator
26
from uuid import uuid4
37

8+
import pytest
9+
410
from pocketbase import PocketBase
11+
from pocketbase.models import FileUpload
12+
from pocketbase.models.collection import Collection
13+
from pocketbase.utils import ClientResponseError
514

15+
def cleanup_backup(client: PocketBase, backup_name: str):
16+
# Cleanup
17+
print("Cleaning up uploaded backup %s" % (backup_name,))
18+
client.backups.delete(backup_name)
619

7-
class TestBackupsService:
8-
def test_create_list_download_and_delete(self, client: PocketBase, state):
9-
state.backup_name = "%s.zip" % (uuid4().hex[:16],)
10-
client.backups.create(state.backup_name)
20+
# Check that it was deleted
21+
for backup in client.backups.get_full_list():
22+
if backup.key == backup_name:
23+
pytest.fail("Backup %s still found in list of all backups" % (backup_name,))
24+
25+
@pytest.fixture
26+
def backup_name(client: PocketBase) -> Iterator[str]:
27+
backup_name = "%s.zip" % (uuid4().hex[:16],)
28+
client.backups.create(backup_name)
29+
try:
30+
yield backup_name
31+
finally:
32+
cleanup_backup(client, backup_name)
33+
34+
35+
@pytest.fixture
36+
def target_collection(client: PocketBase) -> Collection:
37+
collection = client.collections.create(
38+
{
39+
"name": uuid4().hex,
40+
"type": "base",
41+
"schema": [
42+
{
43+
"name": "title",
44+
"type": "text",
45+
"required": True,
46+
"options": {
47+
"min": 10,
48+
},
49+
},
50+
],
51+
}
52+
)
53+
try:
54+
yield collection
55+
finally:
56+
client.collections.delete(collection.id)
1157

12-
try:
13-
# Find new backup in list of all backups
14-
for backup in client.backups.get_full_list():
15-
if backup.key == state.backup_name:
16-
state.backup = backup
17-
assert isinstance(backup.modified, datetime.datetime)
18-
assert backup.size > 0
19-
break
20-
else:
21-
self.fail("Backup %s not found in list of all backups" % (state.backup_name,))
58+
59+
class TestBackupsService:
60+
def test_create_list_download_and_delete(self, client: PocketBase, state, backup_name):
61+
# Find new backup in list of all backups
62+
for backup in client.backups.get_full_list():
63+
if backup.key == backup_name:
64+
state.backup = backup
65+
assert isinstance(backup.modified, datetime.datetime)
66+
assert backup.size > 0
67+
break
68+
else:
69+
pytest.fail("Backup %s not found in list of all backups" % (state.backup_name,))
2270

2371
# Download the backup
24-
data = client.backups.download(state.backup_name)
25-
assert isinstance(data, bytes)
26-
assert len(data) == state.backup.size
27-
finally:
28-
# Cleanup
29-
client.backups.delete(state.backup_name)
72+
data = client.backups.download(backup_name)
73+
assert isinstance(data, bytes)
74+
assert len(data) == state.backup.size
3075

31-
# Check that it was deleted
32-
for backup in client.backups.get_full_list():
33-
if backup.key == state.backup_name:
34-
self.fail("Backup %s still found in list of all backups" % (state.backup_name,))
76+
def test_restore(self, client: PocketBase, state, backup_name, target_collection):
77+
# Create a record that will be deleted with backup is restored.
78+
collection = client.collection(target_collection.id)
79+
state.record = collection.create({"title": "Test record"})
80+
client.backups.restore(backup_name)
81+
until = time.time() + 10
82+
while time.time() < until: # Wait maximum of 10 seconds
83+
try:
84+
collection.get_one(state.record.id)
85+
except ClientResponseError as e:
86+
# Restore causes the service to restart. This will cause a connection error.
87+
# This loop will wait until the service is back up.
88+
if f"[Errno {errno.ECONNREFUSED}]" in str(e):
89+
continue
90+
# This may also occur if server shuts down in the middle of collection check request.
91+
if "Server disconnected without sending a response" in str(e):
92+
continue
93+
if e.status == http.HTTPStatus.NOT_FOUND:
3594
break
95+
raise
3696

97+
def test_upload(self, client: PocketBase, state, backup_name):
98+
state.downloaded_backup = client.backups.download(backup_name)
99+
100+
state.new_backup_name = "%s.zip" % (uuid4().hex[:16],)
101+
upload = FileUpload(state.new_backup_name, state.downloaded_backup, "application/zip")
102+
client.backups.upload(upload)
103+
try:
104+
state.downloaded_new_backup = client.backups.download(state.new_backup_name)
105+
assert state.downloaded_new_backup == state.downloaded_backup
106+
finally:
107+
cleanup_backup(client, state.new_backup_name)

0 commit comments

Comments
 (0)