Skip to content

Commit 98ba442

Browse files
author
Shubham
committed
Test with live Sync server
Before starting the test-session, initiate a Sync server with Docker.
1 parent 810c5e9 commit 98ba442

5 files changed

Lines changed: 144 additions & 66 deletions

File tree

objectbox/sync.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -562,12 +562,14 @@ def close(self):
562562
It can no longer be used afterwards, make a new sync client instead.
563563
Does nothing if this sync client has already been closed.
564564
"""
565-
c.obx_sync_listener_error(self.__c_sync_client_ptr, None, None)
566-
c.obx_sync_listener_login(self.__c_sync_client_ptr, None, None)
567-
c.obx_sync_listener_login_failure(self.__c_sync_client_ptr, None, None)
568-
c.obx_sync_listener_connect(self.__c_sync_client_ptr, None, None)
569-
c.obx_sync_listener_disconnect(self.__c_sync_client_ptr, None, None)
570-
c.obx_sync_listener_change(self.__c_sync_client_ptr, None, None)
565+
c.obx_sync_listener_error(self.__c_sync_client_ptr, ctypes.cast(None, c.OBX_sync_listener_error_t), None)
566+
c.obx_sync_listener_login(self.__c_sync_client_ptr, ctypes.cast(None, c.OBX_sync_listener_login_t), None)
567+
c.obx_sync_listener_login_failure(self.__c_sync_client_ptr,
568+
ctypes.cast(None, c.OBX_sync_listener_login_failure_t), None)
569+
c.obx_sync_listener_connect(self.__c_sync_client_ptr, ctypes.cast(None, c.OBX_sync_listener_connect_t), None)
570+
c.obx_sync_listener_disconnect(self.__c_sync_client_ptr, ctypes.cast(None, c.OBX_sync_listener_disconnect_t),
571+
None)
572+
c.obx_sync_listener_change(self.__c_sync_client_ptr, ctypes.cast(None, c.OBX_sync_listener_change_t), None)
571573
c.obx_sync_close(self.__c_sync_client_ptr)
572574
self.__c_sync_client_ptr = None
573575

tests/common.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
import os
2+
import time
3+
import subprocess
4+
import socket
5+
26
import pytest
37
import objectbox
48
from objectbox.logger import logger
59
from tests.model import *
610
import numpy as np
711
from datetime import datetime, timezone
812
from objectbox import *
13+
import logging
14+
15+
from dataclasses import dataclass
16+
17+
test_logger = logging.getLogger(__name__)
18+
919

1020
def remove_json_model_file():
1121
path = os.path.dirname(os.path.realpath(__file__))
@@ -35,6 +45,56 @@ def create_test_store(db_path: str = "testdata", clear_db: bool = True) -> objec
3545
return objectbox.Store(model=create_default_model(), directory=db_path)
3646

3747

48+
@dataclass
49+
class SyncServerConfig:
50+
container_id: str
51+
port: int
52+
53+
54+
def start_sync_server() -> SyncServerConfig | None:
55+
""" Starts the ObjectBox Sync Server in a Docker container. """
56+
current_dir = os.path.dirname(os.path.realpath(__file__))
57+
user_id = os.getuid()
58+
try:
59+
command = ("docker run "
60+
"--rm "
61+
"-d "
62+
f"--volume {current_dir}:/data "
63+
f"--user {user_id} "
64+
"-p 127.0.0.1:9999:9999 "
65+
"objectboxio/sync-server-trial "
66+
"--conf sync_server_config.json")
67+
logger.info("Using command to start Sync Server Docker container:" + command)
68+
stdout = subprocess.run(command.split(), check=True, capture_output=True, text=True).stdout
69+
container_id = stdout.strip()
70+
71+
start_time = time.time()
72+
while (time.time() - start_time) < 10:
73+
try:
74+
with socket.create_connection(("127.0.0.1", 9999)):
75+
break
76+
except OSError:
77+
pass
78+
else:
79+
raise RuntimeError("Timed out waiting for Sync Server to start")
80+
81+
test_logger.info("Started ObjectBox Sync Server in Docker")
82+
return SyncServerConfig(container_id=container_id, port=9999)
83+
except Exception as e:
84+
test_logger.warning(f"Could not start ObjectBox Sync Server in Docker: {e}")
85+
return None
86+
87+
88+
def stop_sync_server(container_id: str):
89+
""" Stops the ObjectBox Sync Server Docker container. """
90+
try:
91+
command = f"docker stop {container_id}"
92+
subprocess.run(command.split(), check=True)
93+
test_logger.info("Stopped ObjectBox Sync Server Docker container")
94+
except Exception as e:
95+
test_logger.warning(f"Could not stop ObjectBox Sync Server Docker container: {e}")
96+
97+
3898
def assert_equal_prop(actual, expected, default):
3999
if isinstance(expected, objectbox.model.properties.Property):
40100
assert (actual == default)

tests/conftest.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22
from objectbox.logger import logger
3-
from objectbox.sync import SyncLoginListener, SyncConnectionListener, SyncErrorListener
3+
from objectbox.sync import SyncLoginListener, SyncConnectionListener, SyncErrorListener, SyncClient, SyncCredentials
44
from common import *
55

66

@@ -21,6 +21,7 @@ def test_store():
2121
yield store
2222
store.close()
2323

24+
2425
class TestLoginListener(SyncLoginListener):
2526
def __init__(self):
2627
self.logged_in_called = False
@@ -52,22 +53,45 @@ def __init__(self):
5253
def on_error(self, sync_error_code: int):
5354
self.sync_error_code = sync_error_code
5455

56+
5557
@pytest.fixture
5658
def connection_listener():
5759
listener = TestConnectionListener()
5860
yield listener
5961
listener.connected_called = False
6062
listener.disconnected_called = False
6163

64+
6265
@pytest.fixture
6366
def login_listener():
6467
listener = TestLoginListener()
6568
yield listener
6669
listener.logged_in_called = False
6770
listener.login_failure_code = None
6871

72+
6973
@pytest.fixture
7074
def error_listener():
7175
listener = TestErrorListener()
7276
yield listener
73-
listener.sync_error_code = None
77+
listener.sync_error_code = None
78+
79+
80+
@pytest.fixture
81+
def sync_client(test_store, login_listener, connection_listener, error_listener):
82+
server_urls = ["ws://127.0.0.1:9999"]
83+
client = SyncClient(test_store, server_urls)
84+
client.set_credentials(SyncCredentials.none())
85+
client.set_login_listener(login_listener)
86+
client.set_connection_listener(connection_listener)
87+
client.set_error_listener(error_listener)
88+
yield client
89+
client.close()
90+
91+
92+
@pytest.fixture(scope="session")
93+
def sync_server():
94+
server_config = start_sync_server()
95+
yield server_config
96+
if server_config:
97+
stop_sync_server(server_config.container_id)

tests/sync_server_config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"modelFile": "/data/objectbox-model.json",
3+
"dbMaxSize": "1G",
4+
"bind": "ws://0.0.0.0:9999",
5+
"adminBind": "http://0.0.0.0:9980",
6+
"unsecuredNoAuthentication": true,
7+
"_debug": true
8+
}

tests/test_sync.py

Lines changed: 42 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from collections.abc import Callable
2-
from time import sleep
32

43
import pytest
54

@@ -11,32 +10,28 @@ def test_sync_protocol_version():
1110
version = SyncClient.protocol_version()
1211
assert version >= 1
1312

14-
def test_sync_client_states(test_store):
15-
server_urls = ["ws://localhost:9999"]
16-
client = SyncClient(test_store, server_urls)
17-
assert client.get_sync_state() == SyncState.CREATED
18-
client.start()
19-
assert client.get_sync_state() == SyncState.STARTED
20-
client.stop()
21-
assert client.get_sync_state() == SyncState.STOPPED
22-
client.close()
2313

24-
def test_sync_listener(test_store, login_listener, connection_listener):
25-
server_urls = ["ws://127.0.0.1:9999"]
26-
client = SyncClient(test_store, server_urls)
27-
client.set_credentials(SyncCredentials.shared_secret_string("shared_secret"))
28-
client.set_login_listener(login_listener)
29-
client.set_connection_listener(connection_listener)
14+
def test_sync_client_states(sync_client):
15+
assert sync_client.get_sync_state() == SyncState.CREATED
16+
sync_client.start()
17+
assert sync_client.get_sync_state() == SyncState.STARTED
18+
sync_client.stop()
19+
assert sync_client.get_sync_state() == SyncState.STOPPED
20+
sync_client.close()
3021

31-
client.start()
32-
sleep(1)
33-
client.stop()
34-
client.close()
3522

36-
assert login_listener.login_failure_code is not None
37-
assert login_listener.login_failure_code == SyncCode.CREDENTIALS_REJECTED
23+
def test_sync_listener(sync_server, sync_client, login_listener, connection_listener):
24+
if not sync_server:
25+
pytest.skip("Sync server not available")
26+
27+
sync_client.start()
28+
sync_client.wait_for_logged_in_state(timeout_millis=5000)
29+
sync_client.stop()
30+
sync_client.close()
31+
32+
assert login_listener.logged_in_called
33+
assert login_listener.login_failure_code is None
3834
assert connection_listener.connected_called
39-
assert connection_listener.disconnected_called
4035

4136

4237
def test_filter_variables(test_store):
@@ -59,35 +54,29 @@ def test_filter_variables(test_store):
5954
client.close()
6055

6156

62-
def test_outgoing_message_count(test_store):
63-
server_urls = ["ws://localhost:9999"]
64-
client = SyncClient(test_store, server_urls)
65-
66-
count = client.get_outgoing_message_count()
57+
def test_outgoing_message_count(sync_client):
58+
count = sync_client.get_outgoing_message_count()
6759
assert count == 0
6860

69-
count_limited = client.get_outgoing_message_count(limit=10)
61+
count_limited = sync_client.get_outgoing_message_count(limit=10)
7062
assert count_limited == 0
7163

72-
client.close()
73-
74-
with pytest.raises(IllegalArgumentError, match='Argument "sync" must not be null'):
75-
client.get_outgoing_message_count()
64+
sync_client.close()
7665

66+
with pytest.raises(ValueError, match='SyncClient already closed'):
67+
sync_client.get_outgoing_message_count()
7768

78-
def test_multiple_credentials(test_store):
79-
server_urls = ["ws://localhost:9999"]
80-
client = SyncClient(test_store, server_urls)
8169

70+
def test_multiple_credentials(sync_client):
8271
# empty list should raise ValueError
8372
with pytest.raises(ValueError, match='Provide at least one credential'):
84-
client.set_multiple_credentials([])
73+
sync_client.set_multiple_credentials([])
8574

8675
# SyncCredentials.none() is not supported with multiple credentials
8776
with pytest.raises(ValueError, match=r'SyncCredentials.none\(\) is not supported, use set_credentials\(\) instead'):
88-
client.set_multiple_credentials([SyncCredentials.none()])
77+
sync_client.set_multiple_credentials([SyncCredentials.none()])
8978

90-
client.set_multiple_credentials([
79+
sync_client.set_multiple_credentials([
9180
SyncCredentials.google_auth("token_google"),
9281
SyncCredentials.user_and_password("user1", "password"),
9382
SyncCredentials.shared_secret_string("secret1"),
@@ -98,39 +87,34 @@ def test_multiple_credentials(test_store):
9887
])
9988

10089

101-
def test_client_closed_when_store_closed(test_store):
102-
server_urls = ["ws://localhost:9999"]
103-
client = SyncClient(test_store, server_urls)
104-
105-
assert not client.is_closed()
90+
def test_client_closed_when_store_closed(test_store, sync_client):
91+
assert not sync_client.is_closed()
10692
test_store.close()
107-
assert client.is_closed()
93+
assert sync_client.is_closed()
10894

10995

11096
def assert_raises_value_error(fn: Callable[[], object | None], message: str | None = None):
11197
with pytest.raises(ValueError, match=message):
11298
fn()
11399

114100

115-
def test_client_access_after_close_throws_error(test_store):
116-
server_urls = ["ws://localhost:9999"]
117-
client = SyncClient(test_store, server_urls)
118-
client.close()
101+
def test_client_access_after_close_throws_error(sync_client):
102+
sync_client.close()
119103

120-
assert client.is_closed()
104+
assert sync_client.is_closed()
121105

122106
match_error = "SyncClient already closed"
123107

124-
assert_raises_value_error(message=match_error, fn=lambda: client.start())
125-
assert_raises_value_error(message=match_error, fn=lambda: client.stop())
126-
assert_raises_value_error(message=match_error, fn=lambda: client.get_sync_state())
127-
assert_raises_value_error(message=match_error, fn=lambda: client.get_outgoing_message_count())
128-
assert_raises_value_error(message=match_error, fn=lambda: client.set_credentials(SyncCredentials.none()))
108+
assert_raises_value_error(message=match_error, fn=lambda: sync_client.start())
109+
assert_raises_value_error(message=match_error, fn=lambda: sync_client.stop())
110+
assert_raises_value_error(message=match_error, fn=lambda: sync_client.get_sync_state())
111+
assert_raises_value_error(message=match_error, fn=lambda: sync_client.get_outgoing_message_count())
112+
assert_raises_value_error(message=match_error, fn=lambda: sync_client.set_credentials(SyncCredentials.none()))
129113
assert_raises_value_error(message=match_error,
130-
fn=lambda: client.set_credentials(SyncCredentials.google_auth("token_google")))
131-
assert_raises_value_error(message=match_error, fn=lambda: client.set_multiple_credentials([
114+
fn=lambda: sync_client.set_credentials(SyncCredentials.google_auth("token_google")))
115+
assert_raises_value_error(message=match_error, fn=lambda: sync_client.set_multiple_credentials([
132116
SyncCredentials.google_auth("token_google"),
133117
SyncCredentials.user_and_password("user1", "password")
134118
]))
135119
assert_raises_value_error(message=match_error,
136-
fn=lambda: client.set_request_updates_mode(SyncRequestUpdatesMode.AUTO))
120+
fn=lambda: sync_client.set_request_updates_mode(SyncRequestUpdatesMode.AUTO))

0 commit comments

Comments
 (0)