mirror of
https://gitlab.com/veilid/veilid.git
synced 2024-10-01 01:26:08 -04:00
more python test cleanup and fixes
This commit is contained in:
parent
800348451e
commit
c468d9c850
@ -335,8 +335,10 @@ Future<void> testWatchDHTValues(Stream<VeilidUpdate> updateStream) async {
|
|||||||
// Reopen without closing to change routing context and not lose watch
|
// Reopen without closing to change routing context and not lose watch
|
||||||
rec = await rcSet.openDHTRecord(rec.key, writer: rec.ownerKeyPair());
|
rec = await rcSet.openDHTRecord(rec.key, writer: rec.ownerKeyPair());
|
||||||
|
|
||||||
|
// Set the value without a watch
|
||||||
expect(await rcSet.setDHTValue(rec.key, 3, utf8.encode("BLAH")), isNull);
|
expect(await rcSet.setDHTValue(rec.key, 3, utf8.encode("BLAH")), isNull);
|
||||||
|
|
||||||
|
// Now we should NOT get an update
|
||||||
if (await valueChangeQueueIterator
|
if (await valueChangeQueueIterator
|
||||||
.moveNext()
|
.moveNext()
|
||||||
.timeout(const Duration(seconds: 5), onTimeout: () {
|
.timeout(const Duration(seconds: 5), onTimeout: () {
|
||||||
@ -345,6 +347,7 @@ Future<void> testWatchDHTValues(Stream<VeilidUpdate> updateStream) async {
|
|||||||
fail("should not have a change");
|
fail("should not have a change");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
await rcSet.closeDHTRecord(rec.key);
|
await rcSet.closeDHTRecord(rec.key);
|
||||||
await rcSet.deleteDHTRecord(rec.key);
|
await rcSet.deleteDHTRecord(rec.key);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -5,6 +5,7 @@ import pytest
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from . import *
|
from . import *
|
||||||
|
from .api import VeilidTestConnectionError, api_connector
|
||||||
|
|
||||||
##################################################################
|
##################################################################
|
||||||
BOGUS_KEY = veilid.TypedKey.from_value(
|
BOGUS_KEY = veilid.TypedKey.from_value(
|
||||||
@ -13,7 +14,9 @@ BOGUS_KEY = veilid.TypedKey.from_value(
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI):
|
async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI):
|
||||||
rc = await api_connection.new_routing_context()
|
rc = await (await api_connection.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
async with rc:
|
async with rc:
|
||||||
with pytest.raises(veilid.VeilidAPIError):
|
with pytest.raises(veilid.VeilidAPIError):
|
||||||
out = await rc.get_dht_value(BOGUS_KEY, veilid.ValueSubkey(0), False)
|
out = await rc.get_dht_value(BOGUS_KEY, veilid.ValueSubkey(0), False)
|
||||||
@ -21,7 +24,9 @@ async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_open_dht_record_nonexistent_no_writer(api_connection: veilid.VeilidAPI):
|
async def test_open_dht_record_nonexistent_no_writer(api_connection: veilid.VeilidAPI):
|
||||||
rc = await api_connection.new_routing_context()
|
rc = await (await api_connection.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
async with rc:
|
async with rc:
|
||||||
with pytest.raises(veilid.VeilidAPIError):
|
with pytest.raises(veilid.VeilidAPIError):
|
||||||
out = await rc.open_dht_record(BOGUS_KEY, None)
|
out = await rc.open_dht_record(BOGUS_KEY, None)
|
||||||
@ -29,7 +34,9 @@ async def test_open_dht_record_nonexistent_no_writer(api_connection: veilid.Veil
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
|
async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
|
||||||
rc = await api_connection.new_routing_context()
|
rc = await (await api_connection.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
async with rc:
|
async with rc:
|
||||||
with pytest.raises(veilid.VeilidAPIError):
|
with pytest.raises(veilid.VeilidAPIError):
|
||||||
await rc.close_dht_record(BOGUS_KEY)
|
await rc.close_dht_record(BOGUS_KEY)
|
||||||
@ -37,7 +44,9 @@ async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
|
async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
|
||||||
rc = await api_connection.new_routing_context()
|
rc = await (await api_connection.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
async with rc:
|
async with rc:
|
||||||
with pytest.raises(veilid.VeilidAPIError):
|
with pytest.raises(veilid.VeilidAPIError):
|
||||||
await rc.delete_dht_record(BOGUS_KEY)
|
await rc.delete_dht_record(BOGUS_KEY)
|
||||||
@ -45,7 +54,9 @@ async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI):
|
async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI):
|
||||||
rc = await api_connection.new_routing_context()
|
rc = await (await api_connection.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
async with rc:
|
async with rc:
|
||||||
rec = await rc.create_dht_record(
|
rec = await rc.create_dht_record(
|
||||||
veilid.DHTSchema.dflt(1), veilid.CryptoKind.CRYPTO_KIND_VLD0
|
veilid.DHTSchema.dflt(1), veilid.CryptoKind.CRYPTO_KIND_VLD0
|
||||||
@ -56,7 +67,9 @@ async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI)
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI):
|
async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI):
|
||||||
rc = await api_connection.new_routing_context()
|
rc = await (await api_connection.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
async with rc:
|
async with rc:
|
||||||
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(1))
|
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(1))
|
||||||
assert await rc.get_dht_value(rec.key, 0, False) == None
|
assert await rc.get_dht_value(rec.key, 0, False) == None
|
||||||
@ -66,7 +79,9 @@ async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_set_get_dht_value(api_connection: veilid.VeilidAPI):
|
async def test_set_get_dht_value(api_connection: veilid.VeilidAPI):
|
||||||
rc = await api_connection.new_routing_context()
|
rc = await (await api_connection.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
async with rc:
|
async with rc:
|
||||||
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2))
|
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2))
|
||||||
|
|
||||||
@ -93,7 +108,9 @@ async def test_set_get_dht_value(api_connection: veilid.VeilidAPI):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
|
async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
|
||||||
rc = await api_connection.new_routing_context()
|
rc = await (await api_connection.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
async with rc:
|
async with rc:
|
||||||
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2))
|
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2))
|
||||||
key = rec.key
|
key = rec.key
|
||||||
@ -204,38 +221,116 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
|
|||||||
await rc.delete_dht_record(key)
|
await rc.delete_dht_record(key)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_watch_dht_values(api_connection: veilid.VeilidAPI):
|
async def test_watch_dht_values():
|
||||||
rc = await api_connection.new_routing_context()
|
|
||||||
async with rc:
|
|
||||||
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(10))
|
|
||||||
|
|
||||||
vd = await rc.set_dht_value(rec.key, 3, b"BLAH BLAH BLAH")
|
value_change_queue: asyncio.Queue[veilid.VeilidUpdate] = asyncio.Queue()
|
||||||
|
|
||||||
|
async def value_change_update_callback(update: veilid.VeilidUpdate):
|
||||||
|
if update.kind == veilid.VeilidUpdateKind.VALUE_CHANGE:
|
||||||
|
await value_change_queue.put(update)
|
||||||
|
|
||||||
|
try:
|
||||||
|
api = await api_connector(value_change_update_callback)
|
||||||
|
except VeilidTestConnectionError:
|
||||||
|
pytest.skip("Unable to connect to veilid-server.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Make two routing contexts, one with and one without safety
|
||||||
|
# So we can pretend to be a different node and get the watch updates
|
||||||
|
# Normally they would not get sent if the set comes from the same target
|
||||||
|
# as the watch's target
|
||||||
|
rcWatch = await (await api.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
|
rcSet = await (await api.new_routing_context()).with_safety(
|
||||||
|
veilid.SafetySelection.unsafe(veilid.Sequencing.ENSURE_ORDERED)
|
||||||
|
)
|
||||||
|
async with rcWatch, rcSet:
|
||||||
|
# Make a DHT record
|
||||||
|
rec = await rcWatch.create_dht_record(veilid.DHTSchema.dflt(10))
|
||||||
|
|
||||||
|
# Set some subkey we care about
|
||||||
|
vd = await rcWatch.set_dht_value(rec.key, 3, b"BLAH BLAH BLAH")
|
||||||
assert vd == None
|
assert vd == None
|
||||||
|
|
||||||
ts = await rc.watch_dht_values(rec.key, [], 0, 0xFFFFFFFF)
|
# Make a watch on that subkey
|
||||||
|
ts = await rcWatch.watch_dht_values(rec.key, [], 0, 0xFFFFFFFF)
|
||||||
assert ts != 0
|
assert ts != 0
|
||||||
|
|
||||||
vd = await rc.set_dht_value(rec.key, 3, b"BLAH")
|
# Reopen without closing to change routing context and not lose watch
|
||||||
|
rec = await rcSet.open_dht_record(rec.key, rec.owner_key_pair())
|
||||||
|
|
||||||
|
# Now set the subkey and trigger an update
|
||||||
|
vd = await rcSet.set_dht_value(rec.key, 3, b"BLAH")
|
||||||
assert vd == None
|
assert vd == None
|
||||||
|
|
||||||
still_active = await rc.cancel_dht_watch(rec.key, [(0, 2)])
|
# Wait for the update
|
||||||
|
upd = await asyncio.wait_for(value_change_queue.get(), timeout=5)
|
||||||
|
|
||||||
|
# Verify the update
|
||||||
|
assert upd.detail.key == rec.key
|
||||||
|
assert upd.detail.count == 0xFFFFFFFE
|
||||||
|
assert upd.detail.subkeys == [(3,3)]
|
||||||
|
assert upd.detail.value.seq == 1
|
||||||
|
assert upd.detail.value.data == b"BLAH"
|
||||||
|
assert upd.detail.value.writer == rec.owner
|
||||||
|
|
||||||
|
# Reopen without closing to change routing context and not lose watch
|
||||||
|
rec = await rcWatch.open_dht_record(rec.key, rec.owner_key_pair())
|
||||||
|
|
||||||
|
# Cancel some subkeys we don't care about
|
||||||
|
still_active = await rcWatch.cancel_dht_watch(rec.key, [(0, 2)])
|
||||||
assert still_active == True
|
assert still_active == True
|
||||||
|
|
||||||
vd = await rc.set_dht_value(rec.key, 3, b"BLAH BLAH BLAH")
|
# Reopen without closing to change routing context and not lose watch
|
||||||
|
rec = await rcSet.open_dht_record(rec.key, rec.owner_key_pair())
|
||||||
|
|
||||||
|
# Change our subkey
|
||||||
|
vd = await rcSet.set_dht_value(rec.key, 3, b"BLAH BLAH BLAH")
|
||||||
assert vd == None
|
assert vd == None
|
||||||
|
|
||||||
still_active = await rc.cancel_dht_watch(rec.key, [(3, 9)])
|
# Wait for the update
|
||||||
|
upd = await asyncio.wait_for(value_change_queue.get(), timeout=5)
|
||||||
|
|
||||||
|
# Verify the update
|
||||||
|
assert upd.detail.key == rec.key
|
||||||
|
assert upd.detail.count == 0xFFFFFFFD
|
||||||
|
assert upd.detail.subkeys == [(3,3)]
|
||||||
|
assert upd.detail.value.seq == 2
|
||||||
|
assert upd.detail.value.data == b"BLAH BLAH BLAH"
|
||||||
|
assert upd.detail.value.writer == rec.owner
|
||||||
|
|
||||||
|
# Reopen without closing to change routing context and not lose watch
|
||||||
|
rec = await rcWatch.open_dht_record(rec.key, rec.owner_key_pair())
|
||||||
|
|
||||||
|
# Now cancel the update
|
||||||
|
still_active = await rcWatch.cancel_dht_watch(rec.key, [(3, 9)])
|
||||||
assert still_active == False
|
assert still_active == False
|
||||||
|
|
||||||
vd = await rc.set_dht_value(rec.key, 3, b"BLAH")
|
# Reopen without closing to change routing context and not lose watch
|
||||||
|
rec = await rcSet.open_dht_record(rec.key, rec.owner_key_pair())
|
||||||
|
|
||||||
|
# Set the value without a watch
|
||||||
|
vd = await rcSet.set_dht_value(rec.key, 3, b"BLAH")
|
||||||
assert vd == None
|
assert vd == None
|
||||||
|
|
||||||
await rc.close_dht_record(rec.key)
|
# Now we should NOT get an update
|
||||||
await rc.delete_dht_record(rec.key)
|
update = None
|
||||||
|
try:
|
||||||
|
update = await asyncio.wait_for(value_change_queue.get(), timeout=5)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
pass
|
||||||
|
assert update == None
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
await rcSet.close_dht_record(rec.key)
|
||||||
|
await rcSet.delete_dht_record(rec.key)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_inspect_dht_record(api_connection: veilid.VeilidAPI):
|
async def test_inspect_dht_record(api_connection: veilid.VeilidAPI):
|
||||||
rc = await api_connection.new_routing_context()
|
rc = await (await api_connection.new_routing_context()).with_sequencing(
|
||||||
|
veilid.Sequencing.ENSURE_ORDERED
|
||||||
|
)
|
||||||
async with rc:
|
async with rc:
|
||||||
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2))
|
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2))
|
||||||
|
|
||||||
|
@ -111,8 +111,12 @@ class _JsonVeilidAPI(VeilidAPI):
|
|||||||
try:
|
try:
|
||||||
self.reader = None
|
self.reader = None
|
||||||
assert self.writer is not None
|
assert self.writer is not None
|
||||||
self.writer.close()
|
try:
|
||||||
await self.writer.wait_closed()
|
self.writer.close()
|
||||||
|
await self.writer.wait_closed()
|
||||||
|
except:
|
||||||
|
# Already closed
|
||||||
|
pass
|
||||||
self.writer = None
|
self.writer = None
|
||||||
|
|
||||||
for reqid, reqfuture in self.in_flight_requests.items():
|
for reqid, reqfuture in self.in_flight_requests.items():
|
||||||
|
@ -356,11 +356,11 @@ class VeilidRouteChange:
|
|||||||
|
|
||||||
class VeilidValueChange:
|
class VeilidValueChange:
|
||||||
key: TypedKey
|
key: TypedKey
|
||||||
subkeys: list[ValueSubkey]
|
subkeys: list[tuple[ValueSubkey, ValueSubkey]]
|
||||||
count: int
|
count: int
|
||||||
value: ValueData
|
value: ValueData
|
||||||
|
|
||||||
def __init__(self, key: TypedKey, subkeys: list[ValueSubkey], count: int, value: ValueData):
|
def __init__(self, key: TypedKey, subkeys: list[tuple[ValueSubkey, ValueSubkey]], count: int, value: ValueData):
|
||||||
self.key = key
|
self.key = key
|
||||||
self.subkeys = subkeys
|
self.subkeys = subkeys
|
||||||
self.count = count
|
self.count = count
|
||||||
@ -371,7 +371,7 @@ class VeilidValueChange:
|
|||||||
"""JSON object hook"""
|
"""JSON object hook"""
|
||||||
return cls(
|
return cls(
|
||||||
TypedKey(j["key"]),
|
TypedKey(j["key"]),
|
||||||
[ValueSubkey(key) for key in j["subkeys"]],
|
[(p[0], p[1]) for p in j["subkeys"]],
|
||||||
j["count"],
|
j["count"],
|
||||||
ValueData.from_json(j["value"]),
|
ValueData.from_json(j["value"]),
|
||||||
)
|
)
|
||||||
|
@ -363,6 +363,9 @@ class DHTRecordDescriptor:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<{self.__class__.__name__}(key={self.key!r}, owner={self.owner!r}, owner_secret={self.owner_secret!r}, schema={self.schema!r})>"
|
return f"<{self.__class__.__name__}(key={self.key!r}, owner={self.owner!r}, owner_secret={self.owner_secret!r}, schema={self.schema!r})>"
|
||||||
|
|
||||||
|
def owner_key_pair(self) -> Optional[KeyPair]:
|
||||||
|
return KeyPair.from_parts(self.owner, self.owner_secret)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, j: dict) -> Self:
|
def from_json(cls, j: dict) -> Self:
|
||||||
return cls(
|
return cls(
|
||||||
|
Loading…
Reference in New Issue
Block a user