mirror of
https://github.com/progval/irctest.git
synced 2025-04-07 07:49:52 +00:00
Adapt metadata_2 to stdreplies, and run it on Unreal
Passes on 3968354247
This commit is contained in:
@ -11,6 +11,7 @@ from irctest.basecontrollers import BaseServerController, DirectoryBasedControll
|
|||||||
|
|
||||||
TEMPLATE_CONFIG = """
|
TEMPLATE_CONFIG = """
|
||||||
include "modules.default.conf";
|
include "modules.default.conf";
|
||||||
|
loadmodule "third/metadata2";
|
||||||
include "operclass.default.conf";
|
include "operclass.default.conf";
|
||||||
{extras}
|
{extras}
|
||||||
include "help/help.conf";
|
include "help/help.conf";
|
||||||
|
@ -1,41 +1,23 @@
|
|||||||
"""
|
"""
|
||||||
`Deprecated IRCv3 Metadata <https://ircv3.net/specs/core/metadata-3.2>`_
|
`IRCv3 Metadata 2 <https://github.com/ircv3/ircv3-specifications/pull/501>`_
|
||||||
|
(not to be confused with the `deprecated IRCv3 Metadata
|
||||||
|
<https://ircv3.net/specs/core/metadata-3.2>`_)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from irctest import cases
|
from irctest import cases
|
||||||
|
from irctest.patma import ANYSTR, StrRe
|
||||||
|
|
||||||
|
|
||||||
class MetadataTestCase(cases.BaseServerTestCase):
|
class MetadataTestCase(cases.BaseServerTestCase):
|
||||||
valid_metadata_keys = {"valid_key1", "valid_key2"}
|
valid_metadata_keys = {"valid_key1", "valid_key2"}
|
||||||
invalid_metadata_keys = {"invalid_key1", "invalid_key2"}
|
invalid_metadata_keys = {"invalid_key1", "invalid_key2"}
|
||||||
|
|
||||||
@cases.mark_specifications("IRCv3", deprecated=True)
|
@cases.mark_specifications("IRCv3")
|
||||||
def testInIsupport(self):
|
|
||||||
"""“If METADATA is supported, it MUST be specified in RPL_ISUPPORT
|
|
||||||
using the METADATA key.”
|
|
||||||
-- <http://ircv3.net/specs/core/metadata-3.2.html>
|
|
||||||
"""
|
|
||||||
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)
|
|
||||||
def testGetOneUnsetValid(self):
|
def testGetOneUnsetValid(self):
|
||||||
"""<http://ircv3.net/specs/core/metadata-3.2.html#metadata-get>"""
|
"""<http://ircv3.net/specs/core/metadata-3.2.html#metadata-get>"""
|
||||||
self.connectClient("foo")
|
self.connectClient(
|
||||||
|
"foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True
|
||||||
|
)
|
||||||
self.sendLine(1, "METADATA * GET valid_key1")
|
self.sendLine(1, "METADATA * GET valid_key1")
|
||||||
m = self.getMessage(1)
|
m = self.getMessage(1)
|
||||||
self.assertMessageMatch(
|
self.assertMessageMatch(
|
||||||
@ -45,25 +27,26 @@ class MetadataTestCase(cases.BaseServerTestCase):
|
|||||||
"request to an unset valid METADATA key.",
|
"request to an unset valid METADATA key.",
|
||||||
)
|
)
|
||||||
|
|
||||||
@cases.mark_specifications("IRCv3", deprecated=True)
|
@cases.mark_specifications("IRCv3")
|
||||||
def testGetTwoUnsetValid(self):
|
def testGetTwoUnsetValid(self):
|
||||||
"""“Multiple keys may be given. The response will be either RPL_KEYVALUE,
|
"""“Multiple keys may be given. The response will be either RPL_KEYVALUE,
|
||||||
ERR_KEYINVALID or ERR_NOMATCHINGKEY for every key in order.”
|
ERR_KEYINVALID or ERR_NOMATCHINGKEY for every key in order.”
|
||||||
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-get>
|
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-get>
|
||||||
"""
|
"""
|
||||||
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")
|
self.sendLine(1, "METADATA * GET valid_key1 valid_key2")
|
||||||
m = self.getMessage(1)
|
m = self.getMessage(1)
|
||||||
self.assertMessageMatch(
|
self.assertMessageMatch(
|
||||||
m,
|
m,
|
||||||
command="766", # ERR_NOMATCHINGKEY
|
command="766", # RPL_NOMATCHINGKEY
|
||||||
fail_msg="Did not reply with 766 (ERR_NOMATCHINGKEY) to a "
|
fail_msg="Did not reply with 766 (RPL_NOMATCHINGKEY) to a "
|
||||||
"request to two unset valid METADATA key: {msg}",
|
"request to two unset valid METADATA key: {msg}",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertMessageMatch(
|
||||||
m.params[1],
|
|
||||||
"valid_key1",
|
|
||||||
m,
|
m,
|
||||||
|
params=["foo", "foo", "valid_key1", ANYSTR],
|
||||||
fail_msg="Response to “METADATA * GET valid_key1 valid_key2” "
|
fail_msg="Response to “METADATA * GET valid_key1 valid_key2” "
|
||||||
"did not respond to valid_key1 first: {msg}",
|
"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 "
|
fail_msg="Did not reply with two 766 (ERR_NOMATCHINGKEY) to a "
|
||||||
"request to two unset valid METADATA key: {msg}",
|
"request to two unset valid METADATA key: {msg}",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertMessageMatch(
|
||||||
m.params[1],
|
|
||||||
"valid_key2",
|
|
||||||
m,
|
m,
|
||||||
|
params=["foo", "foo", "valid_key2", ANYSTR],
|
||||||
fail_msg="Response to “METADATA * GET valid_key1 valid_key2” "
|
fail_msg="Response to “METADATA * GET valid_key1 valid_key2” "
|
||||||
"did not respond to valid_key2 as second response: {msg}",
|
"did not respond to valid_key2 as second response: {msg}",
|
||||||
)
|
)
|
||||||
|
|
||||||
@cases.mark_specifications("IRCv3", deprecated=True)
|
@cases.mark_specifications("IRCv3")
|
||||||
def testListNoSet(self):
|
def testListNoSet(self):
|
||||||
"""“This subcommand MUST list all currently-set metadata keys along
|
"""“This subcommand MUST list all currently-set metadata keys along
|
||||||
with their values. The response will be zero or more RPL_KEYVALUE
|
with their values. The response will be zero or more RPL_KEYVALUE
|
||||||
events, following by RPL_METADATAEND event.”
|
events, following by RPL_METADATAEND event.”
|
||||||
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-list>
|
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-list>
|
||||||
"""
|
"""
|
||||||
self.connectClient("foo")
|
self.connectClient(
|
||||||
|
"foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True
|
||||||
|
)
|
||||||
self.sendLine(1, "METADATA * LIST")
|
self.sendLine(1, "METADATA * LIST")
|
||||||
m = self.getMessage(1)
|
m = self.getMessage(1)
|
||||||
self.assertMessageMatch(
|
self.assertMessageMatch(
|
||||||
m,
|
m,
|
||||||
command="762", # RPL_METADATAEND
|
command="762", # RPL_METADATAEND
|
||||||
fail_msg="Response to “METADATA * LIST” was not "
|
params=["foo", ANYSTR],
|
||||||
"762 (RPL_METADATAEND) but: {msg}",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@cases.mark_specifications("IRCv3", deprecated=True)
|
@cases.mark_specifications("IRCv3")
|
||||||
def testListInvalidTarget(self):
|
def testListInvalidTarget(self):
|
||||||
"""“In case of invalid target RPL_METADATAEND MUST NOT be sent.”
|
"""“In case of invalid target RPL_METADATAEND MUST NOT be sent.”
|
||||||
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-list>
|
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-list>
|
||||||
"""
|
"""
|
||||||
self.connectClient("foo")
|
self.connectClient(
|
||||||
|
"foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True
|
||||||
|
)
|
||||||
self.sendLine(1, "METADATA foobar LIST")
|
self.sendLine(1, "METADATA foobar LIST")
|
||||||
m = self.getMessage(1)
|
m = self.getMessage(1)
|
||||||
self.assertMessageMatch(
|
self.assertMessageMatch(
|
||||||
m,
|
m,
|
||||||
command="765", # ERR_TARGETINVALID
|
command="FAIL",
|
||||||
|
params=["METADATA", "INVALID_TARGET", "foobar", ANYSTR],
|
||||||
fail_msg="Response to “METADATA <invalid target> LIST” was "
|
fail_msg="Response to “METADATA <invalid target> LIST” was "
|
||||||
"not 765 (ERR_TARGETINVALID) but: {msg}",
|
"not 765 (ERR_TARGETINVALID) but: {msg}",
|
||||||
)
|
)
|
||||||
@ -117,115 +103,81 @@ class MetadataTestCase(cases.BaseServerTestCase):
|
|||||||
self.assertNotIn(
|
self.assertNotIn(
|
||||||
"762",
|
"762",
|
||||||
commands,
|
commands,
|
||||||
fail_msg="Sent “METADATA <invalid target> LIST”, got 765 "
|
fail_msg="Sent “METADATA <invalid target> LIST”, got FAIL INVALID_TARGET, "
|
||||||
"(ERR_TARGETINVALID), and then 762 (RPL_METADATAEND)",
|
"and then 762 (RPL_METADATAEND)",
|
||||||
)
|
)
|
||||||
|
|
||||||
def assertSetValue(self, target, key, value, displayable_value=None):
|
def assertSetValue(self, target, key, value):
|
||||||
if displayable_value is None:
|
|
||||||
displayable_value = value
|
|
||||||
self.sendLine(1, "METADATA {} SET {} :{}".format(target, key, value))
|
self.sendLine(1, "METADATA {} SET {} :{}".format(target, key, value))
|
||||||
m = self.getMessage(1)
|
|
||||||
|
if target == "*":
|
||||||
|
target = StrRe(r"(\*|foo)")
|
||||||
|
|
||||||
self.assertMessageMatch(
|
self.assertMessageMatch(
|
||||||
m,
|
self.getMessage(1),
|
||||||
command="761", # RPL_KEYVALUE
|
command="761", # RPL_KEYVALUE
|
||||||
fail_msg="Did not reply with 761 (RPL_KEYVALUE) to a valid "
|
params=["foo", target, key, ANYSTR, value],
|
||||||
"“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.",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def assertGetValue(self, target, key, value, displayable_value=None):
|
def assertGetValue(self, target, key, value):
|
||||||
self.sendLine(1, "METADATA * GET {}".format(key))
|
self.sendLine(1, "METADATA {} GET {}".format(target, key))
|
||||||
m = self.getMessage(1)
|
|
||||||
|
if target == "*":
|
||||||
|
target = StrRe(r"(\*|foo)")
|
||||||
|
|
||||||
self.assertMessageMatch(
|
self.assertMessageMatch(
|
||||||
m,
|
self.getMessage(1),
|
||||||
command="761", # RPL_KEYVALUE
|
command="761", # RPL_KEYVALUE
|
||||||
fail_msg="Did not reply with 761 (RPL_KEYVALUE) to a valid "
|
params=["foo", target, key, ANYSTR, value],
|
||||||
"“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),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def assertSetGetValue(self, target, key, value, displayable_value=None):
|
def assertSetGetValue(self, target, key, value):
|
||||||
self.assertSetValue(target, key, value, displayable_value)
|
self.assertSetValue(target, key, value)
|
||||||
self.assertGetValue(target, key, value, displayable_value)
|
self.assertGetValue(target, key, value)
|
||||||
|
|
||||||
@cases.mark_specifications("IRCv3", deprecated=True)
|
@cases.mark_specifications("IRCv3")
|
||||||
def testSetGetValid(self):
|
def testSetGetValid(self):
|
||||||
"""<http://ircv3.net/specs/core/metadata-3.2.html>"""
|
"""<http://ircv3.net/specs/core/metadata-3.2.html>"""
|
||||||
self.connectClient("foo")
|
self.connectClient(
|
||||||
|
"foo", capabilities=["draft/metadata-2", "batch"], skip_if_cap_nak=True
|
||||||
|
)
|
||||||
self.assertSetGetValue("*", "valid_key1", "myvalue")
|
self.assertSetGetValue("*", "valid_key1", "myvalue")
|
||||||
|
|
||||||
@cases.mark_specifications("IRCv3", deprecated=True)
|
@cases.mark_specifications("IRCv3")
|
||||||
def testSetGetZeroCharInValue(self):
|
|
||||||
"""“Values are unrestricted, except that they MUST be UTF-8.”
|
|
||||||
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-restrictions>
|
|
||||||
"""
|
|
||||||
self.connectClient("foo")
|
|
||||||
self.assertSetGetValue("*", "valid_key1", "zero->\0<-zero", "zero->\\0<-zero")
|
|
||||||
|
|
||||||
@cases.mark_specifications("IRCv3", deprecated=True)
|
|
||||||
def testSetGetHeartInValue(self):
|
def testSetGetHeartInValue(self):
|
||||||
"""“Values are unrestricted, except that they MUST be UTF-8.”
|
"""“Values are unrestricted, except that they MUST be UTF-8.”
|
||||||
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-restrictions>
|
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-restrictions>
|
||||||
"""
|
"""
|
||||||
heart = b"\xf0\x9f\x92\x9c".decode()
|
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(
|
self.assertSetGetValue(
|
||||||
"*",
|
"*",
|
||||||
"valid_key1",
|
"valid_key1",
|
||||||
"->{}<-".format(heart),
|
"->{}<-".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):
|
def testSetInvalidUtf8(self):
|
||||||
"""“Values are unrestricted, except that they MUST be UTF-8.”
|
"""“Values are unrestricted, except that they MUST be UTF-8.”
|
||||||
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-restrictions>
|
-- <http://ircv3.net/specs/core/metadata-3.2.html#metadata-restrictions>
|
||||||
"""
|
"""
|
||||||
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
|
# Sending directly because it is not valid UTF-8 so Python would
|
||||||
# not like it
|
# not like it
|
||||||
self.clients[1].conn.sendall(
|
self.clients[1].conn.sendall(
|
||||||
b"METADATA * SET valid_key1 " b":invalid UTF-8 ->\xc3<-\r\n"
|
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(
|
self.assertNotIn(
|
||||||
"761",
|
"761",
|
||||||
commands, # RPL_KEYVALUE
|
commands, # RPL_KEYVALUE
|
||||||
|
@ -27,6 +27,7 @@ markers =
|
|||||||
extended-monitor
|
extended-monitor
|
||||||
labeled-response
|
labeled-response
|
||||||
message-tags
|
message-tags
|
||||||
|
metadata-2
|
||||||
draft/multiline
|
draft/multiline
|
||||||
multi-prefix
|
multi-prefix
|
||||||
server-time
|
server-time
|
||||||
|
Reference in New Issue
Block a user