Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c80ab70
Hurl: split tests to mirror the REST services
Fishbowler Sep 25, 2026
87c48d5
Hurl: additional System Properties tests
Fishbowler Sep 25, 2026
b2f1c8d
Hurl: additional Users tests
Fishbowler Sep 25, 2026
4fee525
Hurl: additional User Roster tests
Fishbowler Sep 25, 2026
cba6dce
Hurl: additional User Groups tests
Fishbowler Sep 25, 2026
e70c79c
Hurl: additional Sessions tests
Fishbowler Sep 25, 2026
4cfa76d
Hurl: additional Message Broadcast tests
Fishbowler Sep 25, 2026
97c5e5a
Hurl: additional User Lockouts tests
Fishbowler Sep 25, 2026
d7fb47b
Hurl: additional Security Audit Log tests
Fishbowler Sep 25, 2026
f966d0e
Hurl: make Security Audit Log tests independent of existing entries
Fishbowler Sep 25, 2026
a45fb02
Hurl: Message Archive test prerequisites
Fishbowler Sep 25, 2026
b961c75
Hurl: replace the host variable with more specific URL variables
Fishbowler Sep 25, 2026
7880ce0
Hurl: describe the intent of each test
Fishbowler Sep 25, 2026
ccbf3b4
CI: run Hurl from its container image, replacing the archived action
Fishbowler Sep 25, 2026
bc742da
Hurl: additional Chat Rooms tests
Fishbowler Sep 25, 2026
4ca901f
Hurl: additional Chat Room Affiliations tests
Fishbowler Sep 25, 2026
16f995b
Hurl: additional User vCard tests
Fishbowler Sep 25, 2026
b6dbbf5
Hurl: additional Chat Services tests
Fishbowler Sep 25, 2026
c5e372b
Hurl: disabled tests for known issues
Fishbowler Sep 25, 2026
49ff070
Hurl: document running the tests from the Hurl container image
Fishbowler Sep 25, 2026
430d2ff
Hurl: take the admin credentials from test.env
Fishbowler Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ jobs:
config: ./test/demoboot-with-additions.xml
plugin: ./restAPI.jar

- uses: gacts/install-hurl@v1

