Add relations export and remote full backup/restore.
Expose GET/POST /api/v1/export/ for server dumps and add import UI for relations (CSV/JSON) plus remote backup controls. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import pytest
|
||||
|
||||
from contacts.models import Contact, NetworkMap, NetworkMapMembership, NetworkMapType, Relation
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_export_dump_empty(api_client):
|
||||
response = api_client.get('/api/v1/export/')
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data['version'] == 2
|
||||
assert data['contacts'] == []
|
||||
assert data['relations'] == []
|
||||
assert isinstance(data['networkMapTypes'], list)
|
||||
assert isinstance(data['networkMaps'], list)
|
||||
assert isinstance(data['networkMapMemberships'], list)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_export_dump_with_data(api_client, two_contacts, sample_relation):
|
||||
response = api_client.get('/api/v1/export/')
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data['contacts']) == 2
|
||||
assert len(data['relations']) == 1
|
||||
assert data['relations'][0]['source'] == sample_relation.source_id
|
||||
assert data['relations'][0]['target'] == sample_relation.target_id
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_import_dump_replace(api_client, two_contacts, sample_relation):
|
||||
export_response = api_client.get('/api/v1/export/')
|
||||
dump = export_response.json()
|
||||
assert len(dump['contacts']) == 2
|
||||
|
||||
Contact.objects.all().delete()
|
||||
assert Contact.objects.count() == 0
|
||||
|
||||
import_response = api_client.post('/api/v1/export/?replace=true', dump, format='json')
|
||||
assert import_response.status_code == 200
|
||||
result = import_response.json()
|
||||
assert result['importedContacts'] == 2
|
||||
assert result['importedRelations'] == 1
|
||||
assert Contact.objects.count() == 2
|
||||
assert Relation.objects.count() == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_import_dump_invalid(api_client):
|
||||
response = api_client.post('/api/v1/export/', {'contacts': []}, format='json')
|
||||
assert response.status_code == 400
|
||||
assert 'error' in response.json()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_import_dump_with_maps(api_client, db):
|
||||
NetworkMapMembership.objects.all().delete()
|
||||
NetworkMap.objects.all().delete()
|
||||
NetworkMapType.objects.all().delete()
|
||||
|
||||
map_type = NetworkMapType.objects.create(
|
||||
name='Базовый',
|
||||
sectors=[{'id': 'work', 'label': 'Работа'}],
|
||||
circles=[{'id': 'close', 'label': 'Близкий'}],
|
||||
is_default=True,
|
||||
)
|
||||
contact = Contact.objects.create(name='Алиса')
|
||||
network_map = NetworkMap.objects.create(
|
||||
name='Основная',
|
||||
description='Тест',
|
||||
map_type=map_type,
|
||||
)
|
||||
network_map.memberships.create(
|
||||
contact=contact,
|
||||
life_sphere='work',
|
||||
network_circle='close',
|
||||
importance=4,
|
||||
)
|
||||
|
||||
dump = api_client.get('/api/v1/export/').json()
|
||||
assert len(dump['contacts']) == 1
|
||||
assert len(dump['networkMaps']) == 1
|
||||
assert len(dump['networkMapMemberships']) == 1
|
||||
|
||||
Contact.objects.all().delete()
|
||||
NetworkMap.objects.all().delete()
|
||||
NetworkMapType.objects.all().delete()
|
||||
|
||||
result = api_client.post('/api/v1/export/?replace=true', dump, format='json').json()
|
||||
assert result['importedContacts'] == 1
|
||||
assert result['importedMaps'] == 1
|
||||
assert result['importedMemberships'] == 1
|
||||
assert NetworkMapType.objects.count() == 1
|
||||
assert NetworkMap.objects.count() == 1
|
||||
Reference in New Issue
Block a user