From e17ab347f403df73bfcf169fc053f0f259a724a2 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 15 Apr 2023 14:44:58 +0200 Subject: [PATCH] Adapt metadata_2 to stdreplies, and run it on Unreal Passes on https://github.com/progval/unrealircd-contrib/commit/3968354247d07e341c1ede6e7f3d29688ed702c7 --- irctest/controllers/unrealircd.py | 1 + irctest/server_tests/metadata_2.py | 184 +++++++++++------------------ pytest.ini | 1 + 3 files changed, 70 insertions(+), 116 deletions(-) diff --git a/irctest/controllers/unrealircd.py b/irctest/controllers/unrealircd.py index 8d7e643..1f29cde 100644 --- a/irctest/controllers/unrealircd.py +++ b/irctest/controllers/unrealircd.py @@ -11,6 +11,7 @@ from irctest.basecontrollers import BaseServerController, DirectoryBasedControll TEMPLATE_CONFIG = """ include "modules.default.conf"; +loadmodule "third/metadata2"; include "operclass.default.conf"; {extras} include "help/help.conf"; diff --git a/irctest/server_tests/metadata_2.py b/irctest/server_tests/metadata_2.py index abfde8b..d5844c0 100644 --- a/irctest/server_tests/metadata_2.py +++ b/irctest/server_tests/metadata_2.py @@ -1,41 +1,23 @@ """ -`Deprecated IRCv3 Metadata `_ +`IRCv3 Metadata 2 `_ +(not to be confused with the `deprecated IRCv3 Metadata +`_) """ from irctest import cases +from irctest.patma import ANYSTR, StrRe class MetadataTestCase(cases.BaseServerTestCase): valid_metadata_keys = {"valid_key1", "valid_key2"} invalid_metadata_keys = {"invalid_key1", "invalid_key2"} - @cases.mark_specifications("IRCv3", deprecated=True) - def testInIsupport(self): - """“If METADATA is supported, it MUST be specified in RPL_ISUPPORT - using the METADATA key.” - -- - """ - self.addClient() - self.sendLine(1, "CAP LS 302") - self.getCapLs(1) - self.sendLine(1, "USER foo foo foo :foo") - self.sendLine(1, "NICK foo") - self.sendLine(1, "CAP END") - self.skipToWelcome(1) - m = self.getMessage(1) - while m.command != "005": # RPL_ISUPPORT - m = self.getMessage(1) - self.assertIn( - "METADATA", - {x.split("=")[0] for x in m.params[1:-1]}, - fail_msg="{item} missing from RPL_ISUPPORT", - ) - self.getMessages(1) - - @cases.mark_specifications("IRCv3", deprecated=True) + @cases.mark_specifications("IRCv3") def testGetOneUnsetValid(self): """""" - self.connectClient("foo") + self.connectClient( + "foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True + ) self.sendLine(1, "METADATA * GET valid_key1") m = self.getMessage(1) self.assertMessageMatch( @@ -45,25 +27,26 @@ class MetadataTestCase(cases.BaseServerTestCase): "request to an unset valid METADATA key.", ) - @cases.mark_specifications("IRCv3", deprecated=True) + @cases.mark_specifications("IRCv3") def testGetTwoUnsetValid(self): """“Multiple keys may be given. The response will be either RPL_KEYVALUE, ERR_KEYINVALID or ERR_NOMATCHINGKEY for every key in order.” -- """ - self.connectClient("foo") + self.connectClient( + "foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True + ) self.sendLine(1, "METADATA * GET valid_key1 valid_key2") m = self.getMessage(1) self.assertMessageMatch( m, - command="766", # ERR_NOMATCHINGKEY - fail_msg="Did not reply with 766 (ERR_NOMATCHINGKEY) to a " + command="766", # RPL_NOMATCHINGKEY + fail_msg="Did not reply with 766 (RPL_NOMATCHINGKEY) to a " "request to two unset valid METADATA key: {msg}", ) - self.assertEqual( - m.params[1], - "valid_key1", + self.assertMessageMatch( m, + params=["foo", "foo", "valid_key1", ANYSTR], fail_msg="Response to “METADATA * GET valid_key1 valid_key2” " "did not respond to valid_key1 first: {msg}", ) @@ -74,42 +57,45 @@ class MetadataTestCase(cases.BaseServerTestCase): fail_msg="Did not reply with two 766 (ERR_NOMATCHINGKEY) to a " "request to two unset valid METADATA key: {msg}", ) - self.assertEqual( - m.params[1], - "valid_key2", + self.assertMessageMatch( m, + params=["foo", "foo", "valid_key2", ANYSTR], fail_msg="Response to “METADATA * GET valid_key1 valid_key2” " "did not respond to valid_key2 as second response: {msg}", ) - @cases.mark_specifications("IRCv3", deprecated=True) + @cases.mark_specifications("IRCv3") def testListNoSet(self): """“This subcommand MUST list all currently-set metadata keys along with their values. The response will be zero or more RPL_KEYVALUE events, following by RPL_METADATAEND event.” -- """ - self.connectClient("foo") + self.connectClient( + "foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True + ) self.sendLine(1, "METADATA * LIST") m = self.getMessage(1) self.assertMessageMatch( m, command="762", # RPL_METADATAEND - fail_msg="Response to “METADATA * LIST” was not " - "762 (RPL_METADATAEND) but: {msg}", + params=["foo", ANYSTR], ) - @cases.mark_specifications("IRCv3", deprecated=True) + @cases.mark_specifications("IRCv3") def testListInvalidTarget(self): """“In case of invalid target RPL_METADATAEND MUST NOT be sent.” -- """ - self.connectClient("foo") + self.connectClient( + "foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True + ) self.sendLine(1, "METADATA foobar LIST") m = self.getMessage(1) self.assertMessageMatch( m, - command="765", # ERR_TARGETINVALID + command="FAIL", + params=["METADATA", "INVALID_TARGET", "foobar", ANYSTR], fail_msg="Response to “METADATA LIST” was " "not 765 (ERR_TARGETINVALID) but: {msg}", ) @@ -117,115 +103,81 @@ class MetadataTestCase(cases.BaseServerTestCase): self.assertNotIn( "762", commands, - fail_msg="Sent “METADATA LIST”, got 765 " - "(ERR_TARGETINVALID), and then 762 (RPL_METADATAEND)", + fail_msg="Sent “METADATA LIST”, got FAIL INVALID_TARGET, " + "and then 762 (RPL_METADATAEND)", ) - def assertSetValue(self, target, key, value, displayable_value=None): - if displayable_value is None: - displayable_value = value + def assertSetValue(self, target, key, value): self.sendLine(1, "METADATA {} SET {} :{}".format(target, key, value)) - m = self.getMessage(1) + + if target == "*": + target = StrRe(r"(\*|foo)") + self.assertMessageMatch( - m, + self.getMessage(1), command="761", # RPL_KEYVALUE - fail_msg="Did not reply with 761 (RPL_KEYVALUE) to a valid " - "“METADATA * SET {} :{}”: {msg}", - extra_format=(key, displayable_value), - ) - self.assertEqual( - m.params[1], - "valid_key1", - m, - fail_msg="Second param of 761 after setting “{expects}” to " - "“{}” is not “{expects}”: {msg}.", - extra_format=(displayable_value,), - ) - self.assertEqual( - m.params[3], - value, - m, - fail_msg="Fourth param of 761 after setting “{0}” to " - "“{1}” is not “{1}”: {msg}.", - extra_format=(key, displayable_value), - ) - m = self.getMessage(1) - self.assertMessageMatch( - m, - command="762", # RPL_METADATAEND - fail_msg="Did not send RPL_METADATAEND after setting " - "a valid METADATA key.", + params=["foo", target, key, ANYSTR, value], ) - def assertGetValue(self, target, key, value, displayable_value=None): - self.sendLine(1, "METADATA * GET {}".format(key)) - m = self.getMessage(1) + def assertGetValue(self, target, key, value): + self.sendLine(1, "METADATA {} GET {}".format(target, key)) + + if target == "*": + target = StrRe(r"(\*|foo)") + self.assertMessageMatch( - m, + self.getMessage(1), command="761", # RPL_KEYVALUE - fail_msg="Did not reply with 761 (RPL_KEYVALUE) to a valid " - "“METADATA * GET” when the key is set is set: {msg}", - ) - self.assertEqual( - m.params[1], - key, - m, - fail_msg="Second param of 761 after getting “{expects}” " - "(which is set) is not “{expects}”: {msg}.", - ) - self.assertEqual( - m.params[3], - value, - m, - fail_msg="Fourth param of 761 after getting “{0}” " - "(which is set to “{1}”) is not ”{1}”: {msg}.", - extra_format=(key, displayable_value), + params=["foo", target, key, ANYSTR, value], ) - def assertSetGetValue(self, target, key, value, displayable_value=None): - self.assertSetValue(target, key, value, displayable_value) - self.assertGetValue(target, key, value, displayable_value) + def assertSetGetValue(self, target, key, value): + self.assertSetValue(target, key, value) + self.assertGetValue(target, key, value) - @cases.mark_specifications("IRCv3", deprecated=True) + @cases.mark_specifications("IRCv3") def testSetGetValid(self): """""" - self.connectClient("foo") + self.connectClient( + "foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True + ) self.assertSetGetValue("*", "valid_key1", "myvalue") - @cases.mark_specifications("IRCv3", deprecated=True) - def testSetGetZeroCharInValue(self): - """“Values are unrestricted, except that they MUST be UTF-8.” - -- - """ - self.connectClient("foo") - self.assertSetGetValue("*", "valid_key1", "zero->\0<-zero", "zero->\\0<-zero") - - @cases.mark_specifications("IRCv3", deprecated=True) + @cases.mark_specifications("IRCv3") def testSetGetHeartInValue(self): """“Values are unrestricted, except that they MUST be UTF-8.” -- """ heart = b"\xf0\x9f\x92\x9c".decode() - self.connectClient("foo") + self.connectClient( + "foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True + ) self.assertSetGetValue( "*", "valid_key1", "->{}<-".format(heart), - "zero->{}<-zero".format(heart.encode()), ) - @cases.mark_specifications("IRCv3", deprecated=True) + @cases.xfailIfSoftware( + ["UnrealIRCd"], "UnrealIRCd does not validate UTF-8 in metadata values" + ) + @cases.mark_specifications("IRCv3") def testSetInvalidUtf8(self): """“Values are unrestricted, except that they MUST be UTF-8.” -- """ - self.connectClient("foo") + self.connectClient( + "foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True + ) # Sending directly because it is not valid UTF-8 so Python would # not like it self.clients[1].conn.sendall( b"METADATA * SET valid_key1 " b":invalid UTF-8 ->\xc3<-\r\n" ) - commands = {m.command for m in self.getMessages(1)} + try: + commands = {m.command for m in self.getMessages(1)} + except UnicodeDecodeError: + assert False, "Server sent invalid UTF-8" self.assertNotIn( "761", commands, # RPL_KEYVALUE diff --git a/pytest.ini b/pytest.ini index 375f2bb..6cef85d 100644 --- a/pytest.ini +++ b/pytest.ini @@ -27,6 +27,7 @@ markers = extended-monitor labeled-response message-tags + metadata-2 draft/multiline multi-prefix server-time