Skip to content

Commit d63381a

Browse files
committed
Add Python FFI tests for tiered storage
Add a Python in-memory KV store implementation for the UniFFI-backed builder APIs and extend the Python test suite with tiered-storage coverage. This adds a tier-store test that builds nodes with custom primary, backup, and ephemeral stores through the FFI surface and exercises a full channel lifecycle against them. The test verifies that: - primary-backed data is persisted to both the primary and backup stores - ephemeral-routed data is stored in the ephemeral store - backup contents converge to the primary store contents
1 parent d75e489 commit d63381a

File tree

2 files changed

+345
-93
lines changed

2 files changed

+345
-93
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import threading
2+
3+
from abc import ABC, abstractmethod
4+
from typing import List
5+
6+
from ldk_node import IoError
7+
8+
class AbstractKvStore(ABC):
9+
@abstractmethod
10+
async def read_async(self, primary_namespace: "str",secondary_namespace: "str",key: "str") -> "List[int]":
11+
pass
12+
13+
@abstractmethod
14+
async def write_async(self, primary_namespace: "str",secondary_namespace: "str",key: "str",buf: "List[int]") -> None:
15+
pass
16+
17+
@abstractmethod
18+
async def remove_async(self, primary_namespace: "str",secondary_namespace: "str",key: "str",lazy: "bool") -> None:
19+
pass
20+
21+
@abstractmethod
22+
async def list_async(self, primary_namespace: "str",secondary_namespace: "str") -> "List[str]":
23+
pass
24+
25+
@abstractmethod
26+
def read(self, primary_namespace: "str",secondary_namespace: "str",key: "str") -> "List[int]":
27+
pass
28+
29+
@abstractmethod
30+
def write(self, primary_namespace: "str",secondary_namespace: "str",key: "str",buf: "List[int]") -> None:
31+
pass
32+
33+
@abstractmethod
34+
def remove(self, primary_namespace: "str",secondary_namespace: "str",key: "str",lazy: "bool") -> None:
35+
pass
36+
37+
@abstractmethod
38+
def list(self, primary_namespace: "str",secondary_namespace: "str") -> "List[str]":
39+
pass
40+
41+
class TestKvStore(AbstractKvStore):
42+
def __init__(self, name: str):
43+
self.name = name
44+
# Storage structure: {(primary_ns, secondary_ns): {key: [bytes]}}
45+
self.storage = {}
46+
self._lock = threading.Lock()
47+
48+
def dump(self):
49+
print(f"\n[{self.name}] Store contents:")
50+
for (primary_ns, secondary_ns), keys_dict in self.storage.items():
51+
print(f" Namespace: ({primary_ns!r}, {secondary_ns!r})")
52+
for key, data in keys_dict.items():
53+
print(f" Key: {key!r} -> {len(data)} bytes")
54+
# Optionally show first few bytes
55+
preview = data[:20] if len(data) > 20 else data
56+
print(f" Data preview: {preview}...")
57+
58+
def read(self, primary_namespace: str, secondary_namespace: str, key: str) -> List[int]:
59+
with self._lock:
60+
namespace_key = (primary_namespace, secondary_namespace)
61+
62+
if namespace_key not in self.storage:
63+
raise IoError.NotFound()
64+
65+
if key not in self.storage[namespace_key]:
66+
raise IoError.NotFound()
67+
68+
return list(self.storage[namespace_key][key])
69+
70+
def write(self, primary_namespace: str, secondary_namespace: str, key: str, buf: List[int]) -> None:
71+
with self._lock:
72+
namespace_key = (primary_namespace, secondary_namespace)
73+
if namespace_key not in self.storage:
74+
self.storage[namespace_key] = {}
75+
76+
self.storage[namespace_key][key] = list(buf)
77+
78+
def remove(self, primary_namespace: str, secondary_namespace: str, key: str, lazy: bool) -> None:
79+
with self._lock:
80+
namespace_key = (primary_namespace, secondary_namespace)
81+
if namespace_key not in self.storage:
82+
raise IoError.NotFound()
83+
84+
if key not in self.storage[namespace_key]:
85+
raise IoError.NotFound()
86+
87+
del self.storage[namespace_key][key]
88+
89+
if not self.storage[namespace_key]:
90+
del self.storage[namespace_key]
91+
92+
def list(self, primary_namespace: str, secondary_namespace: str) -> List[str]:
93+
with self._lock:
94+
namespace_key = (primary_namespace, secondary_namespace)
95+
if namespace_key in self.storage:
96+
return sorted(self.storage[namespace_key].keys())
97+
return []
98+
99+
async def read_async(self, primary_namespace: str, secondary_namespace: str, key: str) -> List[int]:
100+
return self.read(primary_namespace, secondary_namespace, key)
101+
102+
async def write_async(self, primary_namespace: str, secondary_namespace: str, key: str, buf: List[int]) -> None:
103+
self.write(primary_namespace, secondary_namespace, key, buf)
104+
105+
async def remove_async(self, primary_namespace: str, secondary_namespace: str, key: str, lazy: bool) -> None:
106+
self.remove(primary_namespace, secondary_namespace, key, lazy)
107+
108+
async def list_async(self, primary_namespace: str, secondary_namespace: str) -> List[str]:
109+
return self.list(primary_namespace, secondary_namespace)
110+

0 commit comments

Comments
 (0)