Files
status-app/ci/Jenkinsfile.tests-e2e
T
VedranandAnastasiya 271bcd014f chore(@e2e): use local waku fleet for linux E2E tests (#20215)
* chore(@e2e): use local waku fleet for E2E tests

Start local waku nodes via docker-compose before running E2E tests
and configure the app to use them instead of status.prod. This avoids
skewing production metrics, adding unnecessary load to prod hosts,
and generating noise in prod storage DBs.

- https://github.com/status-im/status-app/issues/20126

- https://github.com/status-im/status-go//tree/feat/fleet-override

* fix(@e2e): use host networking for local waku nodes

Switch docker-compose to network_mode:host so waku nodes
bind directly to localhost. Fixes ENR multiaddr containing
Docker internal hostnames/IPs unreachable by the app.

- Remove dns4-domain-name (caused Docker hostname in ENR)
- Use /ip4/127.0.0.1 for staticnode references
- Separate discv5 UDP ports (9000/9001
- Enable REST API for proper healthchecks
- Update fleet config JSON with correct DNS ENRs and store addr

* chore(@e2e): remove status community link from tests

---------

Co-authored-by: Anastasiya <anastasija.ig@gmail.com>
2026-04-14 14:49:26 +02:00

334 lines
9.8 KiB
Groovy

#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.42'
pipeline {
agent {
dockerfile {
label getAgentLabel()
filename 'tests.Dockerfile'
dir 'ci'
args '--network=host ' +
'--cap-add=SYS_ADMIN ' +
'--security-opt=apparmor:unconfined ' +
'--device=/dev/fuse ' +
'--volume=/var/run/docker.sock:/var/run/docker.sock ' +
'--volume=/home/jenkins/.squish-license:/home/jenkins/.squish-license ' +
'--volume=/opt/squish-runner-9.1.0-qt-6.9:/opt/squish-runner-9.1.0-qt-6.9 ' +
'--user jenkins'
}
}
parameters {
string(
name: 'GIT_REF',
description: 'Git branch to checkout.',
defaultValue: 'master'
)
string(
name: 'BUILD_SOURCE',
description: 'URL to tar.gz file OR path to Jenkins build.',
defaultValue: getDefaultBuildSource()
)
string(
name: 'TEST_NAME',
description: 'Paste test name/part of test name to run specific test.',
defaultValue: ''
)
string(
name: 'TEST_SCOPE_FLAG',
description: 'Paste a known mark to run tests labeled with this mark',
defaultValue: getDefaultTestScopeFlag()
)
string(
name: 'TESTRAIL_RUN_NAME',
description: 'Test run name in Test Rail.',
defaultValue: ''
)
choice(
name: 'LOG_LEVEL',
description: 'Log level for pytest.',
choices: ['INFO', 'DEBUG', 'TRACE', 'WARNING', 'CRITICAL']
)
choice(
name: 'AGENT_NODE',
description: 'Which Linux worker to run on (leave default for random selection)',
choices: [
'',
'linux-01',
'linux-02',
'linux-03'
],
)
}
options {
timestamps()
/* Prevent Jenkins jobs from running forever */
timeout(time: 120, unit: 'MINUTES')
/* manage how many builds we keep */
buildDiscarder(logRotator(
daysToKeepStr: '60',
numToKeepStr: '50',
artifactNumToKeepStr: '50',
))
disableRestartFromStage()
}
environment {
PLATFORM = 'tests/e2e'
SQUISH_DIR = '/opt/squish-runner-9.1.0-qt-6.9'
PYTHONPATH = "${SQUISH_DIR}/lib:${SQUISH_DIR}/lib/python:${PYTHONPATH}"
LD_LIBRARY_PATH = "${SQUISH_DIR}/lib:${SQUISH_DIR}/python3/lib:${LD_LIBRARY_PATH}"
/* To stop e2e tests using port 8545 */
STATUS_RUNTIME_HTTP_API = 'False'
STATUS_RUNTIME_WS_API = 'False'
/* Avoid race conditions with other builds using virtualenv. */
VIRTUAL_ENV = "${WORKSPACE_TMP}/venv"
PATH = "${VIRTUAL_ENV}/bin:${PATH}"
/* To store user configuratiin files in temp dir */
XDG_CONFIG_HOME = "${WORKSPACE_TMP}/config"
/* To remove QML cache */
QML_DISABLE_CACHE = 1
TESTRAIL_URL = 'https://ethstatus.testrail.net'
TESTRAIL_PROJECT_ID = 18
/* Override QT xcb plugin with linux to avoid errors like:
* "Could not load the Qt platform plugin "xcb" in "" even though it was found." */
QT_QPA_PLATFORM = "linuxfb"
/* Runtime flag to make testing of the app easier. Helps avoiding banners */
STATUS_RUNTIME_TEST_MODE = 1
/* Logging rules let you enable or disable logging for categories */
QT_LOGGING_RULES = '*.warning=true'
/* Set to a non-zero value to make Qt print out diagnostic information about the each (C++) plugin it tries to load. */
/* QT_DEBUG_PLUGINS = 0 */
/* Use local waku fleet */
STATUS_FLEET = 'status-app.test'
STATUS_FLEET_CONFIG_FILE = "${WORKSPACE}/assets/local-waku-fleets-config.json"
/* Hack fix for params not being set in job on first run */
BUILD_SOURCE = "${params.BUILD_SOURCE}"
TEST_NAME = "${params.TEST_NAME}"
TEST_SCOPE_FLAG = "${params.TEST_SCOPE_FLAG}"
TESTRAIL_RUN_NAME = "${params.TESTRAIL_RUN_NAME}"
LOG_LEVEL = "${params.LOG_LEVEL}"
}
stages {
stage('Cleanup Workspace') {
steps {
sh './scripts/clean-git.sh'
}
}
stage('Fetch submodules') {
steps {
sh 'git submodule update --init --recursive'
}
}
stage('Prep') {
steps { script {
setNewBuildName()
updateGitHubStatus()
} }
}
stage('Deps') {
steps { script { dir('test/e2e') {
sh "python3 -m venv ${VIRTUAL_ENV}"
sh 'pip3 install -r requirements.txt'
} } }
}
stage('Download') {
when { expression { params.BUILD_SOURCE.startsWith('http') } }
steps { timeout(5) { script { dir('test/e2e') {
sh 'mkdir -p ./pkg/'
setBuildDescFromFile(params.BUILD_SOURCE)
fileOperations([
fileDownloadOperation(
url: params.BUILD_SOURCE,
targetFileName: 'StatusIm-Desktop.tar.gz',
targetLocation: './pkg/',
userName: '',
password: '',
)
])
} } } }
}
stage('Copy') {
when { expression { ! params.BUILD_SOURCE.startsWith('http') } }
steps { timeout(5) { script { dir('test/e2e') {
copyArtifacts(
projectName: params.BUILD_SOURCE,
filter: 'pkg/*-x86_64.tar.gz',
selector: lastWithArtifacts(),
target: './'
)
setBuildDescFromFile(utils.findFile('pkg/*tar.gz'))
} } } }
}
stage('Unpack') {
steps { timeout(5) { script { dir('test/e2e') {
sh 'mkdir aut'
sh "tar -zxvf '${utils.findFile('pkg/*tar.gz')}' -C './aut'"
env.AUT_PATH = utils.findFile('aut/*.AppImage')
} } } }
}
stage('Start Local Fleet') {
steps { timeout(2) { script {
/* Clean up any leftover containers from a previous aborted run */
sh 'docker compose -f docker-compose.waku.yml down || true'
sh 'docker compose -f docker-compose.waku.yml up -d'
sh 'sleep 15'
} } }
}
stage('Test') {
steps { timeout(getTestStageTimeout()) { script { dir('test/e2e') {
def flags = []
if (params.TEST_NAME) { flags.add("-k=${params.TEST_NAME}") }
if (params.TEST_SCOPE_FLAG) { flags.add(params.TEST_SCOPE_FLAG) }
if (params.LOG_LEVEL) { flags.addAll(["--log-level=${params.LOG_LEVEL}", "--log-cli-level=${params.LOG_LEVEL}"]) }
dir ('configs') { sh 'ln -s _local.ci.py _local.py' }
wrap([
$class: 'Xvfb',
autoDisplayName: true,
parallelBuild: true,
screen: '2560x1440x24',
additionalOptions: '-dpi 1'
]) {
sh 'fluxbox &'
withCredentials([
usernamePassword(
credentialsId: 'test-rail-api-devops',
usernameVariable: 'TESTRAIL_USR',
passwordVariable: 'TESTRAIL_PSW'
),
string(credentialsId: 'wallet-test-user-seed', variable: 'WALLET_TEST_USER_SEED')
]) {
/* Keep the --reruns flag first, or it won't work */
sh """
python3 -m pytest -m "not keycard" -v --reruns=1 --timeout=300 ${flags.join(" ")} \
--disable-warnings \
--alluredir=./allure-results \
-o timeout_func_only=true
"""
}
}
} } } }
}
}
post {
always { script {
/* Stop local waku fleet */
sh 'docker compose -f docker-compose.waku.yml down --remove-orphans || true'
dir('test/e2e') {
archiveArtifacts('aut/*.*')
/* Needed to categorize types of errors and add environment section in allure report. */
sh 'cp ext/allure_files/categories.json allure-results'
sh 'cp ext/allure_files/environment.properties allure-results'
allure([
results: [[path: 'allure-results']],
reportBuildPolicy: 'ALWAYS',
properties: [],
jdk: '',
])
/* Link for Jenkins Builds GitHub comment. */
env.PKG_URL = "${env.BUILD_URL}allure/"
updateGitHubStatus()
}
} }
success { script {
github.notifyPR(true)
} }
failure { script {
github.notifyPR(false)
discord.send(
header: '**Desktop E2E test failure!**',
cred: 'discord-status-desktop-e2e-webhook',
)
} }
cleanup {
cleanWs(disableDeferredWipeout: true)
dir(env.WORKSPACE_TMP) { deleteDir() }
}
}
}
def setNewBuildName() {
if (currentBuild.upstreamBuilds) {
def parent = utils.parentOrCurrentBuild()
currentBuild.displayName = parent.getFullDisplayName().minus('status-app » ')
}
}
def setBuildDescFromFile(fileNameOrPath) {
def tokens = utils.parseFilename(utils.baseName(fileNameOrPath))
if (tokens == null) { /* Fallback for regex fail. */
currentBuild.description = utils.baseName(fileNameOrPath)
return
}
if (tokens.build && tokens.build.startsWith('pr')) {
currentBuild.displayName = tokens.build.replace(/^pr/, 'PR-')
}
currentBuild.description = formatMap([
Node: NODE_NAME,
Build: tokens.build,
Commit: tokens.commit,
Version: (tokens.tstamp ?: tokens.version),
])
}
def updateGitHubStatus() {
/* For PR builds update check status. */
if (params.BUILD_SOURCE ==~ /.*\/PR-[0-9]+\/?$/) {
github.statusUpdate(
context: 'jenkins/prs/tests/e2e-new',
commit: jenkins.getJobCommitByPath(params.BUILD_SOURCE),
repo_url: 'https://github.com/status-im/status-app'
)
}
}
def formatMap(Map data=[:]) {
def text = ''
data.each { key, val -> text += "<b>${key}</b>: ${val}</a><br>\n" }
return text
}
def getDefaultBuildSource() {
return ''
}
def getDefaultTestScopeFlag() {
if (JOB_NAME == "status-app/systems/linux/x86_64/tests-e2e") {
return ''
} else {
return '-m=critical'
}
}
def getTestStageTimeout() { params.TEST_SCOPE_FLAG == '-m=critical' ? 30 : 120 }
def getAgentLabel() {
if (params.AGENT_NODE && params.AGENT_NODE != '') {
return "linuxcontainer && ${params.AGENT_NODE}"
}
return 'linuxcontainer'
}