Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/actions/conversationstest-action/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
if [ -z "${1:-}" ]; then
echo "Usage: run-tests.sh <tag>" >&2
echo "Available tags: demoboot, sasl2" >&2
echo "Available tags: demoboot, sasl2, fast-rotation" >&2
exit 1
fi
INCLUDE_TAGS="$1"
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/continuous-integration-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@ jobs:
- name: sasl2
maestro-tags: sasl2
config-file: build/ci/conversations/configs/sasl2.xml
- name: fast-rotation
maestro-tags: fast-rotation
config-file: build/ci/conversations/configs/fast-rotation.xml

steps:
- name: Checkout local actions and test flows # Do this _before_ untarring the distribution, as the checkout will empty the directory prior to the checkout!
Expand Down
96 changes: 96 additions & 0 deletions build/ci/conversations/configs/fast-rotation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<jive>
<adminConsole>
<port>9090</port>
<securePort>9091</securePort>
</adminConsole>
<connectionProvider>
<className>org.jivesoftware.database.EmbeddedConnectionProvider</className>
</connectionProvider>
<autosetup>
<run>true</run>
<locale>en</locale>
<xmpp>
<auth>
<anonymous>true</anonymous>
</auth>
<domain>example.org</domain>
<fqdn>example.org</fqdn>
</xmpp>
<database>
<mode>embedded</mode>
</database>
<admin>
<email>admin@example.com</email>
<password>admin</password>
</admin>
<users>
<user1>
<username>john</username>
<password>secret</password>
<name>John Doe</name>
<email>john.doe@example.com</email>
<roster>
<item1>
<jid>jane@example.org</jid>
<nickname>Jane</nickname>
</item1>
<item2>
<jid>juan@example.org</jid>
<nickname>Juan</nickname>
</item2>
</roster>
</user1>
<user2>
<username>jane</username>
<password>secret</password>
<name>Jane Doe</name>
<email>jane.doe@example.com</email>
<roster>
<item1>
<jid>john@example.org</jid>
<nickname>John</nickname>
</item1>
<item2>
<jid>juan@example.org</jid>
<nickname>Juan</nickname>
</item2>
</roster>
</user2>
<user3>
<username>juan</username>
<password>secret</password>
<name>Juan Doe</name>
<email>juan.doe@example.com</email>
<roster>
<item1>
<jid>john@example.org</jid>
<nickname>John</nickname>
</item1>
<item2>
<jid>jane@example.org</jid>
<nickname>Jane</nickname>
</item2>
</roster>
</user3>
</users>
</autosetup>
<!-- Enable SASL2 (xmpp.auth.sasl2). TLS is required too, but is satisfied by the CI server's self-signed certificate. -->
<xmpp>
<auth>
<sasl2>true</sasl2>
</auth>
<!--
Force FAST token rotation on every reconnect: rotation happens whenever the time left
before expiry is <= the rotation threshold. Setting the threshold >= the expiry means
that's true from the moment a token is issued, without waiting on the real (multi-day)
defaults.
-->
<fast>
<token>
<expiry>1</expiry> <!-- days -->
<rotation-threshold>48</rotation-threshold> <!-- hours -->
</token>
</fast>
</xmpp>
</jive>
36 changes: 36 additions & 0 deletions build/ci/conversations/flows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ The `demoboot` flows need no extra configuration beyond launching Openfire with
For flows with a specific config, copy the matching file from `build/ci/conversations/configs/`
into your distribution's `conf/` directory as `openfire-demoboot.xml` before starting demoboot mode.

Flows are tagged by which config they need, not by feature: `fast.yaml` is tagged `sasl2`
and reuses `configs/sasl2.xml`, since FAST (XEP-0484) is only advertised as a SASL2 inline
feature and needs no config of its own — `xmpp.fast.enabled` defaults to `true`.

`fast-rotation.yaml` needs its own config (`configs/fast-rotation.xml`): FAST token rotation
happens once the time left before expiry drops to or below the rotation threshold, but the
real defaults are multi-day, so the config sets the threshold >= the expiry to make rotation
happen on every reconnect instead of waiting on the clock.

### 2. Start an Android emulator

Launch an emulator with API 34 and x86_64 architecture. With Maestro installed, you can create or
Expand Down Expand Up @@ -101,6 +110,20 @@ for the full API reference.

The `LOGCAT_TAGS` default filters to Conversations output only — override to capture additional tags if needed.

## Reusing steps across flows: subflows

Common sequences (e.g. logging in) live in `subflows/` and are pulled into a flow with `runFlow`:

```yaml
- runFlow:
file: subflows/login.yaml
```

Subflow files need an `appId` header and a `---` separator like any flow, but no `tags` -
that's what keeps them from being picked up as a standalone flow when Maestro scans the
`flows/` directory. A `runScript` path inside a subflow resolves relative to the subflow's
own directory, not the calling flow's, hence `../scripts/...` inside `subflows/*.yaml`.

## Writing regex patterns

Use `checkForLogs.js` in a flow:
Expand All @@ -115,3 +138,16 @@ Use `checkForLogs.js` in a flow:
The above returns `HTTP/200` if a log line containing "SMACK" followed later by "connected" exists.

The matched lines are available in subsequent steps as `${checkForLogs.output.matchedLines}`.

