mirror of
https://github.com/lightningd/plugins.git
synced 2026-08-19 13:18:19 +02:00
269 lines
8.9 KiB
Python
269 lines
8.9 KiB
Python
import os
|
|
import shelve
|
|
import time
|
|
|
|
import pytest
|
|
from pyln.client import RpcError
|
|
from pyln.testing.fixtures import * # noqa: F401,F403
|
|
from pyln.testing.utils import only_one, wait_for
|
|
|
|
plugin_path = os.path.join(os.path.dirname(__file__), "datastore.py")
|
|
|
|
|
|
# Test taken from lightning/tests/test_misc.py
|
|
def test_datastore(node_factory):
|
|
l1 = node_factory.get_node(options={"plugin": plugin_path})
|
|
time.sleep(5)
|
|
|
|
# Starts empty
|
|
assert l1.rpc.listdatastore() == {"datastore": []} or l1.rpc.listdatastore() == {
|
|
"datastore": [{"key": ["askrene"]}]
|
|
}
|
|
assert l1.rpc.listdatastore("somekey") == {"datastore": []}
|
|
|
|
# Add entries.
|
|
somedata = b"somedata".hex()
|
|
somedata_expect = {
|
|
"key": ["somekey"],
|
|
"generation": 0,
|
|
"hex": somedata,
|
|
"string": "somedata",
|
|
}
|
|
assert l1.rpc.datastore(key="somekey", hex=somedata) == somedata_expect
|
|
|
|
assert somedata_expect in l1.rpc.listdatastore()["datastore"]
|
|
assert l1.rpc.listdatastore("somekey") == {"datastore": [somedata_expect]}
|
|
assert l1.rpc.listdatastore("otherkey") == {"datastore": []}
|
|
|
|
# Cannot add by default.
|
|
with pytest.raises(RpcError, match="already exists"):
|
|
l1.rpc.datastore(key="somekey", hex=somedata)
|
|
|
|
with pytest.raises(RpcError, match="already exists"):
|
|
l1.rpc.datastore(key="somekey", hex=somedata, mode="must-create")
|
|
|
|
# But can insist on replace.
|
|
l1.rpc.datastore(key="somekey", hex=somedata[:-4], mode="must-replace")
|
|
assert (
|
|
only_one(l1.rpc.listdatastore("somekey")["datastore"])["hex"] == somedata[:-4]
|
|
)
|
|
# And append works.
|
|
l1.rpc.datastore(key="somekey", hex=somedata[-4:-2], mode="must-append")
|
|
assert (
|
|
only_one(l1.rpc.listdatastore("somekey")["datastore"])["hex"] == somedata[:-2]
|
|
)
|
|
l1.rpc.datastore(key="somekey", hex=somedata[-2:], mode="create-or-append")
|
|
assert only_one(l1.rpc.listdatastore("somekey")["datastore"])["hex"] == somedata
|
|
|
|
# Generation will have increased due to three ops above.
|
|
somedata_expect["generation"] += 3
|
|
assert somedata_expect in l1.rpc.listdatastore()["datastore"]
|
|
|
|
# Can't replace or append non-existing records if we say not to
|
|
with pytest.raises(RpcError, match="does not exist"):
|
|
l1.rpc.datastore(key="otherkey", hex=somedata, mode="must-replace")
|
|
|
|
with pytest.raises(RpcError, match="does not exist"):
|
|
l1.rpc.datastore(key="otherkey", hex=somedata, mode="must-append")
|
|
|
|
otherdata = b"otherdata".hex()
|
|
otherdata_expect = {
|
|
"key": ["otherkey"],
|
|
"generation": 0,
|
|
"hex": otherdata,
|
|
"string": "otherdata",
|
|
}
|
|
assert (
|
|
l1.rpc.datastore(key="otherkey", string="otherdata", mode="create-or-append")
|
|
== otherdata_expect
|
|
)
|
|
|
|
assert l1.rpc.listdatastore("somekey") == {"datastore": [somedata_expect]}
|
|
assert l1.rpc.listdatastore("otherkey") == {"datastore": [otherdata_expect]}
|
|
assert l1.rpc.listdatastore("badkey") == {"datastore": []}
|
|
|
|
ds = l1.rpc.listdatastore()["datastore"]
|
|
# Order is undefined!
|
|
assert somedata_expect in ds
|
|
assert otherdata_expect in ds
|
|
|
|
assert l1.rpc.deldatastore("somekey") == somedata_expect
|
|
assert otherdata_expect in l1.rpc.listdatastore()["datastore"]
|
|
assert l1.rpc.listdatastore("somekey") == {"datastore": []}
|
|
assert l1.rpc.listdatastore("otherkey") == {"datastore": [otherdata_expect]}
|
|
assert l1.rpc.listdatastore("badkey") == {"datastore": []}
|
|
assert otherdata_expect in l1.rpc.listdatastore()["datastore"]
|
|
|
|
# if it's not a string, won't print
|
|
badstring_expect = {"key": ["badstring"], "generation": 0, "hex": "00"}
|
|
assert l1.rpc.datastore(key="badstring", hex="00") == badstring_expect
|
|
assert l1.rpc.listdatastore("badstring") == {"datastore": [badstring_expect]}
|
|
assert l1.rpc.deldatastore("badstring") == badstring_expect
|
|
|
|
# It's persistent
|
|
l1.restart()
|
|
|
|
assert otherdata_expect in l1.rpc.listdatastore()["datastore"]
|
|
|
|
# We can insist generation match on update.
|
|
with pytest.raises(RpcError, match="generation is different"):
|
|
l1.rpc.datastore(
|
|
key="otherkey",
|
|
hex="00",
|
|
mode="must-replace",
|
|
generation=otherdata_expect["generation"] + 1,
|
|
)
|
|
|
|
otherdata_expect["generation"] += 1
|
|
otherdata_expect["string"] += "a"
|
|
otherdata_expect["hex"] += "61"
|
|
assert (
|
|
l1.rpc.datastore(
|
|
key="otherkey",
|
|
string="otherdataa",
|
|
mode="must-replace",
|
|
generation=otherdata_expect["generation"] - 1,
|
|
)
|
|
== otherdata_expect
|
|
)
|
|
assert otherdata_expect in l1.rpc.listdatastore()["datastore"]
|
|
|
|
# We can insist generation match on delete.
|
|
with pytest.raises(RpcError, match="generation is different"):
|
|
l1.rpc.deldatastore(
|
|
key="otherkey", generation=otherdata_expect["generation"] + 1
|
|
)
|
|
|
|
assert (
|
|
l1.rpc.deldatastore(key="otherkey", generation=otherdata_expect["generation"])
|
|
== otherdata_expect
|
|
)
|
|
assert l1.rpc.listdatastore() == {"datastore": []} or l1.rpc.listdatastore() == {
|
|
"datastore": [{"key": ["askrene"]}]
|
|
}
|
|
|
|
|
|
def test_upgrade(node_factory):
|
|
l1 = node_factory.get_node()
|
|
|
|
datastore = shelve.open(
|
|
os.path.join(l1.daemon.lightning_dir, "regtest", "datastore.dat"), "c"
|
|
)
|
|
datastore["foo"] = b"foodata"
|
|
datastore["bar"] = b"bardata"
|
|
datastore.close()
|
|
|
|
# This "fails" because it unloads itself.
|
|
try:
|
|
l1.rpc.plugin_start(plugin_path)
|
|
except RpcError:
|
|
pass
|
|
|
|
# There's no upgrade if there's a real datastore.
|
|
if l1.daemon.is_in_log("there is a real datastore command"):
|
|
return
|
|
|
|
l1.daemon.wait_for_log("Upgrading store to have generation numbers")
|
|
wait_for(
|
|
lambda: not os.path.exists(
|
|
os.path.join(l1.daemon.lightning_dir, "regtest", "datastore.dat")
|
|
)
|
|
)
|
|
|
|
vals = l1.rpc.listdatastore()["datastore"]
|
|
assert vals == [
|
|
{"key": ["bar"], "generation": 0, "hex": b"bardata".hex(), "string": "bardata"},
|
|
{"key": ["foo"], "generation": 0, "hex": b"foodata".hex(), "string": "foodata"},
|
|
]
|
|
|
|
|
|
def test_datastore_keylist(node_factory):
|
|
l1 = node_factory.get_node(options={"plugin": plugin_path})
|
|
time.sleep(5)
|
|
|
|
# Starts empty
|
|
assert l1.rpc.listdatastore() == {"datastore": []} or l1.rpc.listdatastore() == {
|
|
"datastore": [{"key": ["askrene"]}]
|
|
}
|
|
assert l1.rpc.listdatastore(["a"]) == {"datastore": []}
|
|
assert l1.rpc.listdatastore(["a", "b"]) == {"datastore": []}
|
|
|
|
# Cannot add child to existing!
|
|
l1.rpc.datastore(key="a", string="aval")
|
|
with pytest.raises(RpcError, match=r"1206.*Parent key \[a\] exists"):
|
|
l1.rpc.datastore(key=["a", "b"], string="abval", mode="create-or-replace")
|
|
# Listing subkey gives DNE.
|
|
assert l1.rpc.listdatastore(["a", "b"]) == {"datastore": []}
|
|
l1.rpc.deldatastore(key=["a"])
|
|
|
|
# Create child key.
|
|
l1.rpc.datastore(key=["a", "b"], string="abval")
|
|
assert {"key": ["a"]} in l1.rpc.listdatastore()["datastore"]
|
|
assert l1.rpc.listdatastore(key=["a"]) == {
|
|
"datastore": [
|
|
{
|
|
"key": ["a", "b"],
|
|
"generation": 0,
|
|
"string": "abval",
|
|
"hex": b"abval".hex(),
|
|
}
|
|
]
|
|
}
|
|
|
|
# Cannot create key over that
|
|
with pytest.raises(RpcError, match="has children"):
|
|
l1.rpc.datastore(key="a", string="aval", mode="create-or-replace")
|
|
|
|
# Can create another key.
|
|
l1.rpc.datastore(key=["a", "b2"], string="ab2val")
|
|
assert {"key": ["a"]} in l1.rpc.listdatastore()["datastore"]
|
|
assert l1.rpc.listdatastore(key=["a"]) == {
|
|
"datastore": [
|
|
{
|
|
"key": ["a", "b"],
|
|
"string": "abval",
|
|
"generation": 0,
|
|
"hex": b"abval".hex(),
|
|
},
|
|
{
|
|
"key": ["a", "b2"],
|
|
"string": "ab2val",
|
|
"generation": 0,
|
|
"hex": b"ab2val".hex(),
|
|
},
|
|
]
|
|
}
|
|
|
|
# Can create subkey.
|
|
l1.rpc.datastore(key=["a", "b3", "c"], string="ab2val")
|
|
assert {"key": ["a"]} in l1.rpc.listdatastore()["datastore"]
|
|
assert l1.rpc.listdatastore(key=["a"]) == {
|
|
"datastore": [
|
|
{
|
|
"key": ["a", "b"],
|
|
"string": "abval",
|
|
"generation": 0,
|
|
"hex": b"abval".hex(),
|
|
},
|
|
{
|
|
"key": ["a", "b2"],
|
|
"string": "ab2val",
|
|
"generation": 0,
|
|
"hex": b"ab2val".hex(),
|
|
},
|
|
{"key": ["a", "b3"]},
|
|
]
|
|
}
|
|
|
|
# Can update subkey
|
|
l1.rpc.datastore(key=["a", "b3", "c"], string="2", mode="must-append")
|
|
assert l1.rpc.listdatastore(key=["a", "b3", "c"]) == {
|
|
"datastore": [
|
|
{
|
|
"key": ["a", "b3", "c"],
|
|
"string": "ab2val2",
|
|
"generation": 1,
|
|
"hex": b"ab2val2".hex(),
|
|
}
|
|
]
|
|
}
|