diff --git a/tests/plugins/rpc_command.py b/tests/plugins/rpc_command_1.py similarity index 58% rename from tests/plugins/rpc_command.py rename to tests/plugins/rpc_command_1.py index a993a8d6a..29724dabb 100755 --- a/tests/plugins/rpc_command.py +++ b/tests/plugins/rpc_command_1.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -This plugin is used to test the `rpc_command` hook. +This plugin is used to test the chained `rpc_command` hook. """ from pyln.client import Plugin @@ -12,15 +12,11 @@ def on_rpc_command(plugin, rpc_command, **kwargs): request = rpc_command if request["method"] == "invoice": # Replace part of this command - request["params"]["description"] = "A plugin modified this description" + request["params"]["description"] = "rpc_command_1 modified this description" return {"replace": request} elif request["method"] == "listfunds": # Return a custom result to the command - return {"return": {"result": ["Custom result"]}} - elif request["method"] == "sendpay": - # Don't allow this command to be executed - return {"return": {"error": {"code": -1, - "message": "You cannot do this"}}} + return {"return": {"result": ["Custom rpc_command_1 result"]}} elif request["method"] == "help": request["method"] = "autocleaninvoice" return {"replace": request} diff --git a/tests/plugins/rpc_command_2.py b/tests/plugins/rpc_command_2.py new file mode 100755 index 000000000..f167c1ce5 --- /dev/null +++ b/tests/plugins/rpc_command_2.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +""" +This plugin is used to test the chained `rpc_command` hook. +""" +from pyln.client import Plugin + +plugin = Plugin() + + +@plugin.hook("rpc_command") +def on_rpc_command(plugin, rpc_command, **kwargs): + request = rpc_command + if request["method"] == "invoice": + # Replace part of this command + request["params"]["description"] = "rpc_command_2 modified this description" + return {"replace": request} + elif request["method"] == "sendpay": + # Don't allow this command to be executed + return {"return": {"error": {"code": -1, + "message": "rpc_command_2 cannot do this"}}} + return {"result": "continue"} + + +plugin.run() diff --git a/tests/test_plugin.py b/tests/test_plugin.py index baec3e09c..9699de37c 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -16,6 +16,7 @@ import ast import json import os import pytest +import random import re import signal import sqlite3 @@ -1339,28 +1340,42 @@ def test_sendpay_notifications_nowaiter(node_factory): def test_rpc_command_hook(node_factory): - """Test the `sensitive_command` hook""" - plugin = os.path.join(os.getcwd(), "tests/plugins/rpc_command.py") + """Test the `rpc_command` hook chain""" + plugin = [ + os.path.join(os.getcwd(), "tests/plugins/rpc_command_1.py"), + os.path.join(os.getcwd(), "tests/plugins/rpc_command_2.py") + ] l1 = node_factory.get_node(options={"plugin": plugin}) - # Usage of "sendpay" has been restricted by the plugin - with pytest.raises(RpcError, match=r"You cannot do this"): + # rpc_command_2 plugin restricts using "sendpay" + with pytest.raises(RpcError, match=r"rpc_command_2 cannot do this"): l1.rpc.call("sendpay") - # The plugin replaces a call made for the "invoice" command + # Both plugins will replace calls made for the "invoice" command + # The first will win, for the second a warning should be logged invoice = l1.rpc.invoice(10**6, "test_side", "test_input") decoded = l1.rpc.decodepay(invoice["bolt11"]) - assert decoded["description"] == "A plugin modified this description" + assert decoded["description"] == "rpc_command_1 modified this description" + l1.daemon.wait_for_log("rpc_command hook 'invoice' already modified, ignoring.") - # The plugin sends a custom response to "listfunds" + # rpc_command_1 plugin sends a custom response to "listfunds" funds = l1.rpc.listfunds() - assert funds[0] == "Custom result" + assert funds[0] == "Custom rpc_command_1 result" # Test command redirection to a plugin l1.rpc.call('help', [0]) - # Test command which removes plugin itself! - l1.rpc.plugin_stop('rpc_command.py') + # Check the 'already modified' warning is not logged on just 'continue' + assert not l1.daemon.is_in_log("rpc_command hook 'listfunds' already modified, ignoring.") + + # Tests removing a chained hook in random order. + # Note: This will get flaky by design if theres a problem. + if bool(random.getrandbits(1)): + l1.rpc.plugin_stop('rpc_command_2.py') + l1.rpc.plugin_stop('rpc_command_1.py') + else: + l1.rpc.plugin_stop('rpc_command_1.py') + l1.rpc.plugin_stop('rpc_command_2.py') def test_libplugin(node_factory):