mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-08-08 06:32:45 -04:00
test work
This commit is contained in:
parent
b8e5039251
commit
b6e055e47d
21 changed files with 664 additions and 171 deletions
|
@ -9,12 +9,12 @@ from .conftest import simple_update_callback
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_connect(api_connection):
|
||||
async def test_connect(api_connection: veilid.VeilidAPI):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_node_id(api_connection):
|
||||
async def test_get_node_id(api_connection: veilid.VeilidAPI):
|
||||
state = await api_connection.get_state()
|
||||
node_ids = state.config.config.network.routing_table.node_id
|
||||
|
||||
|
@ -35,7 +35,7 @@ async def test_fail_connect():
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_version(api_connection):
|
||||
async def test_version(api_connection: veilid.VeilidAPI):
|
||||
v = await api_connection.veilid_version()
|
||||
print(f"veilid_version: {v.__dict__}")
|
||||
assert v.__dict__.keys() >= {"_major", "_minor", "_patch"}
|
||||
|
|
|
@ -3,29 +3,26 @@
|
|||
import pytest
|
||||
import veilid
|
||||
from veilid.api import CryptoSystem
|
||||
import gc
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_best_crypto_system(api_connection):
|
||||
bcs: CryptoSystem = await api_connection.best_crypto_system()
|
||||
|
||||
assert await bcs.default_salt_length() == 16
|
||||
|
||||
async def test_best_crypto_system(api_connection: veilid.VeilidAPI):
|
||||
cs: CryptoSystem = await api_connection.best_crypto_system()
|
||||
async with cs:
|
||||
assert await cs.default_salt_length() == 16
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_crypto_system(api_connection):
|
||||
async def test_get_crypto_system(api_connection: veilid.VeilidAPI):
|
||||
cs: CryptoSystem = await api_connection.get_crypto_system(
|
||||
veilid.CryptoKind.CRYPTO_KIND_VLD0
|
||||
)
|
||||
|
||||
assert await cs.default_salt_length() == 16
|
||||
|
||||
# clean up handle early
|
||||
del cs
|
||||
|
||||
async with cs:
|
||||
assert await cs.default_salt_length() == 16
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_crypto_system_invalid(api_connection):
|
||||
async def test_get_crypto_system_invalid(api_connection: veilid.VeilidAPI):
|
||||
with pytest.raises(veilid.VeilidAPIErrorInvalidArgument) as exc:
|
||||
await api_connection.get_crypto_system(veilid.CryptoKind.CRYPTO_KIND_NONE)
|
||||
|
||||
|
@ -35,15 +32,17 @@ async def test_get_crypto_system_invalid(api_connection):
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_hash_and_verify_password(api_connection):
|
||||
bcs = await api_connection.best_crypto_system()
|
||||
nonce = await bcs.random_nonce()
|
||||
salt = nonce.to_bytes()
|
||||
async def test_hash_and_verify_password(api_connection: veilid.VeilidAPI):
|
||||
cs = await api_connection.best_crypto_system()
|
||||
async with cs:
|
||||
nonce = await cs.random_nonce()
|
||||
salt = nonce.to_bytes()
|
||||
|
||||
# Password match
|
||||
phash = await bcs.hash_password(b"abc123", salt)
|
||||
assert await bcs.verify_password(b"abc123", phash)
|
||||
# Password match
|
||||
phash = await cs.hash_password(b"abc123", salt)
|
||||
assert await cs.verify_password(b"abc123", phash)
|
||||
|
||||
# Password mismatch
|
||||
phash2 = await cs.hash_password(b"abc1234", salt)
|
||||
assert not await cs.verify_password(b"abc12345", phash)
|
||||
|
||||
# Password mismatch
|
||||
phash2 = await bcs.hash_password(b"abc1234", salt)
|
||||
assert not await bcs.verify_password(b"abc12345", phash)
|
||||
|
|
51
veilid-python/tests/test_dht.py
Normal file
51
veilid-python/tests/test_dht.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Routing context veilid tests
|
||||
|
||||
import veilid
|
||||
import pytest
|
||||
import asyncio
|
||||
import json
|
||||
from . import *
|
||||
|
||||
##################################################################
|
||||
BOGUS_KEY = veilid.TypedKey.from_value(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.PublicKey.from_bytes(b' '))
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI):
|
||||
# rc = await api_connection.new_routing_context()
|
||||
# async with rc:
|
||||
# with pytest.raises(veilid.VeilidAPIError):
|
||||
# out = await rc.get_dht_value(BOGUS_KEY, veilid.ValueSubkey(0), False)
|
||||
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_open_dht_record_nonexistent_no_writer(api_connection: veilid.VeilidAPI):
|
||||
# rc = await api_connection.new_routing_context()
|
||||
# async with rc:
|
||||
# with pytest.raises(veilid.VeilidAPIError):
|
||||
# out = await rc.open_dht_record(BOGUS_KEY, None)
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
|
||||
# rc = await api_connection.new_routing_context()
|
||||
# async with rc:
|
||||
# with pytest.raises(veilid.VeilidAPIError):
|
||||
# await rc.close_dht_record(BOGUS_KEY)
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
|
||||
# rc = await api_connection.new_routing_context()
|
||||
# async with rc:
|
||||
# with pytest.raises(veilid.VeilidAPIError):
|
||||
# await rc.delete_dht_record(BOGUS_KEY)
|
||||
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI):
|
||||
# rc = await api_connection.new_routing_context()
|
||||
# async with rc:
|
||||
# rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1))
|
||||
# await rc.close_dht_record(rec.key)
|
||||
# await rc.delete_dht_record(rec.key)
|
||||
|
||||
# xxx make tests for tabledb api first
|
||||
# xxx then make a test that creates a record, stores it in a table
|
||||
# xxx then make another test that gets the keys from the table and closes/deletes them
|
|
@ -12,11 +12,15 @@ from .conftest import server_info
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_routing_contexts(api_connection):
|
||||
async def test_routing_contexts(api_connection: veilid.VeilidAPI):
|
||||
rc = await api_connection.new_routing_context()
|
||||
rcp = await rc.with_privacy()
|
||||
rcps = await rcp.with_sequencing(veilid.Sequencing.ENSURE_ORDERED)
|
||||
await rcps.with_custom_privacy(veilid.Stability.RELIABLE)
|
||||
async with rc:
|
||||
rcp = await rc.with_privacy(release = False)
|
||||
async with rcp:
|
||||
rcps = await rcp.with_sequencing(veilid.Sequencing.ENSURE_ORDERED, release = False)
|
||||
async with rcps:
|
||||
rcpscp = await rcps.with_custom_privacy(veilid.Stability.RELIABLE, release = False)
|
||||
await rcpscp.release()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@ -38,24 +42,25 @@ async def test_routing_context_app_message_loopback():
|
|||
|
||||
# make a routing context that uses a safety route
|
||||
rc = await (await api.new_routing_context()).with_privacy()
|
||||
async with rc:
|
||||
|
||||
# make a new local private route
|
||||
prl, blob = await api.new_private_route()
|
||||
|
||||
# make a new local private route
|
||||
prl, blob = await api.new_private_route()
|
||||
# import it as a remote route as well so we can send to it
|
||||
prr = await api.import_remote_private_route(blob)
|
||||
|
||||
# import it as a remote route as well so we can send to it
|
||||
prr = await api.import_remote_private_route(blob)
|
||||
# send an app message to our own private route
|
||||
message = b"abcd1234"
|
||||
await rc.app_message(prr, message)
|
||||
|
||||
# send an app message to our own private route
|
||||
message = b"abcd1234"
|
||||
await rc.app_message(prr, message)
|
||||
# we should get the same message back
|
||||
update: veilid.VeilidUpdate = await asyncio.wait_for(
|
||||
app_message_queue.get(), timeout=10
|
||||
)
|
||||
|
||||
# we should get the same message back
|
||||
update: veilid.VeilidUpdate = await asyncio.wait_for(
|
||||
app_message_queue.get(), timeout=10
|
||||
)
|
||||
|
||||
assert isinstance(update.detail, veilid.VeilidAppMessage)
|
||||
assert update.detail.message == message
|
||||
assert isinstance(update.detail, veilid.VeilidAppMessage)
|
||||
assert update.detail.message == message
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@ -74,37 +79,33 @@ async def test_routing_context_app_call_loopback():
|
|||
|
||||
# make a routing context that uses a safety route
|
||||
rc = await (await api.new_routing_context()).with_privacy()
|
||||
async with rc:
|
||||
|
||||
# make a new local private route
|
||||
prl, blob = await api.new_private_route()
|
||||
|
||||
# make a new local private route
|
||||
prl, blob = await api.new_private_route()
|
||||
# import it as a remote route as well so we can send to it
|
||||
prr = await api.import_remote_private_route(blob)
|
||||
|
||||
# import it as a remote route as well so we can send to it
|
||||
prr = await api.import_remote_private_route(blob)
|
||||
# send an app message to our own private route
|
||||
request = b"abcd1234"
|
||||
app_call_task = asyncio.create_task(
|
||||
rc.app_call(prr, request), name="app call task"
|
||||
)
|
||||
|
||||
# send an app message to our own private route
|
||||
request = b"abcd1234"
|
||||
app_call_task = asyncio.create_task(
|
||||
rc.app_call(prr, request), name="app call task"
|
||||
)
|
||||
# we should get the same request back
|
||||
update: veilid.VeilidUpdate = await asyncio.wait_for(
|
||||
app_call_queue.get(), timeout=10
|
||||
)
|
||||
appcall = update.detail
|
||||
|
||||
# we should get the same request back
|
||||
update: veilid.VeilidUpdate = await asyncio.wait_for(
|
||||
app_call_queue.get(), timeout=10
|
||||
)
|
||||
appcall = update.detail
|
||||
assert isinstance(appcall, veilid.VeilidAppCall)
|
||||
assert appcall.message == request
|
||||
|
||||
assert isinstance(appcall, veilid.VeilidAppCall)
|
||||
assert appcall.message == request
|
||||
# now we reply to the request
|
||||
reply = b"qwer5678"
|
||||
await api.app_call_reply(appcall.call_id, reply)
|
||||
|
||||
# now we reply to the request
|
||||
reply = b"qwer5678"
|
||||
# TK: OperationId use to be a subclass of `int`. When I wrapped `appcall.call_id` in int(),
|
||||
# this failed JSON schema validation, which defines `call_id` as a string. Maybe that was a
|
||||
# typo, and OperationId is really *supposed* to be a str? Alternatively, perhaps the
|
||||
# signature of `app_call_reply` is wrong and it's supposed to take a type other than
|
||||
# OperationId?
|
||||
await api.app_call_reply(OperationId(appcall.call_id), reply)
|
||||
|
||||
# now we should get the reply from the call
|
||||
result = await app_call_task
|
||||
assert result == reply
|
||||
# now we should get the reply from the call
|
||||
result = await app_call_task
|
||||
assert result == reply
|
||||
|
|
127
veilid-python/tests/test_table_db.py
Normal file
127
veilid-python/tests/test_table_db.py
Normal file
|
@ -0,0 +1,127 @@
|
|||
# TableDB veilid tests
|
||||
|
||||
import pytest
|
||||
import veilid
|
||||
from veilid.api import CryptoSystem
|
||||
|
||||
|
||||
TEST_DB = "__pytest_db"
|
||||
TEST_NONEXISTENT_DB = "__pytest_nonexistent_db"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_table_db_nonexistent(api_connection: veilid.VeilidAPI):
|
||||
deleted = await api_connection.delete_table_db(TEST_NONEXISTENT_DB)
|
||||
assert not deleted
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_delete_table_db(api_connection: veilid.VeilidAPI):
|
||||
# delete test db if it exists
|
||||
await api_connection.delete_table_db(TEST_DB)
|
||||
|
||||
tdb = await api_connection.open_table_db(TEST_DB, 1)
|
||||
async with tdb:
|
||||
# delete should fail since it is still open
|
||||
with pytest.raises(veilid.VeilidAPIErrorGeneric) as exc:
|
||||
await api_connection.delete_table_db(TEST_DB)
|
||||
# drop the db
|
||||
|
||||
# now delete should succeed
|
||||
deleted = await api_connection.delete_table_db(TEST_DB)
|
||||
assert deleted
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_twice_table_db(api_connection: veilid.VeilidAPI):
|
||||
# delete test db if it exists
|
||||
await api_connection.delete_table_db(TEST_DB)
|
||||
|
||||
tdb = await api_connection.open_table_db(TEST_DB, 1)
|
||||
tdb2 = await api_connection.open_table_db(TEST_DB, 1)
|
||||
|
||||
# delete should fail because open
|
||||
with pytest.raises(veilid.VeilidAPIErrorGeneric) as exc:
|
||||
await api_connection.delete_table_db(TEST_DB)
|
||||
await tdb.release()
|
||||
|
||||
# delete should fail because open
|
||||
with pytest.raises(veilid.VeilidAPIErrorGeneric) as exc:
|
||||
await api_connection.delete_table_db(TEST_DB)
|
||||
await tdb2.release()
|
||||
|
||||
# delete should now succeed
|
||||
deleted = await api_connection.delete_table_db(TEST_DB)
|
||||
assert deleted
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_twice_table_db_store_load(api_connection: veilid.VeilidAPI):
|
||||
# delete test db if it exists
|
||||
await api_connection.delete_table_db(TEST_DB)
|
||||
|
||||
tdb = await api_connection.open_table_db(TEST_DB, 1)
|
||||
async with tdb:
|
||||
tdb2 = await api_connection.open_table_db(TEST_DB, 1)
|
||||
async with tdb2:
|
||||
# store into first db copy
|
||||
await tdb.store(b"asdf", b"1234")
|
||||
# load from second db copy
|
||||
assert await tdb.load(b"asdf") == b"1234"
|
||||
|
||||
# delete should now succeed
|
||||
deleted = await api_connection.delete_table_db(TEST_DB)
|
||||
assert deleted
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_twice_table_db_store_delete_load(api_connection: veilid.VeilidAPI):
|
||||
# delete test db if it exists
|
||||
await api_connection.delete_table_db(TEST_DB)
|
||||
|
||||
tdb = await api_connection.open_table_db(TEST_DB, 1)
|
||||
async with tdb:
|
||||
tdb2 = await api_connection.open_table_db(TEST_DB, 1)
|
||||
async with tdb2:
|
||||
|
||||
# store into first db copy
|
||||
await tdb.store(b"asdf", b"1234")
|
||||
# delete from second db copy and clean up
|
||||
await tdb2.delete(b"asdf")
|
||||
|
||||
# load from first db copy
|
||||
assert await tdb.load(b"asdf") == None
|
||||
|
||||
# delete should now succeed
|
||||
deleted = await api_connection.delete_table_db(TEST_DB)
|
||||
assert deleted
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resize_table_db(api_connection: veilid.VeilidAPI):
|
||||
# delete test db if it exists
|
||||
await api_connection.delete_table_db(TEST_DB)
|
||||
|
||||
tdb = await api_connection.open_table_db(TEST_DB, 1)
|
||||
async with tdb:
|
||||
# reopen the db with more columns should fail if it is already open
|
||||
with pytest.raises(veilid.VeilidAPIErrorGeneric) as exc:
|
||||
await api_connection.open_table_db(TEST_DB, 2)
|
||||
|
||||
tdb2 = await api_connection.open_table_db(TEST_DB, 2)
|
||||
async with tdb2:
|
||||
# write something to second column
|
||||
await tdb2.store(b"qwer", b"5678", col = 1)
|
||||
|
||||
# reopen the db with fewer columns
|
||||
tdb = await api_connection.open_table_db(TEST_DB, 1)
|
||||
async with tdb:
|
||||
|
||||
# Should fail access to second column
|
||||
with pytest.raises(veilid.VeilidAPIErrorGeneric) as exc:
|
||||
await tdb.load(b"qwer", col = 1)
|
||||
|
||||
# Should succeed with access to second column
|
||||
assert await tdb2.load(b"qwer", col = 1) == b"5678"
|
||||
|
||||
# now delete should succeed
|
||||
deleted = await api_connection.delete_table_db(TEST_DB)
|
||||
assert deleted
|
||||
|
|
@ -7,15 +7,19 @@ from .state import VeilidState
|
|||
|
||||
class RoutingContext(ABC):
|
||||
@abstractmethod
|
||||
async def with_privacy(self) -> Self:
|
||||
async def release(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def with_custom_privacy(self, stability: types.Stability) -> Self:
|
||||
async def with_privacy(self, release = True) -> Self:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def with_sequencing(self, sequencing: types.Sequencing) -> Self:
|
||||
async def with_custom_privacy(self, stability: types.Stability, release = True) -> Self:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def with_sequencing(self, sequencing: types.Sequencing, release = True) -> Self:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
@ -89,21 +93,25 @@ class TableDbTransaction(ABC):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def store(self, col: int, key: bytes, value: bytes):
|
||||
async def store(self, key: bytes, value: bytes, col: int = 0):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete(self, col: int, key: bytes):
|
||||
async def delete(self, key: bytes, col: int = 0):
|
||||
pass
|
||||
|
||||
|
||||
class TableDb(ABC):
|
||||
@abstractmethod
|
||||
async def release(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_column_count(self) -> int:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_keys(self, col: int) -> list[bytes]:
|
||||
async def get_keys(self, col: int = 0) -> list[bytes]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
@ -111,19 +119,23 @@ class TableDb(ABC):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def store(self, col: int, key: bytes, value: bytes):
|
||||
async def store(self, key: bytes, value: bytes, col: int = 0):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def load(self, col: int, key: bytes) -> Optional[bytes]:
|
||||
async def load(self, key: bytes, col: int = 0) -> Optional[bytes]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete(self, col: int, key: bytes) -> Optional[bytes]:
|
||||
async def delete(self, key: bytes, col: int = 0) -> Optional[bytes]:
|
||||
pass
|
||||
|
||||
|
||||
class CryptoSystem(ABC):
|
||||
@abstractmethod
|
||||
async def release(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def cached_dh(
|
||||
self, key: types.PublicKey, secret: types.SecretKey
|
||||
|
@ -284,7 +296,7 @@ class VeilidAPI(ABC):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete_table_db(self, name: str):
|
||||
async def delete_table_db(self, name: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
|
|
@ -320,7 +320,7 @@ class _JsonVeilidAPI(VeilidAPI):
|
|||
)
|
||||
return _JsonTableDb(self, db_id)
|
||||
|
||||
async def delete_table_db(self, name: str):
|
||||
async def delete_table_db(self, name: str) -> bool:
|
||||
return raise_api_result(
|
||||
await self.send_ndjson_request(Operation.DELETE_TABLE_DB, name=name)
|
||||
)
|
||||
|
@ -411,19 +411,44 @@ def validate_rc_op(request: dict, response: dict):
|
|||
class _JsonRoutingContext(RoutingContext):
|
||||
api: _JsonVeilidAPI
|
||||
rc_id: int
|
||||
done: bool
|
||||
|
||||
def __init__(self, api: _JsonVeilidAPI, rc_id: int):
|
||||
self.api = api
|
||||
self.rc_id = rc_id
|
||||
self.done = False
|
||||
|
||||
def __del__(self):
|
||||
self.api.send_one_way_ndjson_request(
|
||||
Operation.ROUTING_CONTEXT,
|
||||
rc_id=self.rc_id,
|
||||
rc_op=RoutingContextOperation.RELEASE,
|
||||
)
|
||||
if not self.done:
|
||||
# attempt to clean up server-side anyway
|
||||
self.api.send_one_way_ndjson_request(
|
||||
Operation.ROUTING_CONTEXT,
|
||||
rc_id=self.rc_id,
|
||||
rc_op=RoutingContextOperation.RELEASE
|
||||
)
|
||||
|
||||
# complain
|
||||
raise AssertionError("Should have released routing context before dropping object")
|
||||
|
||||
async def with_privacy(self) -> Self:
|
||||
async def __aenter__(self) -> Self:
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *excinfo):
|
||||
if not self.done:
|
||||
await self.release()
|
||||
|
||||
async def release(self):
|
||||
if self.done:
|
||||
return
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.ROUTING_CONTEXT,
|
||||
validate=validate_rc_op,
|
||||
rc_id=self.rc_id,
|
||||
rc_op=RoutingContextOperation.RELEASE
|
||||
)
|
||||
self.done = True
|
||||
|
||||
async def with_privacy(self, release = True) -> Self:
|
||||
new_rc_id = raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.ROUTING_CONTEXT,
|
||||
|
@ -432,9 +457,11 @@ class _JsonRoutingContext(RoutingContext):
|
|||
rc_op=RoutingContextOperation.WITH_PRIVACY,
|
||||
)
|
||||
)
|
||||
if release:
|
||||
await self.release()
|
||||
return self.__class__(self.api, new_rc_id)
|
||||
|
||||
async def with_custom_privacy(self, stability: Stability) -> Self:
|
||||
async def with_custom_privacy(self, stability: Stability, release = True) -> Self:
|
||||
new_rc_id = raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.ROUTING_CONTEXT,
|
||||
|
@ -444,9 +471,11 @@ class _JsonRoutingContext(RoutingContext):
|
|||
stability=stability,
|
||||
)
|
||||
)
|
||||
if release:
|
||||
await self.release()
|
||||
return self.__class__(self.api, new_rc_id)
|
||||
|
||||
async def with_sequencing(self, sequencing: Sequencing) -> Self:
|
||||
async def with_sequencing(self, sequencing: Sequencing, release = True) -> Self:
|
||||
new_rc_id = raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.ROUTING_CONTEXT,
|
||||
|
@ -456,6 +485,8 @@ class _JsonRoutingContext(RoutingContext):
|
|||
sequencing=sequencing,
|
||||
)
|
||||
)
|
||||
if release:
|
||||
await self.release()
|
||||
return self.__class__(self.api, new_rc_id)
|
||||
|
||||
async def app_call(self, target: TypedKey | RouteId, request: bytes) -> bytes:
|
||||
|
@ -627,9 +658,27 @@ class _JsonTableDbTransaction(TableDbTransaction):
|
|||
|
||||
def __del__(self):
|
||||
if not self.done:
|
||||
raise AssertionError("Should have committed or rolled back transaction")
|
||||
# attempt to clean up server-side anyway
|
||||
self.api.send_one_way_ndjson_request(
|
||||
Operation.TABLE_DB_TRANSACTION,
|
||||
tx_id=self.tx_id,
|
||||
tx_op=TableDbTransactionOperation.ROLLBACK,
|
||||
)
|
||||
|
||||
# complain
|
||||
raise AssertionError("Should have committed or rolled back transaction before dropping object")
|
||||
|
||||
async def __aenter__(self) -> Self:
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *excinfo):
|
||||
if not self.done:
|
||||
await self.rollback()
|
||||
|
||||
async def commit(self):
|
||||
if self.done:
|
||||
raise AssertionError("Transaction is already done")
|
||||
|
||||
raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.TABLE_DB_TRANSACTION,
|
||||
|
@ -641,17 +690,17 @@ class _JsonTableDbTransaction(TableDbTransaction):
|
|||
self.done = True
|
||||
|
||||
async def rollback(self):
|
||||
raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.TABLE_DB_TRANSACTION,
|
||||
validate=validate_tx_op,
|
||||
tx_id=self.tx_id,
|
||||
tx_op=TableDbTransactionOperation.ROLLBACK,
|
||||
)
|
||||
if self.done:
|
||||
raise AssertionError("Transaction is already done")
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.TABLE_DB_TRANSACTION,
|
||||
validate=validate_tx_op,
|
||||
tx_id=self.tx_id,
|
||||
tx_op=TableDbTransactionOperation.ROLLBACK,
|
||||
)
|
||||
self.done = True
|
||||
|
||||
async def store(self, col: int, key: bytes, value: bytes):
|
||||
async def store(self, key: bytes, value: bytes, col: int = 0):
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.TABLE_DB_TRANSACTION,
|
||||
validate=validate_tx_op,
|
||||
|
@ -662,7 +711,7 @@ class _JsonTableDbTransaction(TableDbTransaction):
|
|||
value=value,
|
||||
)
|
||||
|
||||
async def delete(self, col: int, key: bytes):
|
||||
async def delete(self, key: bytes, col: int = 0):
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.TABLE_DB_TRANSACTION,
|
||||
validate=validate_tx_op,
|
||||
|
@ -684,15 +733,44 @@ def validate_db_op(request: dict, response: dict):
|
|||
class _JsonTableDb(TableDb):
|
||||
api: _JsonVeilidAPI
|
||||
db_id: int
|
||||
done: bool
|
||||
|
||||
def __init__(self, api: _JsonVeilidAPI, db_id: int):
|
||||
self.api = api
|
||||
self.db_id = db_id
|
||||
self.done = False
|
||||
|
||||
def __del__(self):
|
||||
self.api.send_one_way_ndjson_request(
|
||||
Operation.TABLE_DB, db_id=self.db_id, rc_op=TableDbOperation.RELEASE
|
||||
if not self.done:
|
||||
|
||||
# attempt to clean up server-side anyway
|
||||
self.api.send_one_way_ndjson_request(
|
||||
Operation.TABLE_DB,
|
||||
db_id=self.db_id,
|
||||
db_op=TableDbOperation.RELEASE
|
||||
)
|
||||
|
||||
# complain
|
||||
raise AssertionError("Should have released table db before dropping object")
|
||||
|
||||
async def __aenter__(self) -> Self:
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *excinfo):
|
||||
if not self.done:
|
||||
await self.release()
|
||||
|
||||
async def release(self):
|
||||
if self.done:
|
||||
return
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.TABLE_DB,
|
||||
validate=validate_db_op,
|
||||
db_id=self.db_id,
|
||||
db_op=TableDbOperation.RELEASE
|
||||
)
|
||||
self.done = True
|
||||
|
||||
|
||||
async def get_column_count(self) -> int:
|
||||
return raise_api_result(
|
||||
|
@ -704,7 +782,7 @@ class _JsonTableDb(TableDb):
|
|||
)
|
||||
)
|
||||
|
||||
async def get_keys(self, col: int) -> list[bytes]:
|
||||
async def get_keys(self, col: int = 0) -> list[bytes]:
|
||||
return list(
|
||||
map(
|
||||
lambda x: urlsafe_b64decode_no_pad(x),
|
||||
|
@ -731,7 +809,7 @@ class _JsonTableDb(TableDb):
|
|||
)
|
||||
return _JsonTableDbTransaction(self.api, tx_id)
|
||||
|
||||
async def store(self, col: int, key: bytes, value: bytes):
|
||||
async def store(self, key: bytes, value: bytes, col: int = 0):
|
||||
return raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.TABLE_DB,
|
||||
|
@ -744,7 +822,7 @@ class _JsonTableDb(TableDb):
|
|||
)
|
||||
)
|
||||
|
||||
async def load(self, col: int, key: bytes) -> Optional[bytes]:
|
||||
async def load(self, key: bytes, col: int = 0) -> Optional[bytes]:
|
||||
res = raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.TABLE_DB,
|
||||
|
@ -757,7 +835,7 @@ class _JsonTableDb(TableDb):
|
|||
)
|
||||
return None if res is None else urlsafe_b64decode_no_pad(res)
|
||||
|
||||
async def delete(self, col: int, key: bytes) -> Optional[bytes]:
|
||||
async def delete(self, key: bytes, col: int = 0) -> Optional[bytes]:
|
||||
res = raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.TABLE_DB,
|
||||
|
@ -782,17 +860,43 @@ def validate_cs_op(request: dict, response: dict):
|
|||
class _JsonCryptoSystem(CryptoSystem):
|
||||
api: _JsonVeilidAPI
|
||||
cs_id: int
|
||||
done: bool
|
||||
|
||||
def __init__(self, api: _JsonVeilidAPI, cs_id: int):
|
||||
self.api = api
|
||||
self.cs_id = cs_id
|
||||
self.done = False
|
||||
|
||||
def __del__(self):
|
||||
self.api.send_one_way_ndjson_request(
|
||||
if not self.done:
|
||||
|
||||
# attempt to clean up server-side anyway
|
||||
self.api.send_one_way_ndjson_request(
|
||||
Operation.CRYPTO_SYSTEM,
|
||||
cs_id=self.cs_id,
|
||||
cs_op=CryptoSystemOperation.RELEASE
|
||||
)
|
||||
|
||||
# complain
|
||||
raise AssertionError("Should have released crypto system before dropping object")
|
||||
|
||||
async def __aenter__(self) -> Self:
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *excinfo):
|
||||
if not self.done:
|
||||
await self.release()
|
||||
|
||||
async def release(self):
|
||||
if self.done:
|
||||
return
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.CRYPTO_SYSTEM,
|
||||
validate=validate_cs_op,
|
||||
cs_id=self.cs_id,
|
||||
cs_op=CryptoSystemOperation.RELEASE,
|
||||
cs_op=CryptoSystemOperation.RELEASE
|
||||
)
|
||||
self.done = True
|
||||
|
||||
async def cached_dh(self, key: PublicKey, secret: SecretKey) -> SharedSecret:
|
||||
return SharedSecret(
|
||||
|
|
|
@ -1319,6 +1319,30 @@
|
|||
},
|
||||
{
|
||||
"type": "object",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"error"
|
||||
],
|
||||
"properties": {
|
||||
"error": {
|
||||
"$ref": "#/definitions/VeilidAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [
|
||||
"tx_op"
|
||||
],
|
||||
|
@ -1333,6 +1357,30 @@
|
|||
},
|
||||
{
|
||||
"type": "object",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"error"
|
||||
],
|
||||
"properties": {
|
||||
"error": {
|
||||
"$ref": "#/definitions/VeilidAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [
|
||||
"tx_op"
|
||||
],
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import Optional, Self
|
|||
|
||||
from .config import VeilidConfig
|
||||
from .types import (ByteCount, RouteId, Timestamp, TimestampDuration, TypedKey,
|
||||
ValueData, ValueSubkey, VeilidLogLevel,
|
||||
ValueData, ValueSubkey, VeilidLogLevel, OperationId,
|
||||
urlsafe_b64decode_no_pad)
|
||||
|
||||
|
||||
|
@ -309,9 +309,9 @@ class VeilidAppMessage:
|
|||
class VeilidAppCall:
|
||||
sender: Optional[TypedKey]
|
||||
message: bytes
|
||||
call_id: str
|
||||
call_id: OperationId
|
||||
|
||||
def __init__(self, sender: Optional[TypedKey], message: bytes, call_id: str):
|
||||
def __init__(self, sender: Optional[TypedKey], message: bytes, call_id: OperationId):
|
||||
self.sender = sender
|
||||
self.message = message
|
||||
self.call_id = call_id
|
||||
|
@ -322,7 +322,7 @@ class VeilidAppCall:
|
|||
return cls(
|
||||
None if j["sender"] is None else TypedKey(j["sender"]),
|
||||
urlsafe_b64decode_no_pad(j["message"]),
|
||||
j["call_id"],
|
||||
OperationId(j["call_id"]),
|
||||
)
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue