Compare commits

...

24 Commits

Author SHA1 Message Date
Val Lorentz 2283c296aa
Merge 1d8d78e62a into 2ae612c68f 2024-04-14 19:28:32 +02:00
Val Lorentz 2ae612c68f
Makefile: Add selectors in preparation for Sable adding message-tags support (#264)
Some tests Sable would fail are currently disabled only because Sable does not
support message-tags; but it probably will in the near future.
2024-04-13 14:41:45 +02:00
Val Lorentz d908699674
chathistory: Skip assertions based on MSGREFTYPES (#263)
This will be useful to test Sable, which does not support CHATHISTORY
with msgid= yet
2024-04-13 14:41:13 +02:00
Sadie Powell 61ae4bcf9e
Relink the modules directory as well as the lib directory. (#260) 2024-04-04 17:47:48 +02:00
Sadie Powell 0c5c91368a
Pass --nopid to Anope. (#259) 2024-03-21 21:04:13 +01:00
Shivaram Lingamneni c0e6ca4dde
add a test for WHOIS on nonexistent users (#258)
* add a test for WHOIS on nonexistent users

* skip test in Sable for now
2024-03-19 10:30:44 -04:00
Valentin Lorentz e6d54db9ce Fix chkNS on recent hybrid
Since e66f61f8a0
(which is itself a fix for 79c4eb8d75),
Hybrid sends this numeric right after the MOTD
2024-03-17 10:12:55 +01:00
Val Lorentz 03b6fbbfc2
Fix support for latest Anope 2.1 (#257)
enc_sha256 cannot be loaded since 6e0f0b8896
2024-03-10 11:20:57 +01:00
Shivaram Lingamneni ee6c56d84b
basic 005 parameter validation test (#255)
* basic 005 parameter validation test

The overall order of the registration burst is covered by
ConnectionRegistrationTestCase.testConnectionRegistration and doesn't need
to be checked here.

* Update irctest/server_tests/isupport.py

Co-authored-by: Val Lorentz <progval+github@progval.net>

---------

Co-authored-by: Val Lorentz <progval+github@progval.net>
2024-02-12 23:29:23 -05:00
Shivaram Lingamneni 85b519d93a
ci: upgrade actions/setup-go (#254) 2024-02-11 19:48:38 +01:00
Valentin Lorentz 56e0565512 Update Go 2024-02-11 19:29:58 +01:00
Shivaram Lingamneni df2880e379
add an incorrect password test for PLAIN (#253)
* add an incorrect password test for PLAIN

* derace test (hopefully)
2024-02-08 00:45:11 -05:00
Val Lorentz 61a6f047d2
Add support for '*' in place of server name in RPL_WHOREPLY/RPL_WHOSPCRPL (#252)
Sable users are no longer associated with a server, so it now returns
a blank where their server name used to be:
93ab9afa5c
2024-02-07 19:35:02 +01:00
Val Lorentz d75e3fae34
Fix support of Anope 2.1.2 (#251)
It is going to change module names:
7ac1fe5847
2024-01-27 10:32:35 +01:00
Sadie Powell 0ebfbdf6ab
Update the Anope config for the new password length fields. (#250) 2024-01-04 23:18:19 +01:00
Sadie Powell 0f6a485d7d
Fix Anope 2.1 not using the right protocol modules. (#249) 2024-01-04 22:13:16 +01:00
Sadie Powell dfd429014a
Update Anope. (#248) 2024-01-04 20:59:57 +01:00
Val Lorentz d9ad638791
Add regression test for Insp's labeled nick bug (#242)
* Add regression test for Insp's labeled nick bug

* Exclude test from irc2 and ircu2 as they error on CAP REQ
2024-01-04 20:46:50 +01:00
Sadie Powell 246a259111
Update InspIRCd. (#247) 2024-01-04 20:18:28 +01:00
Mitchell Riley 18d04e8f80
Prevent 433 response for Nonutf8{User/Real}name (#244)
Sending `NICK foo` after connectClient() causes an ERR_NICKNAMEINUSE
response instead of the expected RPL_WELCOME.
2023-12-31 19:47:18 +01:00
Shivaram Lingamneni 6425e707ac
fix race condition in #245 (#246) 2023-12-24 07:11:29 +01:00
Shivaram Lingamneni 032d0e32de
update ergo unicode tests (#245)
* `casemapping: ascii` is now default
* test that non-ascii nicks are rejected by default
* test that non-ascii nicks are accepted under `casemapping: precis`
2023-12-21 09:52:33 +01:00
Shivaram Lingamneni 62a039498b
enhance case change test (#243)
* enhance case change test

* assert that the NICK source is correct
2023-11-10 08:08:33 +01:00
Valentin Lorentz 1d8d78e62a sopel: Enable support for SCRAM 2022-10-28 14:26:47 +02:00
20 changed files with 438 additions and 264 deletions

View File

@ -25,15 +25,15 @@ jobs:
uses: actions/checkout@v3
with:
path: anope
ref: 2.0.9
ref: '2.1'
repository: anope/anope
- name: Build Anope
run: |
cd $GITHUB_WORKSPACE/anope/
cp $GITHUB_WORKSPACE/data/anope/* .
CFLAGS=-O0 ./Config -quick
make -C build -j 4
make -C build install
sudo apt-get install ninja-build --no-install-recommends
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local/ -DPROGRAM_NAME=anope -DUSE_PCH=ON -GNinja ..
ninja install
- name: Make artefact tarball
run: cd ~; tar -czf artefacts-anope.tar.gz .local/ go/
- name: Upload build artefacts
@ -153,12 +153,7 @@ jobs:
- name: Build InspIRCd
run: |
cd $GITHUB_WORKSPACE/inspircd/
# Insp3 <= 3.16.0 and Insp4 <= 4.0.0a21 don't support -DINSPIRCD_UNLIMITED_MAINLOOP
patch src/inspircd.cpp < $GITHUB_WORKSPACE/patches/inspircd_mainloop.patch || true
./configure --prefix=$HOME/.local/inspircd --development
CXXFLAGS=-DINSPIRCD_UNLIMITED_MAINLOOP make -j 4
make install
- name: Make artefact tarball
@ -555,9 +550,9 @@ jobs:
path: ergo
ref: master
repository: ergochat/ergo
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: ^1.21.0
go-version: ^1.22.0
- run: go version
- name: Build Ergo
run: |

View File

@ -25,15 +25,15 @@ jobs:
uses: actions/checkout@v3
with:
path: anope
ref: 2.0.9
ref: '2.0'
repository: anope/anope
- name: Build Anope
run: |
cd $GITHUB_WORKSPACE/anope/
cp $GITHUB_WORKSPACE/data/anope/* .
CFLAGS=-O0 ./Config -quick
make -C build -j 4
make -C build install
sudo apt-get install ninja-build --no-install-recommends
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local/ -DPROGRAM_NAME=anope -DUSE_PCH=ON -GNinja ..
ninja install
- name: Make artefact tarball
run: cd ~; tar -czf artefacts-anope.tar.gz .local/ go/
- name: Upload build artefacts
@ -61,12 +61,7 @@ jobs:
- name: Build InspIRCd
run: |
cd $GITHUB_WORKSPACE/inspircd/
# Insp3 <= 3.16.0 and Insp4 <= 4.0.0a21 don't support -DINSPIRCD_UNLIMITED_MAINLOOP
patch src/inspircd.cpp < $GITHUB_WORKSPACE/patches/inspircd_mainloop.patch || true
./configure --prefix=$HOME/.local/inspircd --development
CXXFLAGS=-DINSPIRCD_UNLIMITED_MAINLOOP make -j 4
make install
- name: Make artefact tarball

View File

@ -25,15 +25,15 @@ jobs:
uses: actions/checkout@v3
with:
path: anope
ref: 2.0.9
ref: 2.0.14
repository: anope/anope
- name: Build Anope
run: |
cd $GITHUB_WORKSPACE/anope/
cp $GITHUB_WORKSPACE/data/anope/* .
CFLAGS=-O0 ./Config -quick
make -C build -j 4
make -C build install
sudo apt-get install ninja-build --no-install-recommends
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local/ -DPROGRAM_NAME=anope -DUSE_PCH=ON -GNinja ..
ninja install
- name: Make artefact tarball
run: cd ~; tar -czf artefacts-anope.tar.gz .local/ go/
- name: Upload build artefacts
@ -189,17 +189,12 @@ jobs:
uses: actions/checkout@v3
with:
path: inspircd
ref: v3.15.0
ref: v3.17.0
repository: inspircd/inspircd
- name: Build InspIRCd
run: |
cd $GITHUB_WORKSPACE/inspircd/
# Insp3 <= 3.16.0 and Insp4 <= 4.0.0a21 don't support -DINSPIRCD_UNLIMITED_MAINLOOP
patch src/inspircd.cpp < $GITHUB_WORKSPACE/patches/inspircd_mainloop.patch || true
./configure --prefix=$HOME/.local/inspircd --development
CXXFLAGS=-DINSPIRCD_UNLIMITED_MAINLOOP make -j 4
make install
- name: Make artefact tarball
@ -631,9 +626,9 @@ jobs:
path: ergo
ref: irctest_stable
repository: ergochat/ergo
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: ^1.21.0
go-version: ^1.22.0
- run: go version
- name: Build Ergo
run: |

View File

@ -83,10 +83,16 @@ LIMNORIA_SELECTORS := \
(foo or not foo) \
$(EXTRA_SELECTORS)
# Tests marked with arbitrary_client_tags or react_tag can't pass because Sable does not support client tags yet
# Tests marked with private_chathistory can't pass because Sable does not implement CHATHISTORY for DMs
SABLE_SELECTORS := \
not Ergo \
and not deprecated \
and not strict \
and not arbitrary_client_tags \
and not react_tag \
and not private_chathistory \
and not whowas and not list and not lusers and not userhost and not time and not info \
$(EXTRA_SELECTORS)

View File

@ -1,8 +0,0 @@
INSTDIR="$HOME/.local/"
RUNGROUP=""
UMASK=077
DEBUG="yes"
USE_PCH="yes"
EXTRA_INCLUDE_DIRS=""
EXTRA_LIB_DIRS=""
EXTRA_CONFIG_ARGS=""

View File

@ -372,6 +372,8 @@ class BaseServicesController(_BaseController):
pass
elif msg.command in ("MODE", "221"): # RPL_UMODEIS
pass
elif msg.command == "396": # RPL_VISIBLEHOST
pass
elif msg.command == "NOTICE":
assert msg.prefix is not None
if "!" not in msg.prefix and "." in msg.prefix:

View File

@ -1,7 +1,8 @@
import functools
from pathlib import Path
import shutil
import subprocess
from typing import Type
from typing import Tuple, Type
from irctest.basecontrollers import BaseServicesController, DirectoryBasedController
@ -48,6 +49,8 @@ module {{
client = "NickServ"
forceemail = no
passlen = 1000 # Some tests need long passwords
maxpasslen = 1000
minpasslen = 1
}}
command {{ service = "NickServ"; name = "HELP"; command = "generic/help"; }}
@ -63,17 +66,28 @@ options {{
warningtimeout = 4h
}}
module {{ name = "m_sasl" }}
module {{ name = "enc_sha256" }}
module {{ name = "{module_prefix}sasl" }}
module {{ name = "enc_bcrypt" }}
module {{ name = "ns_cert" }}
"""
@functools.lru_cache()
def installed_version() -> Tuple[int, ...]:
output = subprocess.run(
["anope", "--version"], stdout=subprocess.PIPE, universal_newlines=True
).stdout
(anope, version, *trailing) = output.split()[0].split("-")
assert anope == "Anope"
return tuple(map(int, version.split(".")))
class AnopeController(BaseServicesController, DirectoryBasedController):
"""Collaborator for server controllers that rely on Anope"""
software_name = "Anope"
software_version = None
def run(self, protocol: str, server_hostname: str, server_port: int) -> None:
self.create_config()
@ -88,32 +102,46 @@ class AnopeController(BaseServicesController, DirectoryBasedController):
"ngircd",
)
assert self.directory
services_path = shutil.which("anope")
assert services_path
# Rewrite Anope 2.0 module names for 2.1
if not self.software_version:
self.software_version = installed_version()
if self.software_version >= (2, 1, 0):
if protocol == "charybdis":
protocol = "solanum"
elif protocol == "inspircd3":
protocol = "inspircd"
elif protocol == "unreal4":
protocol = "unrealircd"
with self.open_file("conf/services.conf") as fd:
fd.write(
TEMPLATE_CONFIG.format(
protocol=protocol,
server_hostname=server_hostname,
server_port=server_port,
module_prefix="" if self.software_version >= (2, 1, 2) else "m_",
)
)
with self.open_file("conf/empty_file") as fd:
pass
assert self.directory
services_path = shutil.which("services")
assert services_path
# Config and code need to be in the same directory, *obviously*
(self.directory / "lib").symlink_to(Path(services_path).parent.parent / "lib")
(self.directory / "modules").symlink_to(
Path(services_path).parent.parent / "modules"
)
self.proc = subprocess.Popen(
[
"services",
"-n", # don't fork
"--config=services.conf", # can't be an absolute path
# "--logdir",
# f"/tmp/services-{server_port}.log",
"anope",
"--config=services.conf", # can't be an absolute path in 2.0
"--nofork", # don't fork
"--nopid", # don't write a pid
],
cwd=self.directory,
# stdout=subprocess.DEVNULL,

View File

@ -14,6 +14,7 @@ BASE_CONFIG = {
"name": "My.Little.Server",
"listeners": {},
"max-sendq": "16k",
"casemapping": "ascii",
"connection-limits": {
"enabled": True,
"cidr-len-ipv4": 32,

View File

@ -104,6 +104,7 @@ def installed_version() -> int:
class InspircdController(BaseServerController, DirectoryBasedController):
software_name = "InspIRCd"
software_version = installed_version()
supported_sasl_mechanisms = {"PLAIN"}
supports_sts = False
extban_mute_char = "m"

View File

@ -27,7 +27,11 @@ auth_password = {password}
class SopelController(BaseClientController):
software_name = "Sopel"
supported_sasl_mechanisms = {"PLAIN"}
supported_sasl_mechanisms = {
"PLAIN",
"SCRAM-SHA-256",
"EXTERNAL",
}
supports_sts = False
def __init__(self, test_config: TestCaseControllerConfig):
@ -61,6 +65,8 @@ class SopelController(BaseClientController):
# Runs a client with the config given as arguments
if tls_config is not None:
raise NotImplementedByController("TLS configuration")
if auth and len(auth.mechanisms) > 1:
raise NotImplementedByController("multiple SASL mechanisms")
assert self.proc is None
self.create_config()
with self.open_file(self.filename) as fd:
@ -70,7 +76,12 @@ class SopelController(BaseClientController):
port=port,
username=auth.username if auth else "",
password=auth.password if auth else "",
auth_method="auth_method = sasl" if auth else "",
auth_method=(
f"auth_method = sasl\n"
f"auth_target = {auth.mechanisms[0].to_string()}"
)
if auth
else "",
)
)
self.proc = subprocess.Popen(["sopel", "-c", self.filename])

View File

@ -58,6 +58,16 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
def config() -> cases.TestCaseControllerConfig:
return cases.TestCaseControllerConfig(chathistory=True)
def _supports_msgid(self):
return "msgid" in self.server_support.get(
"MSGREFTYPES", "msgid,timestamp"
).split(",")
def _supports_timestamp(self):
return "timestamp" in self.server_support.get(
"MSGREFTYPES", "msgid,timestamp"
).split(",")
@skip_ngircd
def testInvalidTargets(self):
bar, pw = random_name("bar"), random_name("pw")
@ -460,172 +470,195 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[-1:], result)
self.sendLine(
user,
"CHATHISTORY LATEST %s msgid=%s %d"
% (chname, echo_messages[4].msgid, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[5:], result)
if self._supports_msgid():
self.sendLine(
user,
"CHATHISTORY LATEST %s msgid=%s %d"
% (chname, echo_messages[4].msgid, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[5:], result)
self.sendLine(
user,
"CHATHISTORY LATEST %s timestamp=%s %d"
% (chname, echo_messages[4].time, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[5:], result)
if self._supports_timestamp():
self.sendLine(
user,
"CHATHISTORY LATEST %s timestamp=%s %d"
% (chname, echo_messages[4].time, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[5:], result)
def _validate_chathistory_BEFORE(self, echo_messages, user, chname):
INCLUSIVE_LIMIT = len(echo_messages) * 2
self.sendLine(
user,
"CHATHISTORY BEFORE %s msgid=%s %d"
% (chname, echo_messages[6].msgid, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[:6], result)
if self._supports_msgid():
self.sendLine(
user,
"CHATHISTORY BEFORE %s msgid=%s %d"
% (chname, echo_messages[6].msgid, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[:6], result)
self.sendLine(
user,
"CHATHISTORY BEFORE %s timestamp=%s %d"
% (chname, echo_messages[6].time, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[:6], result)
if self._supports_timestamp():
self.sendLine(
user,
"CHATHISTORY BEFORE %s timestamp=%s %d"
% (chname, echo_messages[6].time, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[:6], result)
self.sendLine(
user,
"CHATHISTORY BEFORE %s timestamp=%s %d"
% (chname, echo_messages[6].time, 2),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:6], result)
self.sendLine(
user,
"CHATHISTORY BEFORE %s timestamp=%s %d"
% (chname, echo_messages[6].time, 2),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:6], result)
def _validate_chathistory_AFTER(self, echo_messages, user, chname):
INCLUSIVE_LIMIT = len(echo_messages) * 2
self.sendLine(
user,
"CHATHISTORY AFTER %s msgid=%s %d"
% (chname, echo_messages[3].msgid, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:], result)
if self._supports_msgid():
self.sendLine(
user,
"CHATHISTORY AFTER %s msgid=%s %d"
% (chname, echo_messages[3].msgid, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:], result)
self.sendLine(
user,
"CHATHISTORY AFTER %s timestamp=%s %d"
% (chname, echo_messages[3].time, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:], result)
if self._supports_timestamp():
self.sendLine(
user,
"CHATHISTORY AFTER %s timestamp=%s %d"
% (chname, echo_messages[3].time, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:], result)
self.sendLine(
user,
"CHATHISTORY AFTER %s timestamp=%s %d" % (chname, echo_messages[3].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:7], result)
self.sendLine(
user,
"CHATHISTORY AFTER %s timestamp=%s %d"
% (chname, echo_messages[3].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:7], result)
def _validate_chathistory_BETWEEN(self, echo_messages, user, chname):
INCLUSIVE_LIMIT = len(echo_messages) * 2
# BETWEEN forwards and backwards
self.sendLine(
user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (
chname,
echo_messages[0].msgid,
echo_messages[-1].msgid,
INCLUSIVE_LIMIT,
),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result)
if self._supports_msgid():
# BETWEEN forwards and backwards
self.sendLine(
user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (
chname,
echo_messages[0].msgid,
echo_messages[-1].msgid,
INCLUSIVE_LIMIT,
),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (
chname,
echo_messages[-1].msgid,
echo_messages[0].msgid,
INCLUSIVE_LIMIT,
),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (
chname,
echo_messages[-1].msgid,
echo_messages[0].msgid,
INCLUSIVE_LIMIT,
),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result)
# BETWEEN forwards and backwards with a limit, should get
# different results this time
self.sendLine(
user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (chname, echo_messages[0].msgid, echo_messages[-1].msgid, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:4], result)
# BETWEEN forwards and backwards with a limit, should get
# different results this time
self.sendLine(
user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (chname, echo_messages[0].msgid, echo_messages[-1].msgid, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:4], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (chname, echo_messages[-1].msgid, echo_messages[0].msgid, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[-4:-1], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (chname, echo_messages[-1].msgid, echo_messages[0].msgid, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[-4:-1], result)
# same stuff again but with timestamps
self.sendLine(
user,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (chname, echo_messages[0].time, echo_messages[-1].time, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (chname, echo_messages[-1].time, echo_messages[0].time, INCLUSIVE_LIMIT),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (chname, echo_messages[0].time, echo_messages[-1].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:4], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (chname, echo_messages[-1].time, echo_messages[0].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[-4:-1], result)
if self._supports_timestamp():
# same stuff again but with timestamps
self.sendLine(
user,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (
chname,
echo_messages[0].time,
echo_messages[-1].time,
INCLUSIVE_LIMIT,
),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (
chname,
echo_messages[-1].time,
echo_messages[0].time,
INCLUSIVE_LIMIT,
),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (chname, echo_messages[0].time, echo_messages[-1].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:4], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (chname, echo_messages[-1].time, echo_messages[0].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[-4:-1], result)
def _validate_chathistory_AROUND(self, echo_messages, user, chname):
self.sendLine(
user,
"CHATHISTORY AROUND %s msgid=%s %d" % (chname, echo_messages[7].msgid, 1),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual([echo_messages[7]], result)
if self._supports_msgid():
self.sendLine(
user,
"CHATHISTORY AROUND %s msgid=%s %d"
% (chname, echo_messages[7].msgid, 1),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual([echo_messages[7]], result)
self.sendLine(
user,
"CHATHISTORY AROUND %s msgid=%s %d" % (chname, echo_messages[7].msgid, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[6:9], result)
self.sendLine(
user,
"CHATHISTORY AROUND %s msgid=%s %d"
% (chname, echo_messages[7].msgid, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[6:9], result)
self.sendLine(
user,
"CHATHISTORY AROUND %s timestamp=%s %d"
% (chname, echo_messages[7].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertIn(echo_messages[7], result)
if self._supports_timestamp():
self.sendLine(
user,
"CHATHISTORY AROUND %s timestamp=%s %d"
% (chname, echo_messages[7].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertIn(echo_messages[7], result)
@pytest.mark.arbitrary_client_tags
@skip_ngircd

View File

@ -12,8 +12,8 @@ class ConfusablesTestCase(cases.BaseServerTestCase):
@staticmethod
def config() -> cases.TestCaseControllerConfig:
return cases.TestCaseControllerConfig(
ergo_config=lambda config: config["accounts"].update(
{"nick-reservation": {"enabled": True, "method": "strict"}}
ergo_config=lambda config: config["server"].update(
{"casemapping": "precis"},
)
)

View File

@ -9,6 +9,38 @@ from irctest import cases, runner
class IsupportTestCase(cases.BaseServerTestCase):
@cases.mark_specifications("Modern")
@cases.mark_isupport("PREFIX")
def testParameters(self):
"""https://modern.ircdocs.horse/#rplisupport-005"""
# <https://modern.ircdocs.horse/#connection-registration>
# "Upon successful completion of the registration process,
# the server MUST send, in this order:
# [...]
# 5. at least one RPL_ISUPPORT (005) numeric to the client."
welcome_005s = [
msg for msg in self.connectClient("foo") if msg.command == "005"
]
self.assertGreaterEqual(len(welcome_005s), 1)
for msg in welcome_005s:
# first parameter is the client's nickname;
# last parameter is a human-readable trailing, typically
# "are supported by this server"
self.assertGreaterEqual(len(msg.params), 3)
self.assertEqual(msg.params[0], "foo")
# "As the maximum number of message parameters to any reply is 15,
# the maximum number of RPL_ISUPPORT tokens that can be advertised
# is 13."
self.assertLessEqual(len(msg.params), 15)
for param in msg.params[1:-1]:
self.validateIsupportParam(param)
def validateIsupportParam(self, param):
if not param.isascii():
raise ValueError("Invalid non-ASCII 005 parameter", param)
# TODO add more validation
@cases.mark_specifications("Modern")
@cases.mark_isupport("PREFIX")
def testPrefix(self):

View File

@ -42,14 +42,21 @@ class RegressionsTestCase(cases.BaseServerTestCase):
self.getMessages(1)
self.getMessages(2)
# case change: both alice and bob should get a successful nick line
# 'alice' is claimed, so 'Alice' is reserved and Bob cannot take it:
self.sendLine(2, "NICK Alice")
ms = self.getMessages(2)
self.assertEqual(len(ms), 1)
self.assertMessageMatch(ms[0], command=ERR_NICKNAMEINUSE)
# but alice can change case to 'Alice'; both alice and bob should get
# a successful NICK line
self.sendLine(1, "NICK Alice")
ms = self.getMessages(1)
self.assertEqual(len(ms), 1)
self.assertMessageMatch(ms[0], command="NICK", params=["Alice"])
self.assertMessageMatch(ms[0], nick="alice", command="NICK", params=["Alice"])
ms = self.getMessages(2)
self.assertEqual(len(ms), 1)
self.assertMessageMatch(ms[0], command="NICK", params=["Alice"])
self.assertMessageMatch(ms[0], nick="alice", command="NICK", params=["Alice"])
# no responses, either to the user or to friends, from a no-op nick change
self.sendLine(1, "NICK Alice")
@ -190,3 +197,27 @@ class RegressionsTestCase(cases.BaseServerTestCase):
self.sendLine(2, "USER u s e r")
reply = self.getRegistrationMessage(2)
self.assertMessageMatch(reply, command=RPL_WELCOME)
@cases.mark_specifications("IRCv3")
def testLabeledNick(self):
"""
InspIRCd up to 3.16.1 used the new nick as source of NICK changes
https://github.com/inspircd/inspircd/issues/2067
https://github.com/inspircd/inspircd/commit/83f01b36a11734fd91a4e7aad99c15463858fe4a
"""
self.connectClient(
"alice",
capabilities=["batch", "labeled-response"],
skip_if_cap_nak=True,
)
self.sendLine(1, "@label=abc NICK alice2")
self.assertMessageMatch(
self.getMessage(1),
nick="alice",
command="NICK",
params=["alice2"],
tags={"label": "abc", **ANYDICT},
)

View File

@ -1,7 +1,7 @@
import base64
from irctest import cases, runner, scram
from irctest.numerics import ERR_SASLFAIL
from irctest.numerics import ERR_SASLFAIL, RPL_LOGGEDIN, RPL_SASLMECHS
from irctest.patma import ANYSTR
@ -48,11 +48,37 @@ class SaslTestCase(cases.BaseServerTestCase):
m = self.getRegistrationMessage(1)
self.assertMessageMatch(
m,
command="900",
command=RPL_LOGGEDIN,
params=[ANYSTR, ANYSTR, "jilles", ANYSTR],
fail_msg="Unexpected reply to correct SASL authentication: {msg}",
)
@cases.mark_specifications("IRCv3")
@cases.skipUnlessHasMechanism("PLAIN")
def testPlainFailure(self):
"""PLAIN authentication with incorrect username/password."""
self.controller.registerUser(self, "jilles", "sesame")
self.addClient()
self.requestCapabilities(1, ["sasl"], skip_if_cap_nak=False)
self.sendLine(1, "AUTHENTICATE PLAIN")
m = self.getRegistrationMessage(1)
self.assertMessageMatch(
m,
command="AUTHENTICATE",
params=["+"],
fail_msg="Sent “AUTHENTICATE PLAIN”, server should have "
"replied with “AUTHENTICATE +”, but instead sent: {msg}",
)
# password 'millet'
self.sendLine(1, "AUTHENTICATE amlsbGVzAGppbGxlcwBtaWxsZXQ=")
m = self.getRegistrationMessage(1)
self.assertMessageMatch(
m,
command=ERR_SASLFAIL,
params=[ANYSTR, ANYSTR],
fail_msg="Unexpected reply to incorrect SASL authentication: {msg}",
)
@cases.mark_specifications("IRCv3")
@cases.skipUnlessHasMechanism("PLAIN")
def testPlainNonAscii(self):
@ -161,11 +187,11 @@ class SaslTestCase(cases.BaseServerTestCase):
self.requestCapabilities(1, ["sasl"], skip_if_cap_nak=False)
self.sendLine(1, "AUTHENTICATE FOO")
m = self.getRegistrationMessage(1)
while m.command == "908": # RPL_SASLMECHS
while m.command == RPL_SASLMECHS:
m = self.getRegistrationMessage(1)
self.assertMessageMatch(
m,
command="904",
command=ERR_SASLFAIL,
fail_msg="Did not reply with 904 to “AUTHENTICATE FOO”: {msg}",
)

View File

@ -5,6 +5,7 @@
"""
from irctest import cases, runner
from irctest.numerics import ERR_ERRONEUSNICKNAME
from irctest.patma import ANYSTR
@ -53,15 +54,15 @@ class Utf8TestCase(cases.BaseServerTestCase):
raise runner.IsupportTokenNotSupported("UTF8ONLY")
self.addClient()
self.sendLine(2, "NICK foo")
self.sendLine(2, "NICK bar")
self.clients[2].conn.sendall(b"USER username * * :i\xe8rc\xe9\r\n")
d = self.clients[2].conn.recv(1024)
if b" FAIL " in d or b" 468 " in d: # ERR_INVALIDUSERNAME
if b"FAIL " in d or b"468 " in d: # ERR_INVALIDUSERNAME
return # nothing more to test
self.assertIn(b" 001 ", d)
self.assertIn(b"001 ", d)
self.sendLine(2, "WHOIS foo")
self.sendLine(2, "WHOIS bar")
self.getMessages(2)
def testNonutf8Username(self):
@ -70,7 +71,7 @@ class Utf8TestCase(cases.BaseServerTestCase):
raise runner.IsupportTokenNotSupported("UTF8ONLY")
self.addClient()
self.sendLine(2, "NICK foo")
self.sendLine(2, "NICK bar")
self.sendLine(2, "USER 😊😊😊😊😊😊😊😊😊😊 * * :realname")
m = self.getRegistrationMessage(2)
if m.command in ("FAIL", "468"): # ERR_INVALIDUSERNAME
@ -79,5 +80,40 @@ class Utf8TestCase(cases.BaseServerTestCase):
m,
command="001",
)
self.sendLine(2, "WHOIS foo")
self.sendLine(2, "WHOIS bar")
self.getMessages(2)
class ErgoUtf8NickEnabledTestCase(cases.BaseServerTestCase):
@staticmethod
def config() -> cases.TestCaseControllerConfig:
return cases.TestCaseControllerConfig(
ergo_config=lambda config: config["server"].update(
{"casemapping": "precis"},
)
)
@cases.mark_specifications("Ergo")
def testUtf8NonAsciiNick(self):
"""Ergo accepts certain non-ASCII UTF8 nicknames if PRECIS is enabled."""
self.connectClient("ıl")
self.joinChannel(1, "#test")
self.connectClient("Claire")
self.joinChannel(2, "#test")
self.sendLine(1, "PRIVMSG #test :hi there")
self.getMessages(1)
self.assertMessageMatch(
self.getMessage(2), nick="ıl", params=["#test", "hi there"]
)
class ErgoUtf8NickDisabledTestCase(cases.BaseServerTestCase):
@cases.mark_specifications("Ergo")
def testUtf8NonAsciiNick(self):
"""Ergo rejects non-ASCII nicknames in its default configuration."""
self.addClient(1)
self.sendLine(1, "USER u s e r")
self.sendLine(1, "NICK Işıl")
self.assertMessageMatch(self.getMessage(1), command=ERR_ERRONEUSNICKNAME)

View File

@ -60,7 +60,7 @@ class BaseWhoTestCase:
"*", # no chan
StrRe("~?" + self.username),
StrRe(host_re),
"My.Little.Server",
StrRe(r"(My.Little.Server|\*)"),
"coolNick",
flags,
StrRe(realname_regexp(self.realname)),
@ -76,7 +76,7 @@ class BaseWhoTestCase:
"#chan",
StrRe("~?" + self.username),
StrRe(host_re),
"My.Little.Server",
StrRe(r"(My.Little.Server|\*)"),
"coolNick",
flags + "@",
StrRe(realname_regexp(self.realname)),
@ -336,7 +336,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
"#chan",
StrRe("~?" + self.username),
StrRe(host_re),
"My.Little.Server",
StrRe(r"(My.Little.Server|\*)"),
"coolNick",
"G@",
StrRe(realname_regexp(self.realname)),
@ -351,7 +351,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
"#chan",
ANYSTR,
ANYSTR,
"My.Little.Server",
StrRe(r"(My.Little.Server|\*)"),
"otherNick",
"H",
StrRe("[0-9]+ .*"),
@ -398,7 +398,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
chan,
ANYSTR,
ANYSTR,
"My.Little.Server",
StrRe(r"(My.Little.Server|\*)"),
"coolNick",
ANYSTR,
ANYSTR,
@ -413,7 +413,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
chan,
ANYSTR,
ANYSTR,
"My.Little.Server",
StrRe(r"(My.Little.Server|\*)"),
"otherNick",
ANYSTR,
ANYSTR,
@ -479,7 +479,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
StrRe("~?myusernam"),
ANYSTR,
ANYSTR,
"My.Little.Server",
StrRe(r"(My.Little.Server|\*)"),
"coolNick",
StrRe("H@?"),
ANYSTR, # hopcount

View File

@ -8,6 +8,7 @@ import pytest
from irctest import cases
from irctest.numerics import (
ERR_NOSUCHNICK,
RPL_AWAY,
RPL_ENDOFWHOIS,
RPL_WHOISACCOUNT,
@ -217,6 +218,25 @@ class WhoisTestCase(_WhoisTestMixin, cases.BaseServerTestCase):
whois_user.params[3], [nick, username, "~" + username, realname]
)
@cases.mark_specifications("RFC2812")
@cases.xfailIfSoftware(["Sable"], "https://github.com/Libera-Chat/sable/issues/101")
def testWhoisMissingUser(self):
"""Test WHOIS on a nonexistent nickname."""
self.connectClient("qux", name="qux")
self.sendLine("qux", "WHOIS bar")
messages = self.getMessages("qux")
self.assertEqual(len(messages), 2)
self.assertMessageMatch(
messages[0],
command=ERR_NOSUCHNICK,
params=["qux", "bar", ANYSTR],
)
self.assertMessageMatch(
messages[1],
command=RPL_ENDOFWHOIS,
params=["qux", "bar", ANYSTR],
)
@pytest.mark.parametrize(
"away,oper",
[(False, False), (True, False), (False, True)],

View File

@ -1,25 +0,0 @@
When a client registers (ie. sends USER+NICK), InspIRCd does not
immediately answers with 001. Instead it waits for the next iteration
of the main loop to call `DoBackgroundUserStuff`.
However, this main loop executes only once a second. This is usually
fine, but makes irctest considerably slower, as irctest uses hundreds
of very short-lived connections.
This patch removes the frequency limitation of the main loop to make
InspIRCd more responsive.
diff --git a/src/inspircd.cpp b/src/inspircd.cpp
index 5760e631b..1da0285fb 100644
--- a/src/inspircd.cpp
+++ b/src/inspircd.cpp
@@ -680,7 +680,7 @@ void InspIRCd::Run()
* timing using this event, so we dont have to
* time this exactly).
*/
- if (TIME.tv_sec != OLDTIME)
+ if (true)
{
CollectStats();
CheckTimeSkip(OLDTIME, TIME.tv_sec);

View File

@ -134,9 +134,9 @@ software:
path: ergo
prefix: ~/go
pre_deps:
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: '^1.21.0'
go-version: '^1.22.0'
- run: go version
separate_build_job: false
build_script: |
@ -148,7 +148,7 @@ software:
name: InspIRCd
repository: inspircd/inspircd
refs: &inspircd_refs
stable: v3.15.0
stable: v3.17.0
release: null
devel: master
devel_release: insp3
@ -158,12 +158,7 @@ software:
separate_build_job: true
build_script: &inspircd_build_script |
cd $GITHUB_WORKSPACE/inspircd/
# Insp3 <= 3.16.0 and Insp4 <= 4.0.0a21 don't support -DINSPIRCD_UNLIMITED_MAINLOOP
patch src/inspircd.cpp < $GITHUB_WORKSPACE/patches/inspircd_mainloop.patch || true
./configure --prefix=$HOME/.local/inspircd --development
CXXFLAGS=-DINSPIRCD_UNLIMITED_MAINLOOP make -j 4
make install
irc2:
@ -348,16 +343,16 @@ software:
separate_build_job: true
path: anope
refs:
stable: "2.0.9"
release: "2.0.9"
devel: "2.0.9"
devel_release: "2.0.9"
stable: "2.0.14"
release: "2.1.1"
devel: "2.1"
devel_release: "2.0"
build_script: |
cd $GITHUB_WORKSPACE/anope/
cp $GITHUB_WORKSPACE/data/anope/* .
CFLAGS=-O0 ./Config -quick
make -C build -j 4
make -C build install
sudo apt-get install ninja-build --no-install-recommends
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local/ -DPROGRAM_NAME=anope -DUSE_PCH=ON -GNinja ..
ninja install
dlk:
name: Dlk