mirror of
https://github.com/progval/irctest.git
synced 2025-04-06 23:39:46 +00:00
Add Sable (#229)
* [WIP] Add support for Sable * tweak sable controller * echo_message: Add missing synchronization for Sable * update sable * whois: Simplify test * WHO: Remove test for oper flag from testWhoChan So it won't fail on Sable, which hides oper status * WHO: Skip/xfail tests for Sable as needed * Skip NakWhole when multi-prefix is not supported * [WIP] Run Sable on CI * working-directory is not setable on actions * this isn't ergo * this really isn't ergo * minimize rust install and cache cargo deps * Need to specify packages to install... * Phony target * Give up on 'cargo install', it seems to ignore the cache * try again to cache the target dir * This isn't Solanum * Comment out BaseServicesController * Parallelize Sable tests * target is relative... * sigh * Fix prefix * Re-add the other software * chathistory: Test TOPIC is not sent unless event-playable is enabled * sable: Dynamically generate certificates This allows using custom server/services names * sable: Enable services * sable: Add support for account registration Sable doesn't support REGISTER via NickServ * sable: Lower log verbosity * Fix lint * Re-add Sable to CI * Fix/skip tests on Sable * Kill sable_services' subprocesses * Bump Sable to include the labeled-response fix * Bump Sable to the channel-rename downgrade fix
This commit is contained in:
70
.github/workflows/test-devel.yml
vendored
70
.github/workflows/test-devel.yml
vendored
@ -402,6 +402,7 @@ jobs:
|
|||||||
- test-ngircd-anope
|
- test-ngircd-anope
|
||||||
- test-ngircd-atheme
|
- test-ngircd-atheme
|
||||||
- test-plexus4
|
- test-plexus4
|
||||||
|
- test-sable
|
||||||
- test-solanum
|
- test-solanum
|
||||||
- test-sopel
|
- test-sopel
|
||||||
- test-thelounge
|
- test-thelounge
|
||||||
@ -570,7 +571,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/go/sbin:~/go/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/go/sbin:~/go/bin:~/go:$PATH
|
||||||
make ergo
|
make ergo
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -642,7 +643,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:~/.local/inspircd:$PATH
|
||||||
make inspircd
|
make inspircd
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -681,7 +682,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:$PATH make
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:~/.local/inspircd:$PATH make
|
||||||
inspircd-anope
|
inspircd-anope
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -819,7 +820,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:~/.local/:$PATH
|
||||||
make ngircd
|
make ngircd
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -858,7 +859,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:$PATH make
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:~/.local/:$PATH make
|
||||||
ngircd-anope
|
ngircd-anope
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -891,7 +892,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:~/.local/:$PATH
|
||||||
make ngircd-atheme
|
make ngircd-atheme
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -939,6 +940,53 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: pytest-results_plexus4_devel
|
name: pytest-results_plexus4_devel
|
||||||
path: pytest.xml
|
path: pytest.xml
|
||||||
|
test-sable:
|
||||||
|
needs: []
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python 3.11
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.11
|
||||||
|
- name: Checkout Sable
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: sable
|
||||||
|
ref: master
|
||||||
|
repository: Libera-Chat/sable
|
||||||
|
- name: Install rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
toolchain: nightly
|
||||||
|
- name: Enable Cargo cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
cache-on-failure: true
|
||||||
|
workspaces: sable -> target
|
||||||
|
- run: rustc --version
|
||||||
|
- name: Build Sable
|
||||||
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE/sable/
|
||||||
|
cargo build
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: sudo apt-get install atheme-services faketime
|
||||||
|
- name: Install irctest dependencies
|
||||||
|
run: |-
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
|
- name: Test with pytest
|
||||||
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=$GITHUB_WORKSPACE/sable/target/debug/sbin:$GITHUB_WORKSPACE/sable/target/debug/bin:$GITHUB_WORKSPACE/sable/target/debug:$PATH
|
||||||
|
make sable
|
||||||
|
timeout-minutes: 30
|
||||||
|
- if: always()
|
||||||
|
name: Publish results
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: pytest-results_sable_devel
|
||||||
|
path: pytest.xml
|
||||||
test-solanum:
|
test-solanum:
|
||||||
needs:
|
needs:
|
||||||
- build-solanum
|
- build-solanum
|
||||||
@ -1061,7 +1109,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:~/.local/unrealircd:$PATH
|
||||||
make unrealircd
|
make unrealircd
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1094,7 +1142,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:~/.local/unrealircd:$PATH
|
||||||
make unrealircd-5
|
make unrealircd-5
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1133,7 +1181,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:$PATH make
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:~/.local/unrealircd:$PATH make
|
||||||
unrealircd-anope
|
unrealircd-anope
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1166,7 +1214,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:~/.local/unrealircd:$PATH
|
||||||
make unrealircd-atheme
|
make unrealircd-atheme
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1210,7 +1258,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:~/.local/unrealircd:$PATH
|
||||||
IRCTEST_DLK_PATH="${{ github.workspace }}/Dlk-Services" IRCTEST_WP_CLI_PATH="${{
|
IRCTEST_DLK_PATH="${{ github.workspace }}/Dlk-Services" IRCTEST_WP_CLI_PATH="${{
|
||||||
github.workspace }}/wp-cli.phar" IRCTEST_WP_ZIP_PATH="${{ github.workspace
|
github.workspace }}/wp-cli.phar" IRCTEST_WP_ZIP_PATH="${{ github.workspace
|
||||||
}}/wordpress-latest.zip" make unrealircd-dlk
|
}}/wordpress-latest.zip" make unrealircd-dlk
|
||||||
|
6
.github/workflows/test-devel_release.yml
vendored
6
.github/workflows/test-devel_release.yml
vendored
@ -132,7 +132,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:~/.local/inspircd:$PATH
|
||||||
make inspircd
|
make inspircd
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -171,7 +171,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:$PATH make
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:~/.local/inspircd:$PATH make
|
||||||
inspircd-anope
|
inspircd-anope
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -204,7 +204,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:~/.local/inspircd:$PATH
|
||||||
make inspircd-atheme
|
make inspircd-atheme
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
|
70
.github/workflows/test-stable.yml
vendored
70
.github/workflows/test-stable.yml
vendored
@ -446,6 +446,7 @@ jobs:
|
|||||||
- test-ngircd-anope
|
- test-ngircd-anope
|
||||||
- test-ngircd-atheme
|
- test-ngircd-atheme
|
||||||
- test-plexus4
|
- test-plexus4
|
||||||
|
- test-sable
|
||||||
- test-solanum
|
- test-solanum
|
||||||
- test-sopel
|
- test-sopel
|
||||||
- test-thelounge
|
- test-thelounge
|
||||||
@ -646,7 +647,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/go/sbin:~/go/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/go/sbin:~/go/bin:~/go:$PATH
|
||||||
make ergo
|
make ergo
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -718,7 +719,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:~/.local/inspircd:$PATH
|
||||||
make inspircd
|
make inspircd
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -757,7 +758,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:$PATH make
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:~/.local/inspircd:$PATH make
|
||||||
inspircd-anope
|
inspircd-anope
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -790,7 +791,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/inspircd/sbin:~/.local/inspircd/bin:~/.local/inspircd:$PATH
|
||||||
make inspircd-atheme
|
make inspircd-atheme
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -977,7 +978,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:~/.local/:$PATH
|
||||||
make ngircd
|
make ngircd
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1016,7 +1017,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:$PATH make
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:~/.local/:$PATH make
|
||||||
ngircd-anope
|
ngircd-anope
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1049,7 +1050,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local//sbin:~/.local//bin:~/.local/:$PATH
|
||||||
make ngircd-atheme
|
make ngircd-atheme
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1097,6 +1098,53 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: pytest-results_plexus4_stable
|
name: pytest-results_plexus4_stable
|
||||||
path: pytest.xml
|
path: pytest.xml
|
||||||
|
test-sable:
|
||||||
|
needs: []
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python 3.11
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.11
|
||||||
|
- name: Checkout Sable
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: sable
|
||||||
|
ref: ff1179512a79eba57ca468a5f83af84ecce08a5b
|
||||||
|
repository: Libera-Chat/sable
|
||||||
|
- name: Install rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
toolchain: nightly
|
||||||
|
- name: Enable Cargo cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
cache-on-failure: true
|
||||||
|
workspaces: sable -> target
|
||||||
|
- run: rustc --version
|
||||||
|
- name: Build Sable
|
||||||
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE/sable/
|
||||||
|
cargo build
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: sudo apt-get install atheme-services faketime
|
||||||
|
- name: Install irctest dependencies
|
||||||
|
run: |-
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
|
- name: Test with pytest
|
||||||
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=$GITHUB_WORKSPACE/sable/target/debug/sbin:$GITHUB_WORKSPACE/sable/target/debug/bin:$GITHUB_WORKSPACE/sable/target/debug:$PATH
|
||||||
|
make sable
|
||||||
|
timeout-minutes: 30
|
||||||
|
- if: always()
|
||||||
|
name: Publish results
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: pytest-results_sable_stable
|
||||||
|
path: pytest.xml
|
||||||
test-solanum:
|
test-solanum:
|
||||||
needs:
|
needs:
|
||||||
- build-solanum
|
- build-solanum
|
||||||
@ -1219,7 +1267,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:~/.local/unrealircd:$PATH
|
||||||
make unrealircd
|
make unrealircd
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1252,7 +1300,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:~/.local/unrealircd:$PATH
|
||||||
make unrealircd-5
|
make unrealircd-5
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1291,7 +1339,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:$PATH make
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:~/.local/unrealircd:$PATH make
|
||||||
unrealircd-anope
|
unrealircd-anope
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
@ -1324,7 +1372,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest pytest-xdist -r requirements.txt
|
pip install pytest pytest-xdist -r requirements.txt
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:$PATH
|
run: PYTEST_ARGS='--junit-xml pytest.xml' PATH=$HOME/.local/bin:$PATH PATH=~/.local/unrealircd/sbin:~/.local/unrealircd/bin:~/.local/unrealircd:$PATH
|
||||||
make unrealircd-atheme
|
make unrealircd-atheme
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
- if: always()
|
- if: always()
|
||||||
|
18
Makefile
18
Makefile
@ -87,6 +87,13 @@ LIMNORIA_SELECTORS := \
|
|||||||
(foo or not foo) \
|
(foo or not foo) \
|
||||||
$(EXTRA_SELECTORS)
|
$(EXTRA_SELECTORS)
|
||||||
|
|
||||||
|
SABLE_SELECTORS := \
|
||||||
|
not Ergo \
|
||||||
|
and not deprecated \
|
||||||
|
and not strict \
|
||||||
|
and not whowas and not list and not lusers and not userhost and not time and not info \
|
||||||
|
$(EXTRA_SELECTORS)
|
||||||
|
|
||||||
SOLANUM_SELECTORS := \
|
SOLANUM_SELECTORS := \
|
||||||
not Ergo \
|
not Ergo \
|
||||||
and not deprecated \
|
and not deprecated \
|
||||||
@ -118,9 +125,9 @@ UNREALIRCD_SELECTORS := \
|
|||||||
and not private_chathistory \
|
and not private_chathistory \
|
||||||
$(EXTRA_SELECTORS)
|
$(EXTRA_SELECTORS)
|
||||||
|
|
||||||
.PHONY: all flakes bahamut charybdis ergo inspircd ircu2 snircd irc2 mammon nefarious limnoria sopel solanum unrealircd
|
.PHONY: all flakes bahamut charybdis ergo inspircd ircu2 snircd irc2 mammon nefarious limnoria sable sopel solanum unrealircd
|
||||||
|
|
||||||
all: flakes bahamut charybdis ergo inspircd ircu2 snircd irc2 mammon nefarious limnoria sopel solanum unrealircd
|
all: flakes bahamut charybdis ergo inspircd ircu2 snircd irc2 mammon nefarious limnoria sable sopel solanum unrealircd
|
||||||
|
|
||||||
flakes:
|
flakes:
|
||||||
find irctest/ -name "*.py" -not -path "irctest/scram/*" -print0 | xargs -0 pyflakes3
|
find irctest/ -name "*.py" -not -path "irctest/scram/*" -print0 | xargs -0 pyflakes3
|
||||||
@ -249,6 +256,13 @@ ngircd-atheme:
|
|||||||
-m 'services' \
|
-m 'services' \
|
||||||
-k "$(NGIRCD_SELECTORS)"
|
-k "$(NGIRCD_SELECTORS)"
|
||||||
|
|
||||||
|
sable:
|
||||||
|
$(PYTEST) $(PYTEST_ARGS) \
|
||||||
|
--controller=irctest.controllers.sable \
|
||||||
|
-n 20 \
|
||||||
|
-m 'not services' \
|
||||||
|
-k '$(SABLE_SELECTORS)'
|
||||||
|
|
||||||
solanum:
|
solanum:
|
||||||
$(PYTEST) $(PYTEST_ARGS) \
|
$(PYTEST) $(PYTEST_ARGS) \
|
||||||
--controller=irctest.controllers.solanum \
|
--controller=irctest.controllers.solanum \
|
||||||
|
@ -68,6 +68,7 @@ class _BaseController:
|
|||||||
|
|
||||||
supports_sts: bool
|
supports_sts: bool
|
||||||
supported_sasl_mechanisms: Set[str]
|
supported_sasl_mechanisms: Set[str]
|
||||||
|
|
||||||
proc: Optional[subprocess.Popen]
|
proc: Optional[subprocess.Popen]
|
||||||
|
|
||||||
_used_ports: Set[Tuple[str, int]]
|
_used_ports: Set[Tuple[str, int]]
|
||||||
@ -248,6 +249,12 @@ class BaseServerController(_BaseController):
|
|||||||
extban_mute_char: Optional[str] = None
|
extban_mute_char: Optional[str] = None
|
||||||
"""Character used for the 'mute' extban"""
|
"""Character used for the 'mute' extban"""
|
||||||
nickserv = "NickServ"
|
nickserv = "NickServ"
|
||||||
|
sync_sleep_time = 0.0
|
||||||
|
"""How many seconds to sleep before clients synchronously get messages.
|
||||||
|
|
||||||
|
This can be 0 for servers answering all commands in order (all but Sable as of
|
||||||
|
this writing), as irctest emits a PING, waits for a PONG, and captures all messages
|
||||||
|
between the two."""
|
||||||
|
|
||||||
def __init__(self, *args: Any, **kwargs: Any):
|
def __init__(self, *args: Any, **kwargs: Any):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -350,6 +357,7 @@ class BaseServicesController(_BaseController):
|
|||||||
c.connect(self.server_controller.hostname, self.server_controller.port)
|
c.connect(self.server_controller.hostname, self.server_controller.port)
|
||||||
c.sendLine("NICK chkNS")
|
c.sendLine("NICK chkNS")
|
||||||
c.sendLine("USER chk chk chk chk")
|
c.sendLine("USER chk chk chk chk")
|
||||||
|
time.sleep(self.server_controller.sync_sleep_time)
|
||||||
for msg in c.getMessages(synchronize=False):
|
for msg in c.getMessages(synchronize=False):
|
||||||
if msg.command == "PING":
|
if msg.command == "PING":
|
||||||
# Hi Unreal
|
# Hi Unreal
|
||||||
|
@ -585,9 +585,13 @@ class BaseServerTestCase(
|
|||||||
del self.clients[name]
|
del self.clients[name]
|
||||||
|
|
||||||
def getMessages(self, client: TClientName, **kwargs: Any) -> List[Message]:
|
def getMessages(self, client: TClientName, **kwargs: Any) -> List[Message]:
|
||||||
|
if kwargs.get("synchronize", True):
|
||||||
|
time.sleep(self.controller.sync_sleep_time)
|
||||||
return self.clients[client].getMessages(**kwargs)
|
return self.clients[client].getMessages(**kwargs)
|
||||||
|
|
||||||
def getMessage(self, client: TClientName, **kwargs: Any) -> Message:
|
def getMessage(self, client: TClientName, **kwargs: Any) -> Message:
|
||||||
|
if kwargs.get("synchronize", True):
|
||||||
|
time.sleep(self.controller.sync_sleep_time)
|
||||||
return self.clients[client].getMessage(**kwargs)
|
return self.clients[client].getMessage(**kwargs)
|
||||||
|
|
||||||
def getRegistrationMessage(self, client: TClientName) -> Message:
|
def getRegistrationMessage(self, client: TClientName) -> Message:
|
||||||
|
@ -211,9 +211,6 @@ class ErgoController(BaseServerController, DirectoryBasedController):
|
|||||||
username: str,
|
username: str,
|
||||||
password: Optional[str] = None,
|
password: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
# XXX: Move this somewhere else when
|
|
||||||
# https://github.com/ircv3/ircv3-specifications/pull/152 becomes
|
|
||||||
# part of the specification
|
|
||||||
if not case.run_services:
|
if not case.run_services:
|
||||||
# Ergo does not actually need this, but other controllers do, so we
|
# Ergo does not actually need this, but other controllers do, so we
|
||||||
# are checking it here as well for tests that aren't tested with other
|
# are checking it here as well for tests that aren't tested with other
|
||||||
|
481
irctest/controllers/sable.py
Normal file
481
irctest/controllers/sable.py
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
from typing import Optional, Type
|
||||||
|
|
||||||
|
from irctest.basecontrollers import (
|
||||||
|
BaseServerController,
|
||||||
|
BaseServicesController,
|
||||||
|
DirectoryBasedController,
|
||||||
|
NotImplementedByController,
|
||||||
|
)
|
||||||
|
from irctest.cases import BaseServerTestCase
|
||||||
|
from irctest.exceptions import NoMessageException
|
||||||
|
from irctest.patma import ANYSTR
|
||||||
|
|
||||||
|
GEN_CERTS = """
|
||||||
|
mkdir -p useless_openssl_data/
|
||||||
|
|
||||||
|
cat > openssl.cnf <<EOF
|
||||||
|
[ ca ]
|
||||||
|
default_ca = CA_default # The default ca section
|
||||||
|
|
||||||
|
[ CA_default ]
|
||||||
|
new_certs_dir = useless_openssl_data/
|
||||||
|
database = useless_openssl_data/db
|
||||||
|
policy = policy_anything
|
||||||
|
serial = useless_openssl_data/serial
|
||||||
|
copy_extensions = copy
|
||||||
|
email_in_dn = no
|
||||||
|
rand_serial = no
|
||||||
|
|
||||||
|
[ policy_anything ]
|
||||||
|
countryName = optional
|
||||||
|
stateOrProvinceName = optional
|
||||||
|
localityName = optional
|
||||||
|
organizationName = optional
|
||||||
|
organizationalUnitName = optional
|
||||||
|
commonName = supplied
|
||||||
|
emailAddress = optional
|
||||||
|
|
||||||
|
[ usr_cert ]
|
||||||
|
subjectAltName=subject:copy
|
||||||
|
EOF
|
||||||
|
|
||||||
|
rm -f useless_openssl_data/db
|
||||||
|
touch useless_openssl_data/db
|
||||||
|
echo 01 > useless_openssl_data/serial
|
||||||
|
|
||||||
|
# Generate CA
|
||||||
|
openssl req -x509 -nodes -newkey rsa:2048 -batch \
|
||||||
|
-subj "/CN=Test CA" \
|
||||||
|
-outform PEM -out ca_cert.pem \
|
||||||
|
-keyout ca_cert.key
|
||||||
|
|
||||||
|
for server in $*; do
|
||||||
|
openssl genrsa -traditional \
|
||||||
|
-out $server.key \
|
||||||
|
2048
|
||||||
|
openssl req -nodes -batch -new \
|
||||||
|
-addext "subjectAltName = DNS:$server" \
|
||||||
|
-key $server.key \
|
||||||
|
-outform PEM -out server_$server.req
|
||||||
|
openssl ca -config openssl.cnf -days 3650 -md sha512 -batch \
|
||||||
|
-subj /CN=$server \
|
||||||
|
-keyfile ca_cert.key -cert ca_cert.pem \
|
||||||
|
-in server_$server.req \
|
||||||
|
-out $server.pem
|
||||||
|
openssl x509 -sha1 -in $server.pem -fingerprint -noout \
|
||||||
|
| sed "s/.*=//" | sed "s/://g" | tr '[:upper:]' '[:lower:]' > $server.pem.sha1
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -r useless_openssl_data/
|
||||||
|
"""
|
||||||
|
|
||||||
|
_certs_dir = None
|
||||||
|
|
||||||
|
|
||||||
|
def certs_dir() -> Path:
|
||||||
|
global _certs_dir
|
||||||
|
if _certs_dir is None:
|
||||||
|
certs_dir = tempfile.TemporaryDirectory()
|
||||||
|
(Path(certs_dir.name) / "gen_certs.sh").write_text(GEN_CERTS)
|
||||||
|
subprocess.run(
|
||||||
|
["bash", "gen_certs.sh", "My.Little.Server", "My.Little.Services"],
|
||||||
|
cwd=certs_dir.name,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
_certs_dir = certs_dir
|
||||||
|
return Path(_certs_dir.name)
|
||||||
|
|
||||||
|
|
||||||
|
NETWORK_CONFIG = """
|
||||||
|
{
|
||||||
|
"fanout": 1,
|
||||||
|
"ca_file": "%(certs_dir)s/ca_cert.pem",
|
||||||
|
|
||||||
|
"peers": [
|
||||||
|
{ "name": "My.Little.Services", "address": "%(services_hostname)s:%(services_port)s", "fingerprint": "%(services_cert_sha1)s" },
|
||||||
|
{ "name": "My.Little.Server", "address": "%(server1_hostname)s:%(server1_port)s", "fingerprint": "%(server1_cert_sha1)s" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
NETWORK_CONFIG_CONFIG = """
|
||||||
|
{
|
||||||
|
"opers": [
|
||||||
|
{
|
||||||
|
"name": "operuser",
|
||||||
|
// echo -n "operpassword" | openssl passwd -6 -stdin
|
||||||
|
"hash": "$6$z5yA.OfGliDoi/R2$BgSsguS6bxAsPSCygDisgDw5JZuo5.88eU3Hyc7/4OaNpeKIxWGjOggeHzOl0xLiZg1vfwxXjOTFN14wG5vNI."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"alias_users": [
|
||||||
|
{
|
||||||
|
"nick": "ChanServ",
|
||||||
|
"user": "ChanServ",
|
||||||
|
"host": "services.",
|
||||||
|
"realname": "Channel services compatibility layer",
|
||||||
|
"command_alias": "CS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nick": "NickServ",
|
||||||
|
"user": "NickServ",
|
||||||
|
"host": "services.",
|
||||||
|
"realname": "Account services compatibility layer",
|
||||||
|
"command_alias": "NS"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"default_roles": {
|
||||||
|
"builtin:op": [
|
||||||
|
"always_send",
|
||||||
|
"op_self", "op_grant", "voice_self", "voice_grant",
|
||||||
|
"receive_op", "receive_voice", "receive_opmod",
|
||||||
|
"topic", "kick", "set_simple_mode", "set_key",
|
||||||
|
"rename",
|
||||||
|
"ban_view", "ban_add", "ban_remove_any",
|
||||||
|
"quiet_view", "quiet_add", "quiet_remove_any",
|
||||||
|
"exempt_view", "exempt_add", "exempt_remove_any",
|
||||||
|
"invite_self", "invite_other",
|
||||||
|
"invex_view", "invex_add", "invex_remove_any"
|
||||||
|
],
|
||||||
|
"builtin:voice": [
|
||||||
|
"always_send",
|
||||||
|
"voice_self",
|
||||||
|
"receive_voice",
|
||||||
|
"ban_view", "quiet_view"
|
||||||
|
],
|
||||||
|
"builtin:all": [
|
||||||
|
"ban_view", "quiet_view"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"debug_mode": true
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
SERVER_CONFIG = """
|
||||||
|
{
|
||||||
|
"server_id": 1,
|
||||||
|
"server_name": "My.Little.Server",
|
||||||
|
|
||||||
|
"management": {
|
||||||
|
"address": "%(server1_management_hostname)s:%(server1_management_port)s",
|
||||||
|
"client_ca": "%(certs_dir)s/ca_cert.pem",
|
||||||
|
"authorised_fingerprints": [
|
||||||
|
{ "name": "user1", "fingerprint": "435bc6db9f22e84ba5d9652432154617c9509370" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
"server": {
|
||||||
|
"listeners": [
|
||||||
|
{ "address": "%(c2s_hostname)s:%(c2s_port)s" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
"event_log": {
|
||||||
|
"event_expiry": 300, // five minutes, for local testing
|
||||||
|
},
|
||||||
|
|
||||||
|
"tls_config": {
|
||||||
|
"key_file": "%(certs_dir)s/My.Little.Server.key",
|
||||||
|
"cert_file": "%(certs_dir)s/My.Little.Server.pem",
|
||||||
|
},
|
||||||
|
|
||||||
|
"node_config": {
|
||||||
|
"listen_addr": "%(server1_hostname)s:%(server1_port)s",
|
||||||
|
"cert_file": "%(certs_dir)s/My.Little.Server.pem",
|
||||||
|
"key_file": "%(certs_dir)s/My.Little.Server.key",
|
||||||
|
},
|
||||||
|
|
||||||
|
"log": {
|
||||||
|
"dir": "log/server1/",
|
||||||
|
|
||||||
|
"module-levels": {
|
||||||
|
"": "debug",
|
||||||
|
"sable_ircd": "trace",
|
||||||
|
},
|
||||||
|
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"target": "stdout",
|
||||||
|
"level": "trace",
|
||||||
|
"modules": [ "sable", "audit", "client_listener" ],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
SERVICES_CONFIG = """
|
||||||
|
{
|
||||||
|
"server_id": 99,
|
||||||
|
"server_name": "My.Little.Services",
|
||||||
|
|
||||||
|
"management": {
|
||||||
|
"address": "%(services_management_hostname)s:%(services_management_port)s",
|
||||||
|
"client_ca": "%(certs_dir)s/ca_cert.pem",
|
||||||
|
"authorised_fingerprints": [
|
||||||
|
{ "name": "user1", "fingerprint": "435bc6db9f22e84ba5d9652432154617c9509370" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"server": {
|
||||||
|
"database": "test_database.json",
|
||||||
|
"default_roles": {
|
||||||
|
"builtin:founder": [
|
||||||
|
"founder", "access_view", "access_edit", "role_view", "role_edit",
|
||||||
|
"op_self", "op_grant",
|
||||||
|
"voice_self", "voice_grant",
|
||||||
|
"always_send",
|
||||||
|
"invite_self", "invite_other",
|
||||||
|
"receive_op", "receive_voice", "receive_opmod",
|
||||||
|
"topic", "kick", "set_simple_mode", "set_key",
|
||||||
|
"rename",
|
||||||
|
"ban_view", "ban_add", "ban_remove_any",
|
||||||
|
"quiet_view", "quiet_add", "quiet_remove_any",
|
||||||
|
"exempt_view", "exempt_add", "exempt_remove_any",
|
||||||
|
"invex_view", "invex_add", "invex_remove_any"
|
||||||
|
],
|
||||||
|
"builtin:op": [
|
||||||
|
"always_send",
|
||||||
|
"receive_op", "receive_voice", "receive_opmod",
|
||||||
|
"topic", "kick", "set_simple_mode", "set_key",
|
||||||
|
"rename",
|
||||||
|
"ban_view", "ban_add", "ban_remove_any",
|
||||||
|
"quiet_view", "quiet_add", "quiet_remove_any",
|
||||||
|
"exempt_view", "exempt_add", "exempt_remove_any",
|
||||||
|
"invex_view", "invex_add", "invex_remove_any"
|
||||||
|
],
|
||||||
|
"builtin:voice": [
|
||||||
|
"always_send", "voice_self", "receive_voice"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"event_log": {
|
||||||
|
"event_expiry": 300, // five minutes, for local testing
|
||||||
|
},
|
||||||
|
|
||||||
|
"tls_config": {
|
||||||
|
"key_file": "%(certs_dir)s/My.Little.Services.key",
|
||||||
|
"cert_file": "%(certs_dir)s/My.Little.Services.pem"
|
||||||
|
},
|
||||||
|
|
||||||
|
"node_config": {
|
||||||
|
"listen_addr": "%(services_hostname)s:%(services_port)s",
|
||||||
|
"cert_file": "%(certs_dir)s/My.Little.Services.pem",
|
||||||
|
"key_file": "%(certs_dir)s/My.Little.Services.key"
|
||||||
|
},
|
||||||
|
|
||||||
|
"log": {
|
||||||
|
"dir": "log/services/",
|
||||||
|
|
||||||
|
"module-levels": {
|
||||||
|
"": "debug"
|
||||||
|
},
|
||||||
|
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"target": "stdout",
|
||||||
|
"level": "debug",
|
||||||
|
"modules": [ "sable_services" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class SableController(BaseServerController, DirectoryBasedController):
|
||||||
|
software_name = "Sable"
|
||||||
|
supported_sasl_mechanisms = {"PLAIN"}
|
||||||
|
sync_sleep_time = 0.1
|
||||||
|
"""Sable processes commands very quickly, but responses for commands changing the
|
||||||
|
state may be sent after later commands for messages which don't."""
|
||||||
|
|
||||||
|
def run(
|
||||||
|
self,
|
||||||
|
hostname: str,
|
||||||
|
port: int,
|
||||||
|
*,
|
||||||
|
password: Optional[str],
|
||||||
|
ssl: bool,
|
||||||
|
run_services: bool,
|
||||||
|
faketime: Optional[str],
|
||||||
|
) -> None:
|
||||||
|
if password is not None:
|
||||||
|
raise NotImplementedByController("PASS command")
|
||||||
|
if ssl:
|
||||||
|
raise NotImplementedByController("SSL")
|
||||||
|
assert self.proc is None
|
||||||
|
self.port = port
|
||||||
|
self.create_config()
|
||||||
|
|
||||||
|
assert self.directory
|
||||||
|
|
||||||
|
(self.directory / "configs").mkdir()
|
||||||
|
|
||||||
|
c2s_hostname = hostname
|
||||||
|
c2s_port = port
|
||||||
|
del hostname, port
|
||||||
|
# base controller expects this to check for NickServ presence itself
|
||||||
|
self.hostname = c2s_hostname
|
||||||
|
self.port = c2s_port
|
||||||
|
|
||||||
|
(server1_hostname, server1_port) = self.get_hostname_and_port()
|
||||||
|
(services_hostname, services_port) = self.get_hostname_and_port()
|
||||||
|
|
||||||
|
# Sable requires inbound connections to match the configured hostname,
|
||||||
|
# so we can't configure 0.0.0.0
|
||||||
|
server1_hostname = services_hostname = "127.0.0.1"
|
||||||
|
|
||||||
|
(
|
||||||
|
server1_management_hostname,
|
||||||
|
server1_management_port,
|
||||||
|
) = self.get_hostname_and_port()
|
||||||
|
(
|
||||||
|
services_management_hostname,
|
||||||
|
services_management_port,
|
||||||
|
) = self.get_hostname_and_port()
|
||||||
|
|
||||||
|
self.template_vars = dict(
|
||||||
|
certs_dir=certs_dir(),
|
||||||
|
c2s_hostname=c2s_hostname,
|
||||||
|
c2s_port=c2s_port,
|
||||||
|
server1_hostname=server1_hostname,
|
||||||
|
server1_port=server1_port,
|
||||||
|
server1_cert_sha1=(certs_dir() / "My.Little.Server.pem.sha1")
|
||||||
|
.read_text()
|
||||||
|
.strip(),
|
||||||
|
server1_management_hostname=server1_management_hostname,
|
||||||
|
server1_management_port=server1_management_port,
|
||||||
|
services_hostname=services_hostname,
|
||||||
|
services_port=services_port,
|
||||||
|
services_cert_sha1=(certs_dir() / "My.Little.Services.pem.sha1")
|
||||||
|
.read_text()
|
||||||
|
.strip(),
|
||||||
|
services_management_hostname=services_management_hostname,
|
||||||
|
services_management_port=services_management_port,
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.open_file("configs/network.conf") as fd:
|
||||||
|
fd.write(NETWORK_CONFIG % self.template_vars)
|
||||||
|
with self.open_file("configs/network_config.conf") as fd:
|
||||||
|
fd.write(NETWORK_CONFIG_CONFIG % self.template_vars)
|
||||||
|
with self.open_file("configs/server1.conf") as fd:
|
||||||
|
fd.write(SERVER_CONFIG % self.template_vars)
|
||||||
|
|
||||||
|
if faketime and shutil.which("faketime"):
|
||||||
|
faketime_cmd = ["faketime", "-f", faketime]
|
||||||
|
self.faketime_enabled = True
|
||||||
|
else:
|
||||||
|
faketime_cmd = []
|
||||||
|
|
||||||
|
self.proc = subprocess.Popen(
|
||||||
|
[
|
||||||
|
*faketime_cmd,
|
||||||
|
"sable_ircd",
|
||||||
|
"--foreground",
|
||||||
|
"--server-conf",
|
||||||
|
self.directory / "configs/server1.conf",
|
||||||
|
"--network-conf",
|
||||||
|
self.directory / "configs/network.conf",
|
||||||
|
"--bootstrap-network",
|
||||||
|
self.directory / "configs/network_config.conf",
|
||||||
|
],
|
||||||
|
cwd=self.directory,
|
||||||
|
preexec_fn=os.setsid,
|
||||||
|
)
|
||||||
|
self.pgroup_id = os.getpgid(self.proc.pid)
|
||||||
|
|
||||||
|
if run_services:
|
||||||
|
self.services_controller = SableServicesController(self.test_config, self)
|
||||||
|
self.services_controller.run(
|
||||||
|
protocol="sable",
|
||||||
|
server_hostname=services_hostname,
|
||||||
|
server_port=services_port,
|
||||||
|
)
|
||||||
|
|
||||||
|
def kill_proc(self) -> None:
|
||||||
|
os.killpg(self.pgroup_id, signal.SIGKILL)
|
||||||
|
super().kill_proc()
|
||||||
|
|
||||||
|
def registerUser(
|
||||||
|
self,
|
||||||
|
case: BaseServerTestCase, # type: ignore
|
||||||
|
username: str,
|
||||||
|
password: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
|
# XXX: Move this somewhere else when
|
||||||
|
# https://github.com/ircv3/ircv3-specifications/pull/152 becomes
|
||||||
|
# part of the specification
|
||||||
|
if not case.run_services:
|
||||||
|
raise ValueError(
|
||||||
|
"Attempted to register a nick, but `run_services` it not True."
|
||||||
|
)
|
||||||
|
assert password
|
||||||
|
client = case.addClient(show_io=True)
|
||||||
|
case.sendLine(client, "NICK " + username)
|
||||||
|
case.sendLine(client, "USER r e g :user")
|
||||||
|
while case.getRegistrationMessage(client).command != "001":
|
||||||
|
pass
|
||||||
|
case.getMessages(client)
|
||||||
|
case.sendLine(
|
||||||
|
client,
|
||||||
|
f"REGISTER * * {password}",
|
||||||
|
)
|
||||||
|
for _ in range(100):
|
||||||
|
time.sleep(0.1)
|
||||||
|
try:
|
||||||
|
msg = case.getMessage(client)
|
||||||
|
except NoMessageException:
|
||||||
|
continue
|
||||||
|
case.assertMessageMatch(
|
||||||
|
msg, command="REGISTER", params=["SUCCESS", username, ANYSTR]
|
||||||
|
)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise NoMessageException()
|
||||||
|
case.sendLine(client, "QUIT")
|
||||||
|
case.assertDisconnected(client)
|
||||||
|
|
||||||
|
|
||||||
|
class SableServicesController(BaseServicesController):
|
||||||
|
server_controller: SableController
|
||||||
|
software_name = "Sable Services"
|
||||||
|
|
||||||
|
def run(self, protocol: str, server_hostname: str, server_port: int) -> None:
|
||||||
|
assert protocol == "sable"
|
||||||
|
assert self.server_controller.directory is not None
|
||||||
|
|
||||||
|
with self.server_controller.open_file("configs/services.conf") as fd:
|
||||||
|
fd.write(SERVICES_CONFIG % self.server_controller.template_vars)
|
||||||
|
|
||||||
|
self.proc = subprocess.Popen(
|
||||||
|
[
|
||||||
|
"sable_services",
|
||||||
|
"--foreground",
|
||||||
|
"--server-conf",
|
||||||
|
self.server_controller.directory / "configs/services.conf",
|
||||||
|
"--network-conf",
|
||||||
|
self.server_controller.directory / "configs/network.conf",
|
||||||
|
],
|
||||||
|
cwd=self.server_controller.directory,
|
||||||
|
preexec_fn=os.setsid,
|
||||||
|
)
|
||||||
|
self.pgroup_id = os.getpgid(self.proc.pid)
|
||||||
|
|
||||||
|
def kill_proc(self) -> None:
|
||||||
|
os.killpg(self.pgroup_id, signal.SIGKILL)
|
||||||
|
super().kill_proc()
|
||||||
|
|
||||||
|
|
||||||
|
def get_irctest_controller_class() -> Type[SableController]:
|
||||||
|
return SableController
|
@ -13,7 +13,7 @@ def ircv3_timestamp_to_unixtime(timestamp: str) -> float:
|
|||||||
|
|
||||||
|
|
||||||
def random_name(base: str) -> str:
|
def random_name(base: str) -> str:
|
||||||
return base + "-" + secrets.token_hex(8)
|
return base + "-" + secrets.token_hex(5)
|
||||||
|
|
||||||
|
|
||||||
def find_hostname_and_port() -> Tuple[str, int]:
|
def find_hostname_and_port() -> Tuple[str, int]:
|
||||||
|
@ -56,6 +56,10 @@ class CapTestCase(cases.BaseServerTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@cases.mark_specifications("IRCv3")
|
@cases.mark_specifications("IRCv3")
|
||||||
|
@cases.xfailIfSoftware(
|
||||||
|
["Sable"],
|
||||||
|
"does not support multi-prefix",
|
||||||
|
)
|
||||||
def testReqOne(self):
|
def testReqOne(self):
|
||||||
"""Tests requesting a single capability"""
|
"""Tests requesting a single capability"""
|
||||||
self.addClient(1)
|
self.addClient(1)
|
||||||
@ -89,8 +93,8 @@ class CapTestCase(cases.BaseServerTestCase):
|
|||||||
|
|
||||||
@cases.mark_specifications("IRCv3")
|
@cases.mark_specifications("IRCv3")
|
||||||
@cases.xfailIfSoftware(
|
@cases.xfailIfSoftware(
|
||||||
["ngIRCd"],
|
["ngIRCd", "Sable"],
|
||||||
"ngIRCd does not support userhost-in-names",
|
"does not support userhost-in-names",
|
||||||
)
|
)
|
||||||
def testReqTwo(self):
|
def testReqTwo(self):
|
||||||
"""Tests requesting two capabilities at once"""
|
"""Tests requesting two capabilities at once"""
|
||||||
@ -131,8 +135,8 @@ class CapTestCase(cases.BaseServerTestCase):
|
|||||||
|
|
||||||
@cases.mark_specifications("IRCv3")
|
@cases.mark_specifications("IRCv3")
|
||||||
@cases.xfailIfSoftware(
|
@cases.xfailIfSoftware(
|
||||||
["ngIRCd"],
|
["ngIRCd", "Sable"],
|
||||||
"ngIRCd does not support userhost-in-names",
|
"does not support userhost-in-names",
|
||||||
)
|
)
|
||||||
def testReqOneThenOne(self):
|
def testReqOneThenOne(self):
|
||||||
"""Tests requesting two capabilities in different messages"""
|
"""Tests requesting two capabilities in different messages"""
|
||||||
@ -183,8 +187,8 @@ class CapTestCase(cases.BaseServerTestCase):
|
|||||||
|
|
||||||
@cases.mark_specifications("IRCv3")
|
@cases.mark_specifications("IRCv3")
|
||||||
@cases.xfailIfSoftware(
|
@cases.xfailIfSoftware(
|
||||||
["ngIRCd"],
|
["ngIRCd", "Sable"],
|
||||||
"ngIRCd does not support userhost-in-names",
|
"does not support userhost-in-names",
|
||||||
)
|
)
|
||||||
def testReqPostRegistration(self):
|
def testReqPostRegistration(self):
|
||||||
"""Tests requesting more capabilities after CAP END"""
|
"""Tests requesting more capabilities after CAP END"""
|
||||||
@ -300,7 +304,8 @@ class CapTestCase(cases.BaseServerTestCase):
|
|||||||
""" # noqa
|
""" # noqa
|
||||||
self.addClient(1)
|
self.addClient(1)
|
||||||
self.sendLine(1, "CAP LS 302")
|
self.sendLine(1, "CAP LS 302")
|
||||||
self.assertIn("multi-prefix", self.getCapLs(1))
|
if "multi-prefix" not in self.getCapLs(1):
|
||||||
|
raise CapabilityNotSupported("multi-prefix")
|
||||||
self.sendLine(1, "CAP REQ :foo multi-prefix bar")
|
self.sendLine(1, "CAP REQ :foo multi-prefix bar")
|
||||||
m = self.getRegistrationMessage(1)
|
m = self.getRegistrationMessage(1)
|
||||||
self.assertMessageMatch(
|
self.assertMessageMatch(
|
||||||
|
@ -46,7 +46,7 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
|
|||||||
result = []
|
result = []
|
||||||
for msg in inner_msgs:
|
for msg in inner_msgs:
|
||||||
if (
|
if (
|
||||||
msg.command == "PRIVMSG"
|
msg.command in ("PRIVMSG", "TOPIC")
|
||||||
and batch_tag is not None
|
and batch_tag is not None
|
||||||
and msg.tags.get("batch") == batch_tag
|
and msg.tags.get("batch") == batch_tag
|
||||||
):
|
):
|
||||||
@ -220,6 +220,47 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
|
|||||||
self.validate_echo_messages(NUM_MESSAGES, echo_messages)
|
self.validate_echo_messages(NUM_MESSAGES, echo_messages)
|
||||||
self.validate_chathistory(subcommand, echo_messages, 1, chname)
|
self.validate_chathistory(subcommand, echo_messages, 1, chname)
|
||||||
|
|
||||||
|
@skip_ngircd
|
||||||
|
def testChathistoryNoEventPlayback(self):
|
||||||
|
"""Tests that non-messages don't appear in the chat history when event-playback
|
||||||
|
is not enabled."""
|
||||||
|
|
||||||
|
self.connectClient(
|
||||||
|
"bar",
|
||||||
|
capabilities=[
|
||||||
|
"message-tags",
|
||||||
|
"server-time",
|
||||||
|
"echo-message",
|
||||||
|
"batch",
|
||||||
|
"labeled-response",
|
||||||
|
"sasl",
|
||||||
|
CHATHISTORY_CAP,
|
||||||
|
],
|
||||||
|
skip_if_cap_nak=True,
|
||||||
|
)
|
||||||
|
chname = "#chan" + secrets.token_hex(12)
|
||||||
|
self.joinChannel(1, chname)
|
||||||
|
self.getMessages(1)
|
||||||
|
self.getMessages(1)
|
||||||
|
|
||||||
|
NUM_MESSAGES = 10
|
||||||
|
echo_messages = []
|
||||||
|
for i in range(NUM_MESSAGES):
|
||||||
|
self.sendLine(1, "TOPIC %s :this is topic %d" % (chname, i))
|
||||||
|
self.getMessages(1)
|
||||||
|
self.sendLine(1, "PRIVMSG %s :this is message %d" % (chname, i))
|
||||||
|
echo_messages.extend(
|
||||||
|
msg.to_history_message() for msg in self.getMessages(1)
|
||||||
|
)
|
||||||
|
time.sleep(0.002)
|
||||||
|
|
||||||
|
self.validate_echo_messages(NUM_MESSAGES, echo_messages)
|
||||||
|
self.sendLine(1, "CHATHISTORY LATEST %s * 100" % chname)
|
||||||
|
(batch_open, *messages, batch_close) = self.getMessages(1)
|
||||||
|
self.assertMessageMatch(batch_open, command="BATCH")
|
||||||
|
self.assertMessageMatch(batch_close, command="BATCH")
|
||||||
|
self.assertEqual([msg for msg in messages if msg.command != "PRIVMSG"], [])
|
||||||
|
|
||||||
@pytest.mark.parametrize("subcommand", SUBCOMMANDS)
|
@pytest.mark.parametrize("subcommand", SUBCOMMANDS)
|
||||||
@skip_ngircd
|
@skip_ngircd
|
||||||
def testChathistoryEventPlayback(self, subcommand):
|
def testChathistoryEventPlayback(self, subcommand):
|
||||||
@ -244,21 +285,27 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
|
|||||||
NUM_MESSAGES = 10
|
NUM_MESSAGES = 10
|
||||||
echo_messages = []
|
echo_messages = []
|
||||||
for i in range(NUM_MESSAGES):
|
for i in range(NUM_MESSAGES):
|
||||||
|
self.sendLine(1, "TOPIC %s :this is topic %d" % (chname, i))
|
||||||
|
echo_messages.extend(
|
||||||
|
msg.to_history_message() for msg in self.getMessages(1)
|
||||||
|
)
|
||||||
|
time.sleep(0.002)
|
||||||
|
|
||||||
self.sendLine(1, "PRIVMSG %s :this is message %d" % (chname, i))
|
self.sendLine(1, "PRIVMSG %s :this is message %d" % (chname, i))
|
||||||
echo_messages.extend(
|
echo_messages.extend(
|
||||||
msg.to_history_message() for msg in self.getMessages(1)
|
msg.to_history_message() for msg in self.getMessages(1)
|
||||||
)
|
)
|
||||||
time.sleep(0.002)
|
time.sleep(0.002)
|
||||||
|
|
||||||
self.validate_echo_messages(NUM_MESSAGES, echo_messages)
|
self.validate_echo_messages(NUM_MESSAGES * 2, echo_messages)
|
||||||
self.validate_chathistory(subcommand, echo_messages, 1, chname)
|
self.validate_chathistory(subcommand, echo_messages, 1, chname)
|
||||||
|
|
||||||
@pytest.mark.parametrize("subcommand", SUBCOMMANDS)
|
@pytest.mark.parametrize("subcommand", SUBCOMMANDS)
|
||||||
@pytest.mark.private_chathistory
|
@pytest.mark.private_chathistory
|
||||||
@skip_ngircd
|
@skip_ngircd
|
||||||
def testChathistoryDMs(self, subcommand):
|
def testChathistoryDMs(self, subcommand):
|
||||||
c1 = "foo" + secrets.token_hex(12)
|
c1 = random_name("foo")
|
||||||
c2 = "bar" + secrets.token_hex(12)
|
c2 = random_name("bar")
|
||||||
self.controller.registerUser(self, c1, "sesame1")
|
self.controller.registerUser(self, c1, "sesame1")
|
||||||
self.controller.registerUser(self, c2, "sesame2")
|
self.controller.registerUser(self, c2, "sesame2")
|
||||||
self.connectClient(
|
self.connectClient(
|
||||||
@ -313,7 +360,7 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
|
|||||||
self.validate_chathistory(subcommand, echo_messages, 1, c2)
|
self.validate_chathistory(subcommand, echo_messages, 1, c2)
|
||||||
self.validate_chathistory(subcommand, echo_messages, 2, c1)
|
self.validate_chathistory(subcommand, echo_messages, 2, c1)
|
||||||
|
|
||||||
c3 = "baz" + secrets.token_hex(12)
|
c3 = random_name("baz")
|
||||||
self.connectClient(
|
self.connectClient(
|
||||||
c3,
|
c3,
|
||||||
capabilities=[
|
capabilities=[
|
||||||
@ -583,8 +630,8 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
|
|||||||
@pytest.mark.arbitrary_client_tags
|
@pytest.mark.arbitrary_client_tags
|
||||||
@skip_ngircd
|
@skip_ngircd
|
||||||
def testChathistoryTagmsg(self):
|
def testChathistoryTagmsg(self):
|
||||||
c1 = "foo" + secrets.token_hex(12)
|
c1 = random_name("foo")
|
||||||
c2 = "bar" + secrets.token_hex(12)
|
c2 = random_name("bar")
|
||||||
chname = "#chan" + secrets.token_hex(12)
|
chname = "#chan" + secrets.token_hex(12)
|
||||||
self.controller.registerUser(self, c1, "sesame1")
|
self.controller.registerUser(self, c1, "sesame1")
|
||||||
self.controller.registerUser(self, c2, "sesame2")
|
self.controller.registerUser(self, c2, "sesame2")
|
||||||
@ -683,8 +730,8 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
|
|||||||
@skip_ngircd
|
@skip_ngircd
|
||||||
def testChathistoryDMClientOnlyTags(self):
|
def testChathistoryDMClientOnlyTags(self):
|
||||||
# regression test for Ergo #1411
|
# regression test for Ergo #1411
|
||||||
c1 = "foo" + secrets.token_hex(12)
|
c1 = random_name("foo")
|
||||||
c2 = "bar" + secrets.token_hex(12)
|
c2 = random_name("bar")
|
||||||
self.controller.registerUser(self, c1, "sesame1")
|
self.controller.registerUser(self, c1, "sesame1")
|
||||||
self.controller.registerUser(self, c2, "sesame2")
|
self.controller.registerUser(self, c2, "sesame2")
|
||||||
self.connectClient(
|
self.connectClient(
|
||||||
|
@ -32,6 +32,9 @@ class EchoMessageTestCase(cases.BaseServerTestCase):
|
|||||||
|
|
||||||
self.sendLine(1, "JOIN #chan")
|
self.sendLine(1, "JOIN #chan")
|
||||||
|
|
||||||
|
# Synchronize
|
||||||
|
self.getMessages(1)
|
||||||
|
|
||||||
if not solo:
|
if not solo:
|
||||||
self.connectClient("qux", capabilities=capabilities)
|
self.connectClient("qux", capabilities=capabilities)
|
||||||
self.sendLine(2, "JOIN #chan")
|
self.sendLine(2, "JOIN #chan")
|
||||||
|
@ -13,6 +13,7 @@ class PrivmsgTestCase(cases.BaseServerTestCase):
|
|||||||
"""<https://tools.ietf.org/html/rfc2812#section-3.3.1>"""
|
"""<https://tools.ietf.org/html/rfc2812#section-3.3.1>"""
|
||||||
self.connectClient("foo")
|
self.connectClient("foo")
|
||||||
self.sendLine(1, "JOIN #chan")
|
self.sendLine(1, "JOIN #chan")
|
||||||
|
self.getMessages(1) # synchronize
|
||||||
self.connectClient("bar")
|
self.connectClient("bar")
|
||||||
self.sendLine(2, "JOIN #chan")
|
self.sendLine(2, "JOIN #chan")
|
||||||
self.getMessages(2) # synchronize
|
self.getMessages(2) # synchronize
|
||||||
|
@ -87,7 +87,7 @@ class BaseWhoTestCase:
|
|||||||
class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
||||||
@cases.mark_specifications("Modern")
|
@cases.mark_specifications("Modern")
|
||||||
def testWhoStar(self):
|
def testWhoStar(self):
|
||||||
if self.controller.software_name == "Bahamut":
|
if self.controller.software_name in ("Bahamut", "Sable"):
|
||||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||||
|
|
||||||
self._init()
|
self._init()
|
||||||
@ -118,7 +118,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||||||
)
|
)
|
||||||
@cases.mark_specifications("Modern")
|
@cases.mark_specifications("Modern")
|
||||||
def testWhoNick(self, mask):
|
def testWhoNick(self, mask):
|
||||||
if "*" in mask and self.controller.software_name == "Bahamut":
|
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||||
|
|
||||||
self._init()
|
self._init()
|
||||||
@ -148,7 +148,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||||||
ids=["username", "realname-mask", "hostname"],
|
ids=["username", "realname-mask", "hostname"],
|
||||||
)
|
)
|
||||||
def testWhoUsernameRealName(self, mask):
|
def testWhoUsernameRealName(self, mask):
|
||||||
if "*" in mask and self.controller.software_name == "Bahamut":
|
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||||
|
|
||||||
self._init()
|
self._init()
|
||||||
@ -201,7 +201,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||||||
)
|
)
|
||||||
@cases.mark_specifications("Modern")
|
@cases.mark_specifications("Modern")
|
||||||
def testWhoNickAway(self, mask):
|
def testWhoNickAway(self, mask):
|
||||||
if "*" in mask and self.controller.software_name == "Bahamut":
|
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||||
|
|
||||||
self._init()
|
self._init()
|
||||||
@ -228,9 +228,14 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"mask", ["coolNick", "coolnick", "coolni*"], ids=["exact", "casefolded", "mask"]
|
"mask", ["coolNick", "coolnick", "coolni*"], ids=["exact", "casefolded", "mask"]
|
||||||
)
|
)
|
||||||
|
@cases.xfailIfSoftware(
|
||||||
|
["Sable"],
|
||||||
|
"Sable does not advertise oper status in WHO: "
|
||||||
|
"https://github.com/Libera-Chat/sable/pull/77",
|
||||||
|
)
|
||||||
@cases.mark_specifications("Modern")
|
@cases.mark_specifications("Modern")
|
||||||
def testWhoNickOper(self, mask):
|
def testWhoNickOper(self, mask):
|
||||||
if "*" in mask and self.controller.software_name == "Bahamut":
|
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||||
|
|
||||||
self._init()
|
self._init()
|
||||||
@ -262,9 +267,14 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"mask", ["coolNick", "coolnick", "coolni*"], ids=["exact", "casefolded", "mask"]
|
"mask", ["coolNick", "coolnick", "coolni*"], ids=["exact", "casefolded", "mask"]
|
||||||
)
|
)
|
||||||
|
@cases.xfailIfSoftware(
|
||||||
|
["Sable"],
|
||||||
|
"Sable does not advertise oper status in WHO: "
|
||||||
|
"https://github.com/Libera-Chat/sable/pull/77",
|
||||||
|
)
|
||||||
@cases.mark_specifications("Modern")
|
@cases.mark_specifications("Modern")
|
||||||
def testWhoNickAwayAndOper(self, mask):
|
def testWhoNickAwayAndOper(self, mask):
|
||||||
if "*" in mask and self.controller.software_name == "Bahamut":
|
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||||
|
|
||||||
self._init()
|
self._init()
|
||||||
@ -298,18 +308,11 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||||||
@pytest.mark.parametrize("mask", ["#chan", "#CHAN"], ids=["exact", "casefolded"])
|
@pytest.mark.parametrize("mask", ["#chan", "#CHAN"], ids=["exact", "casefolded"])
|
||||||
@cases.mark_specifications("Modern")
|
@cases.mark_specifications("Modern")
|
||||||
def testWhoChan(self, mask):
|
def testWhoChan(self, mask):
|
||||||
if "*" in mask and self.controller.software_name == "Bahamut":
|
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||||
|
|
||||||
self._init()
|
self._init()
|
||||||
|
|
||||||
self.sendLine(1, "OPER operuser operpassword")
|
|
||||||
self.assertIn(
|
|
||||||
RPL_YOUREOPER,
|
|
||||||
[m.command for m in self.getMessages(1)],
|
|
||||||
fail_msg="OPER failed",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.sendLine(1, "AWAY :be right back")
|
self.sendLine(1, "AWAY :be right back")
|
||||||
self.getMessages(1)
|
self.getMessages(1)
|
||||||
self.getMessages(2)
|
self.getMessages(2)
|
||||||
@ -335,7 +338,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||||||
StrRe(host_re),
|
StrRe(host_re),
|
||||||
"My.Little.Server",
|
"My.Little.Server",
|
||||||
"coolNick",
|
"coolNick",
|
||||||
"G*@",
|
"G@",
|
||||||
StrRe(realname_regexp(self.realname)),
|
StrRe(realname_regexp(self.realname)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -589,7 +592,7 @@ class WhoServicesTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||||||
class WhoInvisibleTestCase(cases.BaseServerTestCase):
|
class WhoInvisibleTestCase(cases.BaseServerTestCase):
|
||||||
@cases.mark_specifications("Modern")
|
@cases.mark_specifications("Modern")
|
||||||
def testWhoInvisible(self):
|
def testWhoInvisible(self):
|
||||||
if self.controller.software_name == "Bahamut":
|
if self.controller.software_name in ("Bahamut", "Sable"):
|
||||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||||
|
|
||||||
self.connectClient("evan", name="evan")
|
self.connectClient("evan", name="evan")
|
||||||
|
@ -195,18 +195,26 @@ class WhoisTestCase(_WhoisTestMixin, cases.BaseServerTestCase):
|
|||||||
|
|
||||||
self.connectClient("otherNick")
|
self.connectClient("otherNick")
|
||||||
self.getMessages(2)
|
self.getMessages(2)
|
||||||
self.sendLine(2, f"WHOIS {server} coolnick")
|
self.sendLine(2, f"WHOIS {server} {nick}")
|
||||||
messages = self.getMessages(2)
|
messages = self.getMessages(2)
|
||||||
whois_user = messages[0]
|
whois_user = messages[0]
|
||||||
self.assertEqual(whois_user.command, RPL_WHOISUSER)
|
self.assertMessageMatch(
|
||||||
|
whois_user,
|
||||||
|
command=RPL_WHOISUSER,
|
||||||
# "<client> <nick> <username> <host> * :<realname>"
|
# "<client> <nick> <username> <host> * :<realname>"
|
||||||
self.assertEqual(whois_user.params[1], nick)
|
params=[
|
||||||
self.assertIn(whois_user.params[2], ("~" + username, username))
|
"otherNick",
|
||||||
|
nick,
|
||||||
|
StrRe("~?" + username),
|
||||||
|
ANYSTR,
|
||||||
|
ANYSTR,
|
||||||
|
realname,
|
||||||
|
],
|
||||||
|
)
|
||||||
# dumb regression test for oragono/oragono#355:
|
# dumb regression test for oragono/oragono#355:
|
||||||
self.assertNotIn(
|
self.assertNotIn(
|
||||||
whois_user.params[3], [nick, username, "~" + username, realname]
|
whois_user.params[3], [nick, username, "~" + username, realname]
|
||||||
)
|
)
|
||||||
self.assertEqual(whois_user.params[5], realname)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"away,oper",
|
"away,oper",
|
||||||
|
@ -151,6 +151,7 @@ def get_test_job(*, config, test_config, test_id, version_flavor, jobs):
|
|||||||
env += (
|
env += (
|
||||||
f"PATH={software_config['prefix']}/sbin"
|
f"PATH={software_config['prefix']}/sbin"
|
||||||
f":{software_config['prefix']}/bin"
|
f":{software_config['prefix']}/bin"
|
||||||
|
f":{software_config['prefix']}"
|
||||||
f":$PATH "
|
f":$PATH "
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -250,6 +250,34 @@ software:
|
|||||||
make -j 4
|
make -j 4
|
||||||
make install
|
make install
|
||||||
|
|
||||||
|
sable:
|
||||||
|
name: Sable
|
||||||
|
repository: Libera-Chat/sable
|
||||||
|
refs:
|
||||||
|
stable: ff1179512a79eba57ca468a5f83af84ecce08a5b
|
||||||
|
release: null
|
||||||
|
devel: master
|
||||||
|
devel_release: null
|
||||||
|
path: sable
|
||||||
|
prefix: "$GITHUB_WORKSPACE/sable/target/debug"
|
||||||
|
pre_deps:
|
||||||
|
- name: Install rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
- name: Enable Cargo cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: "sable -> target"
|
||||||
|
cache-on-failure: true
|
||||||
|
- run: rustc --version
|
||||||
|
separate_build_job: false
|
||||||
|
build_script: |
|
||||||
|
cd $GITHUB_WORKSPACE/sable/
|
||||||
|
cargo build
|
||||||
|
|
||||||
snircd:
|
snircd:
|
||||||
name: snircd
|
name: snircd
|
||||||
repository: quakenet/snircd
|
repository: quakenet/snircd
|
||||||
@ -454,6 +482,9 @@ tests:
|
|||||||
nefarious:
|
nefarious:
|
||||||
software: [nefarious]
|
software: [nefarious]
|
||||||
|
|
||||||
|
sable:
|
||||||
|
software: [sable]
|
||||||
|
|
||||||
# doesn't build because it can't find liblex for some reason
|
# doesn't build because it can't find liblex for some reason
|
||||||
#snircd:
|
#snircd:
|
||||||
# software: [snircd]
|
# software: [snircd]
|
||||||
|
Reference in New Issue
Block a user