For a log line produced by work that happens asynchronously after the UI has already settled
(for example, a background reconnect), pass `MAX_ATTEMPTS`/`DELAY_MS` to poll the sidecar
instead of taking a single snapshot:

```yaml
- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: 'quick start with HT'
MAX_ATTEMPTS: 30 # default: 1 (single snapshot, no retry)
DELAY_MS: 1000 # default: 1000
```
41 changes: 3 additions & 38 deletions build/ci/conversations/flows/bind2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,11 @@ onFlowComplete:

- runScript: scripts/startSession.js

- launchApp:
clearState: true

- assertVisible: "I already have an account"

- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: initializing database.*

- tapOn: "I already have an account"
- tapOn: "More options"
- tapOn: "Settings"
- tapOn: "Connection"
- tapOn: "Show extended connection settings when setting up an account"
- tapOn: "Navigate up"
- tapOn: "Navigate up"

- tapOn: "XMPP address"
- inputText: "jane@example.org"
- tapOn: "Password"
- inputText: "secret"
- tapOn: "Hostname"
- inputText: "10.0.2.2"
- tapOn: "Next"

- assertVisible: "Accept Unknown Certificate?"
- tapOn: "Always"

- assertVisible: "Publish avatar"
- tapOn: "Skip"
- runFlow:
file: subflows/login.yaml

- runFlow:
when:
visible: "Battery optimizations enabled"
commands:
- tapOn: "Next"
- assertVisible: "Let app always run in background?"
- tapOn: "Allow"
file: subflows/dismiss-onboarding-dialogs.yaml

# Received Bind2 features
- runScript:
Expand Down Expand Up @@ -79,4 +45,3 @@ onFlowComplete:
id: "server_info_bind2"
text: "available"
- takeScreenshot: server_info

30 changes: 2 additions & 28 deletions build/ci/conversations/flows/cb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,8 @@ onFlowStart:

- runScript: scripts/startSession.js

- launchApp:
clearState: true

- assertVisible: "I already have an account"

- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: initializing database.*

- tapOn: "I already have an account"
- tapOn: "More options"
- tapOn: "Settings"
- tapOn: "Connection"
- tapOn: "Show extended connection settings when setting up an account"
- tapOn: "Navigate up"
- tapOn: "Navigate up"

- tapOn: "XMPP address"
- inputText: "jane@example.org"
- tapOn: "Password"
- inputText: "secret"
- tapOn: "Hostname"
- inputText: "10.0.2.2"
- tapOn: "Next"

- assertVisible: "Accept Unknown Certificate?"
- tapOn: "Always"
- runFlow:
file: subflows/login.yaml

- runScript:
file: scripts/checkForLogs.js
Expand Down
100 changes: 100 additions & 0 deletions build/ci/conversations/flows/fast-rotation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
appId: eu.siacs.conversations
name: FAST token rotation
tags:
- fast-rotation
onFlowStart:
- runScript: scripts/checkHealth.js
---

- runScript: scripts/startSession.js

- runFlow:
file: subflows/login.yaml

- runFlow:
file: subflows/dismiss-onboarding-dialogs.yaml

- assertVisible: "Once you start a new conversation.*"

# The initial, password-based login should obtain and cache a FAST (XEP-0484) token
- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: 'jane@example\.org: storing hashed token Mechanism\['

# A freshly issued token starts in a "new" slot, and is only promoted to "current" the first
# time it's actually used - the rotation-threshold check (see configs/fast-rotation.xml, which
# forces it to always be due) only applies to a "current" token. So it's this token's SECOND
# use, not its first, where the server should rotate it.

# Reconnect #1: first use of the initial token - promotes it, no rotation expected yet.
- runScript: scripts/startSession.js

- launchApp

- assertVisible: "Once you start a new conversation.*"

- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: 'jane@example\.org.*quick start with HT'
MAX_ATTEMPTS: 30
DELAY_MS: 1000

- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: 'jane@example\.org.*logged in \(using SASL_2\)'
MAX_ATTEMPTS: 30
DELAY_MS: 1000

# Reconnect #2: second use of that same token - now "current", so the server should rotate it.
- runScript: scripts/startSession.js

- launchApp

- assertVisible: "Once you start a new conversation.*"

- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: 'jane@example\.org.*quick start with HT'
MAX_ATTEMPTS: 30
DELAY_MS: 1000

- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: 'jane@example\.org.*logged in \(using SASL_2\)'
MAX_ATTEMPTS: 30
DELAY_MS: 1000

- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: 'jane@example\.org: storing hashed token Mechanism\['
MAX_ATTEMPTS: 30
DELAY_MS: 1000

# Reconnect #3: proves the rotated token was actually persisted and is usable - if rotation had
# silently broken (e.g. the replacement was never stored, or was stored but unusable), this
# reconnect would fail FAST and fall back to a password prompt instead.
- runScript: scripts/startSession.js

- launchApp

- assertVisible: "Once you start a new conversation.*"

- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: 'jane@example\.org.*quick start with HT'
MAX_ATTEMPTS: 30
DELAY_MS: 1000

- runScript:
file: scripts/checkForLogs.js
env:
PATTERN: 'jane@example\.org.*logged in \(using SASL_2\)'
MAX_ATTEMPTS: 30
DELAY_MS: 1000
Loading
Loading