The second scenario, chosen to be a bridge rather than another HTTP component: bridges are roughly 40 of the 70 roles here, and validating the shape on role two is cheaper than discovering it on role forty. It found two things the first role could not. Bridges read a whole family of variables from outside themselves - matrix_bridges_relay_enabled, _encryption_enabled, _encryption_default, _msc4190_enabled, _self_sign_enabled, plus matrix_admin - all defined in matrix-base. Since every bridge reads them, they belong in shared context rather than in each bridge's scenario: molecule-shared/playbook-context.yml now carries them along with the identity and path variables the first scenario had inline. Note it is loaded through vars_files, which outranks inventory group_vars, so it is authoritative; a scenario that wants to prove one of these reaches the rendered configuration should say so explicitly. Appservices call /whoami while starting and refuse to run if the id returned is not the bot user they were configured as - mautrix-whatsapp exits 17 with "Unexpected user ID in whoami call". The shared stub now takes the id it should claim to be. Expect every bridge and bot to need this. What the scenario proves: the bridge starts and stays up, opens its appservice port, its rendered config and appservice registration carry the scenario's tokens and bot user, it created its sqlite database under the role's data path as the role's uid, and it runs the version the role pins. It does not bridge anything and never will. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>pull/5575/head
| @@ -0,0 +1,56 @@ | |||||
| # SPDX-FileCopyrightText: 2026 Slavi Pantaleev | |||||
| # | |||||
| # SPDX-License-Identifier: AGPL-3.0-or-later | |||||
| --- | |||||
| # The variables a role here reads from its surroundings rather than from its own | |||||
| # defaults. In a real run `matrix-base` and `group_vars/matrix_servers` provide | |||||
| # them; in a scenario they have to come from somewhere, and including | |||||
| # `matrix-base` itself does far more than a role scenario needs. | |||||
| # | |||||
| # Include from a scenario's prepare.yml, converge.yml and verify.yml: | |||||
| # | |||||
| # vars_files: | |||||
| # - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml" | |||||
| # | |||||
| # A scenario can override any of these in its own group_vars - that is the point | |||||
| # of testing a role with values it would not have chosen for itself. | |||||
| # | |||||
| # Keep this to variables that come from OUTSIDE the role under test. Anything the | |||||
| # role defines belongs in the scenario, not here. | |||||
| # --- Identity and paths (matrix-base) -------------------------------------- | |||||
| matrix_base_data_path: /matrix | |||||
| matrix_domain: molecule.local | |||||
| matrix_user_name: matrix | |||||
| matrix_group_name: matrix | |||||
| # Deliberately not 1000: the base images already have a user there, so a distinct | |||||
| # id is what proves a role used the one it was given rather than coinciding with | |||||
| # the image's own. | |||||
| matrix_user_uid: 1234 | |||||
| matrix_user_gid: 1234 | |||||
| # Empty in the playbook's own defaults too. Components that would invite an | |||||
| # administrator into a room skip doing so when it is empty, which is what a | |||||
| # scenario wants. | |||||
| matrix_admin: '' | |||||
| # --- Bridge-wide switches (matrix-base) ------------------------------------ | |||||
| # | |||||
| # Every bridge role reads these, so they live here rather than in each bridge's | |||||
| # scenario. The values match the playbook's own defaults: encryption off, no | |||||
| # relay, no MSC4190. A bridge scenario that wants to prove one of these reaches | |||||
| # the rendered configuration should override it in its own group_vars. | |||||
| matrix_bridges_encryption_enabled: false | |||||
| matrix_bridges_encryption_default: false | |||||
| matrix_bridges_msc4190_enabled: false | |||||
| matrix_bridges_self_sign_enabled: false | |||||
| matrix_bridges_relay_enabled: false | |||||
| matrix_bridges_exposure_enabled: true | |||||
| matrix_bridges_exposure_hostname: molecule.local | |||||
| matrix_bridges_exposure_path_prefix: /bridges | |||||
| @@ -0,0 +1,3 @@ | |||||
| SPDX-FileCopyrightText: 2026 Slavi Pantaleev | |||||
| SPDX-License-Identifier: AGPL-3.0-or-later | |||||
| @@ -50,6 +50,10 @@ | |||||
| - --network-alias={{ molecule_shared_stub_hostname | default('matrix.molecule.local') }} | - --network-alias={{ molecule_shared_stub_hostname | default('matrix.molecule.local') }} | ||||
| - --env=STUB_SERVER_NAME={{ molecule_shared_stub_server_name | default('molecule.local') }} | - --env=STUB_SERVER_NAME={{ molecule_shared_stub_server_name | default('molecule.local') }} | ||||
| - --env=STUB_JOINED_ROOMS={{ (molecule_shared_stub_joined_rooms | default([])) | join(',') }} | - --env=STUB_JOINED_ROOMS={{ (molecule_shared_stub_joined_rooms | default([])) | join(',') }} | ||||
| # Appservices call /whoami on startup and refuse to run if the id | |||||
| # returned is not the bot user they were configured as, so a scenario | |||||
| # bridging anything has to tell the stub who it should claim to be. | |||||
| - --env=STUB_USER_ID={{ molecule_shared_stub_user_id | default('@stub:' + (molecule_shared_stub_server_name | default('molecule.local'))) }} | |||||
| - --volume=/root/molecule-homeserver-stub.py:/stub.py:ro | - --volume=/root/molecule-homeserver-stub.py:/stub.py:ro | ||||
| - "{{ molecule_shared_image_python }}" | - "{{ molecule_shared_image_python }}" | ||||
| - python3 | - python3 | ||||
| @@ -11,6 +11,8 @@ | |||||
| - name: Include roles for matrix-alertmanager-receiver Molecule tests | - name: Include roles for matrix-alertmanager-receiver Molecule tests | ||||
| hosts: all | hosts: all | ||||
| become: true | become: true | ||||
| vars_files: | |||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml" | |||||
| gather_facts: true | gather_facts: true | ||||
| tasks: | tasks: | ||||
| - name: Include roles for matrix-alertmanager-receiver Molecule tests | - name: Include roles for matrix-alertmanager-receiver Molecule tests | ||||
| @@ -27,18 +27,6 @@ provisioner: | |||||
| inventory: | inventory: | ||||
| group_vars: | group_vars: | ||||
| all: | all: | ||||
| # This role lives inside the playbook, so the variables the playbook | |||||
| # would normally supply from `matrix-base` and from group_vars have to | |||||
| # be provided here instead. These are the ones the role actually reads. | |||||
| matrix_base_data_path: /matrix | |||||
| matrix_domain: molecule.local | |||||
| matrix_user_name: matrix | |||||
| matrix_group_name: matrix | |||||
| # Deliberately not 1000: the ubuntu2604 image already has a user there, | |||||
| # so a distinct id proves the role used the one it was given. | |||||
| matrix_user_uid: 1234 | |||||
| matrix_user_gid: 1234 | |||||
| matrix_alertmanager_receiver_hostname: alertmanager-receiver.molecule.local | matrix_alertmanager_receiver_hostname: alertmanager-receiver.molecule.local | ||||
| matrix_alertmanager_receiver_path_prefix: / | matrix_alertmanager_receiver_path_prefix: / | ||||
| matrix_alertmanager_receiver_container_network: matrix-alertmanager-receiver-molecule | matrix_alertmanager_receiver_container_network: matrix-alertmanager-receiver-molecule | ||||
| @@ -8,6 +8,7 @@ | |||||
| become: true | become: true | ||||
| vars_files: | vars_files: | ||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml" | - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml" | ||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml" | |||||
| gather_facts: true | gather_facts: true | ||||
| tasks: | tasks: | ||||
| - name: Ensure apt cache is updated | - name: Ensure apt cache is updated | ||||
| @@ -8,6 +8,7 @@ | |||||
| become: true | become: true | ||||
| vars_files: | vars_files: | ||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml" | - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml" | ||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml" | |||||
| gather_facts: false | gather_facts: false | ||||
| tasks: | tasks: | ||||
| @@ -0,0 +1,36 @@ | |||||
| # SPDX-FileCopyrightText: 2026 Slavi Pantaleev | |||||
| # | |||||
| # SPDX-License-Identifier: AGPL-3.0-or-later | |||||
| --- | |||||
| - name: Include roles for mautrix-whatsapp Molecule tests | |||||
| hosts: all | |||||
| become: true | |||||
| vars_files: | |||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml" | |||||
| gather_facts: true | |||||
| tasks: | |||||
| - name: Include roles for mautrix-whatsapp Molecule tests | |||||
| ansible.builtin.include_role: | |||||
| name: "{{ role_name }}" | |||||
| public: true | |||||
| loop: | |||||
| - com.devture.ansible.role.playbook_help | |||||
| - com.devture.ansible.role.systemd_docker_base | |||||
| - "custom/{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" | |||||
| loop_control: | |||||
| loop_var: role_name | |||||
| - name: Ensure mautrix-whatsapp is started | |||||
| hosts: all | |||||
| become: true | |||||
| gather_facts: false | |||||
| tasks: | |||||
| - name: Ensure systemd daemon is reloaded | |||||
| ansible.builtin.systemd_service: | |||||
| daemon_reload: true | |||||
| - name: Ensure the mautrix-whatsapp systemd service is started | |||||
| ansible.builtin.systemd_service: | |||||
| name: matrix-mautrix-whatsapp.service | |||||
| state: started | |||||
| @@ -0,0 +1,81 @@ | |||||
| # SPDX-FileCopyrightText: 2026 Slavi Pantaleev | |||||
| # | |||||
| # SPDX-License-Identifier: AGPL-3.0-or-later | |||||
| --- | |||||
| dependency: | |||||
| name: galaxy | |||||
| options: | |||||
| requirements-file: requirements.yml | |||||
| force: true | |||||
| driver: | |||||
| name: docker | |||||
| platforms: | |||||
| - name: mautrix-whatsapp-${MOLECULE_DISTRO:-ubuntu2604}-default | |||||
| image: "geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2604}-ansible:latest" | |||||
| command: ${MOLECULE_DOCKER_COMMAND:-""} | |||||
| volumes: | |||||
| - /sys/fs/cgroup:/sys/fs/cgroup:rw | |||||
| cgroupns_mode: host | |||||
| privileged: true | |||||
| pre_build_image: true | |||||
| provisioner: | |||||
| name: ansible | |||||
| config_options: | |||||
| defaults: | |||||
| callback_result_format: yaml | |||||
| inventory: | |||||
| group_vars: | |||||
| all: | |||||
| matrix_bridge_mautrix_whatsapp_container_network: mautrix-whatsapp-molecule | |||||
| # The homeserver stub prepare.yml stands up. The bridge contacts it | |||||
| # while starting; it is not a real homeserver and nothing is asserted | |||||
| # about it. | |||||
| matrix_bridge_mautrix_whatsapp_homeserver_address: http://matrix.molecule.local:8008 | |||||
| # sqlite keeps the scenario to one container. The role only requires a | |||||
| # database hostname when the engine is postgres, and testing which | |||||
| # database engine the bridge can talk to is not what this proves. | |||||
| matrix_bridge_mautrix_whatsapp_database_engine: sqlite | |||||
| # Appservice tokens. These are what the bridge and homeserver would | |||||
| # authenticate to each other with; here they only have to reach the | |||||
| # rendered configuration and the registration file. | |||||
| matrix_bridge_mautrix_whatsapp_appservice_token: molecule_as_token_4f2a91 | |||||
| matrix_bridge_mautrix_whatsapp_homeserver_token: molecule_hs_token_9b3e77 | |||||
| # Deliberately different from the role's defaults, so verify.yml can | |||||
| # tell what the role rendered apart from what the bridge would have | |||||
| # defaulted to on its own. | |||||
| matrix_bridge_mautrix_whatsapp_appservice_bot_username: molecule-whatsappbot | |||||
| matrix_bridge_mautrix_whatsapp_homeserver_domain: molecule.local | |||||
| # Traefik is not deployed here, so the labels the role would render for | |||||
| # it are switched off and their absence is asserted instead. | |||||
| matrix_bridge_mautrix_whatsapp_container_labels_traefik_enabled: false | |||||
| # verify.yml runs as its own play, where role defaults are out of scope, | |||||
| # so the paths it reads are pinned here as literals matching what the | |||||
| # role derives from matrix_base_data_path. | |||||
| matrix_bridge_mautrix_whatsapp_base_path: /matrix/mautrix-whatsapp | |||||
| matrix_bridge_mautrix_whatsapp_config_path: /matrix/mautrix-whatsapp/config | |||||
| matrix_bridge_mautrix_whatsapp_data_path: /matrix/mautrix-whatsapp/data | |||||
| env: | |||||
| # Workaround for https://github.com/ansible/molecule/issues/4391 | |||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles | |||||
| scenario: | |||||
| test_sequence: | |||||
| - dependency | |||||
| - cleanup | |||||
| - destroy | |||||
| - syntax | |||||
| - create | |||||
| - prepare | |||||
| - converge | |||||
| - idempotence | |||||
| - verify | |||||
| - cleanup | |||||
| - destroy | |||||
| verifier: | |||||
| name: ansible | |||||
| @@ -0,0 +1,81 @@ | |||||
| # SPDX-FileCopyrightText: 2026 Slavi Pantaleev | |||||
| # | |||||
| # SPDX-License-Identifier: AGPL-3.0-or-later | |||||
| --- | |||||
| - name: Prepare mautrix-whatsapp Molecule tests | |||||
| hosts: all | |||||
| become: true | |||||
| vars_files: | |||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml" | |||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml" | |||||
| gather_facts: true | |||||
| tasks: | |||||
| - name: Ensure apt cache is updated | |||||
| ansible.builtin.apt: | |||||
| update_cache: true | |||||
| cache_valid_time: 600 | |||||
| when: ansible_os_family == 'Debian' | |||||
| - name: Ensure required packages are installed | |||||
| ansible.builtin.package: | |||||
| name: | |||||
| - python3-requests | |||||
| - fuse-overlayfs | |||||
| state: present | |||||
| - name: Ensure Docker is installed | |||||
| ansible.builtin.include_role: | |||||
| name: ansible-role-docker | |||||
| vars: | |||||
| docker_daemon_options: | |||||
| storage-driver: fuse-overlayfs | |||||
| # The role's file tasks set owner/group by name and Ansible resolves those | |||||
| # through the passwd database, so they have to exist first. matrix-base | |||||
| # creates them in a real deployment. | |||||
| - name: Ensure the matrix group exists | |||||
| ansible.builtin.group: | |||||
| name: "{{ matrix_group_name }}" | |||||
| gid: "{{ matrix_user_gid }}" | |||||
| state: present | |||||
| - name: Ensure the matrix user exists | |||||
| ansible.builtin.user: | |||||
| name: "{{ matrix_user_name }}" | |||||
| uid: "{{ matrix_user_uid }}" | |||||
| group: "{{ matrix_group_name }}" | |||||
| create_home: false | |||||
| system: true | |||||
| state: present | |||||
| - name: Ensure the base data path exists | |||||
| ansible.builtin.file: | |||||
| path: "{{ matrix_base_data_path }}" | |||||
| state: directory | |||||
| owner: "{{ matrix_user_name }}" | |||||
| group: "{{ matrix_group_name }}" | |||||
| mode: "0750" | |||||
| - name: Ensure the container network the role attaches to exists | |||||
| ansible.builtin.command: | |||||
| argv: | |||||
| - docker | |||||
| - network | |||||
| - create | |||||
| - "{{ matrix_bridge_mautrix_whatsapp_container_network }}" | |||||
| register: mautrix_whatsapp_molecule_network | |||||
| changed_when: mautrix_whatsapp_molecule_network.rc == 0 | |||||
| failed_when: | |||||
| - mautrix_whatsapp_molecule_network.rc != 0 | |||||
| - "'already exists' not in mautrix_whatsapp_molecule_network.stderr" | |||||
| # The bridge contacts the homeserver as it starts. It is not being asked to | |||||
| # bridge anything - see molecule-shared/homeserver-stub.py. | |||||
| - name: Ensure the homeserver stub is running | |||||
| ansible.builtin.include_tasks: | |||||
| file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/tasks/homeserver-stub.yml" | |||||
| vars: | |||||
| molecule_shared_stub_network: "{{ matrix_bridge_mautrix_whatsapp_container_network }}" | |||||
| molecule_shared_stub_server_name: molecule.local | |||||
| molecule_shared_stub_user_id: "@{{ matrix_bridge_mautrix_whatsapp_appservice_bot_username }}:{{ matrix_bridge_mautrix_whatsapp_homeserver_domain }}" | |||||
| @@ -0,0 +1 @@ | |||||
| ../../../../../molecule-shared/requirements.yml | |||||
| @@ -0,0 +1,177 @@ | |||||
| # SPDX-FileCopyrightText: 2026 Slavi Pantaleev | |||||
| # | |||||
| # SPDX-License-Identifier: AGPL-3.0-or-later | |||||
| --- | |||||
| # What this proves: the bridge starts, reads the configuration and registration | |||||
| # the role rendered, opens its appservice port, and is the version the role | |||||
| # pins. It does NOT bridge anything - there is no WhatsApp on the other side and | |||||
| # there is deliberately never going to be one. See docs/molecule-testing.md. | |||||
| - name: Verify mautrix-whatsapp | |||||
| hosts: all | |||||
| become: true | |||||
| vars_files: | |||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml" | |||||
| - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml" | |||||
| gather_facts: false | |||||
| tasks: | |||||
| # Read from the role's own defaults rather than pinned in molecule.yml, so | |||||
| # the version assertion below compares the running image against what the | |||||
| # role ships instead of against the scenario itself. | |||||
| - name: Load the role's defaults under a separate name | |||||
| ansible.builtin.include_vars: | |||||
| file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/main.yml" | |||||
| name: mautrix_whatsapp_role_defaults | |||||
| - name: Wait for the mautrix-whatsapp service to become active | |||||
| ansible.builtin.systemd_service: | |||||
| name: matrix-mautrix-whatsapp.service | |||||
| register: mautrix_whatsapp_service | |||||
| until: mautrix_whatsapp_service.status.ActiveState == 'active' | |||||
| retries: 30 | |||||
| delay: 5 | |||||
| failed_when: false | |||||
| # `Restart=always` means a bridge crash-looping on a configuration it cannot | |||||
| # read still reports `active`, so the restart counter is checked too. It is | |||||
| # asserted `is defined` because `| int` turns a missing property into 0 and | |||||
| # would pass vacuously. | |||||
| - name: Assert the service is active and has not been restarting | |||||
| ansible.builtin.assert: | |||||
| that: | |||||
| - mautrix_whatsapp_service.status.ActiveState == 'active' | |||||
| - mautrix_whatsapp_service.status.NRestarts is defined | |||||
| - mautrix_whatsapp_service.status.NRestarts | int == 0 | |||||
| fail_msg: >- | |||||
| matrix-mautrix-whatsapp.service is | |||||
| {{ mautrix_whatsapp_service.status.ActiveState | default('unknown') }} | |||||
| after {{ mautrix_whatsapp_service.status.NRestarts | default('?') }} | |||||
| automatic restart(s) | |||||
| success_msg: "matrix-mautrix-whatsapp.service is active and has not restarted" | |||||
| # The appservice port is the bridge's own listener, the one a homeserver | |||||
| # would push transactions to. It opening at all means the bridge got through | |||||
| # reading its configuration and setting itself up. | |||||
| - name: Wait for the bridge to open its appservice port | |||||
| ansible.builtin.command: | |||||
| argv: | |||||
| - docker | |||||
| - run | |||||
| - --rm | |||||
| - --network={{ matrix_bridge_mautrix_whatsapp_container_network }} | |||||
| - "{{ molecule_shared_image_curl }}" | |||||
| - --silent | |||||
| - --output | |||||
| - /dev/null | |||||
| - --write-out | |||||
| - "HTTP_STATUS=%{http_code}" | |||||
| - "http://matrix-mautrix-whatsapp:8080/_matrix/mau/live" | |||||
| register: mautrix_whatsapp_live | |||||
| changed_when: false | |||||
| until: "'HTTP_STATUS=000' not in mautrix_whatsapp_live.stdout" | |||||
| retries: 24 | |||||
| delay: 5 | |||||
| failed_when: false | |||||
| - name: Assert the bridge answers on its appservice port | |||||
| ansible.builtin.assert: | |||||
| that: | |||||
| - "'HTTP_STATUS=000' not in mautrix_whatsapp_live.stdout" | |||||
| fail_msg: >- | |||||
| The bridge did not answer on its appservice port | |||||
| ({{ mautrix_whatsapp_live.stdout | default('no output') }}) | |||||
| success_msg: "The bridge answers on its appservice port" | |||||
| - name: Read the configuration the role rendered | |||||
| ansible.builtin.slurp: | |||||
| src: "{{ matrix_bridge_mautrix_whatsapp_config_path }}/config.yaml" | |||||
| register: mautrix_whatsapp_config_file | |||||
| # Each of these differs from what the bridge would use on its own, so their | |||||
| # presence means the role's configuration is what the bridge is running on | |||||
| # rather than something that happened to agree with it. | |||||
| - name: Assert the rendered configuration carries this scenario's values | |||||
| ansible.builtin.assert: | |||||
| that: | |||||
| - matrix_bridge_mautrix_whatsapp_homeserver_address in mautrix_whatsapp_config_rendered | |||||
| - matrix_bridge_mautrix_whatsapp_appservice_bot_username in mautrix_whatsapp_config_rendered | |||||
| - matrix_bridge_mautrix_whatsapp_appservice_token in mautrix_whatsapp_config_rendered | |||||
| fail_msg: "The rendered configuration does not carry the scenario's values" | |||||
| success_msg: "The rendered configuration carries the scenario's values" | |||||
| vars: | |||||
| mautrix_whatsapp_config_rendered: "{{ mautrix_whatsapp_config_file.content | b64decode }}" | |||||
| # The registration file is the half of the appservice handshake the | |||||
| # homeserver reads, and it is generated by the role rather than by the | |||||
| # bridge, so it is worth checking on its own. | |||||
| - name: Read the appservice registration the role rendered | |||||
| ansible.builtin.slurp: | |||||
| src: "{{ matrix_bridge_mautrix_whatsapp_config_path }}/registration.yaml" | |||||
| register: mautrix_whatsapp_registration_file | |||||
| - name: Assert the registration carries the scenario's tokens and bot user | |||||
| ansible.builtin.assert: | |||||
| that: | |||||
| - matrix_bridge_mautrix_whatsapp_appservice_token in mautrix_whatsapp_registration_rendered | |||||
| - matrix_bridge_mautrix_whatsapp_homeserver_token in mautrix_whatsapp_registration_rendered | |||||
| - matrix_bridge_mautrix_whatsapp_appservice_bot_username in mautrix_whatsapp_registration_rendered | |||||
| fail_msg: "The appservice registration does not carry the scenario's tokens and bot user" | |||||
| success_msg: "The appservice registration carries the scenario's tokens and bot user" | |||||
| vars: | |||||
| mautrix_whatsapp_registration_rendered: "{{ mautrix_whatsapp_registration_file.content | b64decode }}" | |||||
| # sqlite was chosen in molecule.yml, so the bridge should have created its | |||||
| # database under the role's data path. This is the cheap proof that the data | |||||
| # path reached the process and is writable by the uid the role runs it as. | |||||
| - name: Look for the bridge's sqlite database under the role's data path | |||||
| ansible.builtin.stat: | |||||
| path: "{{ matrix_bridge_mautrix_whatsapp_data_path }}/mautrix-whatsapp.db" | |||||
| register: mautrix_whatsapp_database | |||||
| - name: Assert the bridge created its database where the role put its data path | |||||
| ansible.builtin.assert: | |||||
| that: | |||||
| - mautrix_whatsapp_database.stat.exists | |||||
| - mautrix_whatsapp_database.stat.uid | int == matrix_user_uid | int | |||||
| fail_msg: >- | |||||
| The bridge did not create its database under | |||||
| {{ matrix_bridge_mautrix_whatsapp_data_path }}, or it is not owned by | |||||
| uid {{ matrix_user_uid }} | |||||
| success_msg: "The bridge created its database under the role's data path, as the role's uid" | |||||
| - name: Read the image of the running container | |||||
| ansible.builtin.command: | |||||
| argv: | |||||
| - docker | |||||
| - container | |||||
| - inspect | |||||
| - matrix-mautrix-whatsapp | |||||
| - --format | |||||
| - "{{ '{{' }} .Config.Image {{ '}}' }}" | |||||
| register: mautrix_whatsapp_image | |||||
| changed_when: false | |||||
| - name: Assert the running container is the version defaults/main.yml pins | |||||
| ansible.builtin.assert: | |||||
| that: | |||||
| - mautrix_whatsapp_role_defaults.matrix_bridge_mautrix_whatsapp_version in mautrix_whatsapp_image.stdout | |||||
| fail_msg: >- | |||||
| The running container is {{ mautrix_whatsapp_image.stdout }}, which does | |||||
| not carry the pinned version | |||||
| {{ mautrix_whatsapp_role_defaults.matrix_bridge_mautrix_whatsapp_version }} | |||||
| success_msg: "The running container is the version defaults/main.yml pins" | |||||
| - name: Read the labels the role rendered | |||||
| ansible.builtin.slurp: | |||||
| src: "{{ matrix_bridge_mautrix_whatsapp_base_path }}/labels" | |||||
| register: mautrix_whatsapp_labels | |||||
| - name: Assert no Traefik labels are emitted while Traefik support is disabled | |||||
| ansible.builtin.assert: | |||||
| that: | |||||
| - "'traefik.' not in (mautrix_whatsapp_labels.content | b64decode)" | |||||
| fail_msg: >- | |||||
| Traefik labels were emitted even though | |||||
| matrix_bridge_mautrix_whatsapp_container_labels_traefik_enabled is false | |||||
| success_msg: "No Traefik labels are emitted while Traefik support is disabled" | |||||