- name: Test the plugin
run: |
hurl --test --report-junit test-results.xml --variables-file test/test.env --jobs 1 test/*.hurl
docker run --rm --network host --user "$(id -u):$(id -g)" --volume "$PWD:$PWD" --workdir "$PWD" \
ghcr.io/orange-opensource/hurl:8.0.1 \
--test --report-junit test-results.xml --variables-file test/test.env --jobs 1 test/*.hurl

- name: Expose Openfire logs
uses: actions/upload-artifact@v7
Expand Down
25 changes: 23 additions & 2 deletions test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,31 @@

The tests contained in this folder are written in Hurl (see [docs](https://hurl.dev/docs/manual.html)).

Install Hurl with instructions as per the documentation.
Install Hurl with instructions as per the documentation, and run the tests from the root of the repository:

```bash
hurl --test --variables-file test/test.env --jobs 1 test/*.hurl
```

Alternatively, run Hurl from its container image, as CI does (`--network host` lets the container reach Openfire on
`localhost`):

```bash
docker run --rm --network host --user "$(id -u):$(id -g)" --volume "$PWD:$PWD" --workdir "$PWD" \
ghcr.io/orange-opensource/hurl:8.0.1 \
--test --variables-file test/test.env --jobs 1 test/*.hurl
```

Configure the Rest API:

* Enable it
* Set auth for shared key, and set the value in test.env
* Set `adminConsole.access.allow-wildcards-in-excludes` to true
* Set `adminConsole.access.allow-wildcards-in-excludes` to true

test.env defines where the tests find Openfire: `restapi_url` (the base URL of the REST API, including its version),
`adminconsole_url` and `bosh_url`, as well as the shared secret (`authkey`) and the credentials of an admin user
(`admin_username` and `admin_password`), which are used to log in to the admin console.

Some tests create XMPP client sessions by logging in over BOSH (Openfire's HTTP binding, which listens on port 7070 by
default). The BOSH endpoint is configured by the `bosh_url` variable in test.env; when Openfire runs in a container, make
sure that port 7070 is published (or override the variable, e.g. `--variable bosh_url=http://<container-ip>:7070`).
297 changes: 297 additions & 0 deletions test/chatroomaffiliations.hurl
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
# Affiliations are tested on a dedicated chat room, and a dedicated group, which are removed at the end.
POST {{restapi_url}}/chatrooms
Authorization: {{authkey}}
Content-Type: application/xml
```
<?xml version="1.0" encoding="UTF-8"?>
<chatRoom>
<roomName>ops</roomName>
<naturalName>Operations</naturalName>
<description>Affiliations test room</description>
<owners>
<owner>john@example.org</owner>
</owners>
</chatRoom>
```
HTTP 201

POST {{restapi_url}}/groups
Authorization: {{authkey}}
Content-Type: application/xml
```
<?xml version="1.0" encoding="UTF-8"?>
<group>
<name>crew</name>
<description>Crew</description>
<shared>false</shared>
<admins/>
<members>
<member>john</member>
<member>jane</member>
</members>
</group>
```
HTTP 201


# TEST: The owners that a chat room was created with are listed
GET {{restapi_url}}/chatrooms/ops/owners
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/owners/owner" count == 1
xpath "string(/owners/owner)" == "john@example.org"

# TEST: A new chat room has no admins, members or outcasts
GET {{restapi_url}}/chatrooms/ops/admins
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/admins/admin" count == 0

GET {{restapi_url}}/chatrooms/ops/members
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/members/member" count == 0

GET {{restapi_url}}/chatrooms/ops/outcasts
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/outcasts/outcast" count == 0

# TEST: Add an affiliation by JID
POST {{restapi_url}}/chatrooms/ops/members/jane@example.org
Authorization: {{authkey}}
HTTP 201

GET {{restapi_url}}/chatrooms/ops/members
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/members/member" count == 1
xpath "string(/members/member)" == "jane@example.org"

# TEST: A user has only one affiliation, so adding another replaces the previous one
POST {{restapi_url}}/chatrooms/ops/admins/jane@example.org
Authorization: {{authkey}}
HTTP 201

GET {{restapi_url}}/chatrooms/ops/admins
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/admins/admin" count == 1
xpath "string(/admins/admin)" == "jane@example.org"

GET {{restapi_url}}/chatrooms/ops/members
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/members/member" count == 0

# TEST: Add an affiliation by username, which is resolved against the local domain
POST {{restapi_url}}/chatrooms/ops/outcasts/mallory
Authorization: {{authkey}}
HTTP 201

GET {{restapi_url}}/chatrooms/ops/outcasts
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/outcasts/outcast" count == 1
xpath "string(/outcasts/outcast)" == "mallory@example.org"

# TEST: Remove an affiliation
DELETE {{restapi_url}}/chatrooms/ops/admins/jane@example.org
Authorization: {{authkey}}
HTTP 200

GET {{restapi_url}}/chatrooms/ops/admins
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/admins/admin" count == 0

DELETE {{restapi_url}}/chatrooms/ops/outcasts/mallory@example.org
Authorization: {{authkey}}
HTTP 200

GET {{restapi_url}}/chatrooms/ops/outcasts
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/outcasts/outcast" count == 0

# TEST: Removing the last owner of a chat room is a conflict
DELETE {{restapi_url}}/chatrooms/ops/owners/john@example.org
Authorization: {{authkey}}
HTTP 409
[Asserts]
xpath "string(/error/exception)" == "NotAllowedException"

GET {{restapi_url}}/chatrooms/ops/owners
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "string(/owners/owner)" == "john@example.org"

# TEST: Affiliate the members of a group, which the room lists as a group (or its members, when expanded)
POST {{restapi_url}}/chatrooms/ops/members/group/crew
Authorization: {{authkey}}
HTTP 201

GET {{restapi_url}}/chatrooms/ops
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/chatRoom/memberGroups/memberGroup" count == 1
xpath "string(/chatRoom/memberGroups/memberGroup)" == "crew"
xpath "/chatRoom/members/member" count == 0

GET {{restapi_url}}/chatrooms/ops?expandGroups=true
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/chatRoom/members[member='john@example.org']" exists
xpath "/chatRoom/members[member='jane@example.org']" exists

# TEST: Remove the affiliation of the members of a group
DELETE {{restapi_url}}/chatrooms/ops/members/group/crew
Authorization: {{authkey}}
HTTP 200

GET {{restapi_url}}/chatrooms/ops
Authorization: {{authkey}}
HTTP 200
[Asserts]
xpath "/chatRoom/memberGroups/memberGroup" count == 0

# TEST: Getting the affiliations of a chat room that does not exist fails
GET {{restapi_url}}/chatrooms/nonexistent/members
Authorization: {{authkey}}
HTTP 404
[Asserts]
xpath "string(/error/exception)" == "RoomNotFoundException"
xpath "string(/error/resource)" == "nonexistent"

# TEST: An affiliation type that does not exist is not found
# The API documents a 400 for this, but the URL does not match any endpoint, as the affiliation type is part of the
# path. See https://github.com/igniterealtime/openfire-restAPI-plugin/issues/267
GET {{restapi_url}}/chatrooms/ops/visitors
Authorization: {{authkey}}
HTTP 404

# TEST: Replace the affiliations of a type with a collection
# Disabled until https://github.com/igniterealtime/openfire-restAPI-plugin/issues/163 is fixed.
PUT {{restapi_url}}/chatrooms/ops/members
Authorization: {{authkey}}
Content-Type: application/xml
[Options]
skip: true
```
<?xml version="1.0" encoding="UTF-8"?>
<members>
<member>jane@example.org</member>
<member>mallory@example.org</member>
</members>
```
HTTP 201

GET {{restapi_url}}/chatrooms/ops/members
Authorization: {{authkey}}
[Options]
skip: true
HTTP 200
[Asserts]
xpath "/members/member" count == 2

PUT {{restapi_url}}/chatrooms/ops/members
Authorization: {{authkey}}
Content-Type: application/xml
[Options]
skip: true
```
<?xml version="1.0" encoding="UTF-8"?>
<members>
<member>jane@example.org</member>
</members>
```
HTTP 201

GET {{restapi_url}}/chatrooms/ops/members
Authorization: {{authkey}}
[Options]
skip: true
HTTP 200
[Asserts]
xpath "/members/member" count == 1
xpath "string(/members/member)" == "jane@example.org"

DELETE {{restapi_url}}/chatrooms/ops/members/jane@example.org
Authorization: {{authkey}}
[Options]
skip: true
HTTP 200

# TEST: Add a collection of affiliations
# Disabled until https://github.com/igniterealtime/openfire-restAPI-plugin/issues/163 is fixed.
POST {{restapi_url}}/chatrooms/ops/outcasts
Authorization: {{authkey}}
Content-Type: application/xml
[Options]
skip: true
```
<?xml version="1.0" encoding="UTF-8"?>
<outcasts>
<outcast>eve@example.org</outcast>
<outcast>mallory@example.org</outcast>
</outcasts>
```
HTTP 201

GET {{restapi_url}}/chatrooms/ops/outcasts
Authorization: {{authkey}}
[Options]
skip: true
HTTP 200
[Asserts]
xpath "/outcasts/outcast" count == 2

DELETE {{restapi_url}}/chatrooms/ops/outcasts/eve@example.org
Authorization: {{authkey}}
[Options]
skip: true
HTTP 200

DELETE {{restapi_url}}/chatrooms/ops/outcasts/mallory@example.org
Authorization: {{authkey}}
[Options]
skip: true
HTTP 200

# TEST: Affiliating the members of a group that does not exist fails
# Disabled until https://github.com/igniterealtime/openfire-restAPI-plugin/issues/286 is fixed.
POST {{restapi_url}}/chatrooms/ops/members/group/nonexistent
Authorization: {{authkey}}
[Options]
skip: true
HTTP 404

GET {{restapi_url}}/chatrooms/ops/members
Authorization: {{authkey}}
[Options]
skip: true
HTTP 200
[Asserts]
xpath "/members[member='nonexistent@example.org']" not exists


DELETE {{restapi_url}}/chatrooms/ops
Authorization: {{authkey}}
HTTP 200

DELETE {{restapi_url}}/groups/crew
Authorization: {{authkey}}
HTTP 200
Loading
Loading