name: CI - Staging (GitHub)
jobs:
├── build-test-static-analysis
│ ├── Démarrage de PostgreSQL 16
│ ├── Spotless, Checkstyle et SpotBugs
│ ├── Tests unitaires
│ ├── Tests d'intégration
│ ├── Rapport JaCoCo
│ ├── Construction du fichier JAR
│ ├── Analyse CodeQL
│ └── Scan Trivy du système de fichiers
├── functional-tests
│ ├── Téléchargement du fichier JAR
│ ├── Scan de l'image Docker
│ ├── Démarrage du conteneur Flashcards
│ ├── Vérification de l'endpoint Actuator
│ ├── Installation de Node.js et Newman
│ └── Exécution des tests d'API
└── load-tests
├── Téléchargement du fichier JAR
├── Démarrage de l'application Spring Boot
├── Installation de k6
└── Exécution des tests de charge
services:
- name: postgres:16
alias: postgres
L'application Spring Boot doit interroger le service via son alias réseau et non via localhost (déclaré dans les fichiers de l'application)jdbc:postgresql://postgres:5432/flashcardsdb
au lieu de
jdbc:postgresql://localhost:5432/flashcardsdb
staging-build
│
▼
staging-it
│
├──────────────┐
▼ ▼
staging-trivy semgrep-sast
│ │
└──────┬───────┘
▼
staging-docker-build
│
▼
staging-trivy-image
│
▼
staging-functional
│
▼
staging-load-test
stages:
- test
- security
- docker
- security-image
- functional
- performance
Pour cette branche, nous déclarons l'ensemble des étapes pour couvrir tout le cycle de vie de l'image
Ajoutons les stages suivant:
staging-build:
stage: test
image: maven:3.9.11-eclipse-temurin-17
cache:
key: maven
paths:
- .m2/repository
policy: pull-push
script:
- chmod +x mvnw
- ./mvnw spotless:check
- ./mvnw -B clean test
artifacts:
when: always
paths:
- target/site/jacoco/
rules:
- if: '$CI_COMMIT_BRANCH == "staging"'
staging-it:
stage: test
needs:
- job: staging-build
artifacts: true
cache:
key: maven
paths:
- .m2/repository
policy: pull-push
image: maven:3.9.11-eclipse-temurin-17
services:
- name: postgres:16.14-alpine3.23
alias: postgres
variables:
SPRING_PROFILES_ACTIVE: it
POSTGRES_DB: flashcardsdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
DB_HOST: postgres
DB_PORT: "5432"
DB_USER: postgres
DB_PASSWORD: $POSTGRES_PASSWORD
script:
- chmod +x mvnw
- sleep 10
- ./mvnw -B verify -DskipUnitTests=true -Pcoverage-staging
artifacts:
when: always
paths:
- target/site/jacoco/
- target/*.jar
- target/failsafe-reports/
- .m2/repository/
expire_in: 30 mins
rules:
- if: '$CI_COMMIT_BRANCH == "staging"'
staging-trivy:
stage: security
needs:
- job: staging-it
artifacts: true
image:
name: aquasec/trivy:0.72.0
entrypoint: [""]
cache:
- key: maven
paths:
- .m2/repository
policy: pull
- key: trivy-db
paths:
- .trivycache
policy: pull-push
variables:
TRIVY_CACHE_DIR: ".trivycache"
script:
- trivy fs \
--severity HIGH,CRITICAL \
--format table \
--scanners vuln,misconfig \
--skip-dirs ".trivycache" \
--skip-dirs "target" \
--cache-dir "$TRIVY_CACHE_DIR" \
--offline-scan \
--exit-code 0 \
--no-progress \
.
rules:
- if: '$CI_COMMIT_BRANCH == "staging"'
+-----------------------+
| staging-build |
+-----------------------+
|
| (1) Télécharge & remplit le cache
▼
===================================================
|| CACHE GLOBAL : maven (.m2/repository) || <----+
=================================================== |
| |
| (2) Réutilise (Évite les retéléchargements) | (5) Analyse les
▼ | .jar en local
+-----------------------+ | (Offline)
| staging-it | [Service: PostgreSQL] |
+-----------------------+ |
| |
| (3) Génère les fichiers compilés |
▼ |
==================================================== |
|| ARTEFACTS: Dossier target/ (.jar & rapports) || |
==================================================== |
| |
| (4) Transmet instantanément via "needs" |
▼ |
+-----------------------+ |
| staging-trivy | ---------------------------------+
+-----------------------+
▲
| (6) Persiste les signatures de failles
▼
=================================================
|| CACHE LOCAL : trivy-db (.trivycache) ||
=================================================
staging-docker-build:
stage: docker
needs:
- staging-trivy
- job: staging-it
artifacts: true
image: docker:29.6.1
services:
- name: docker:29.6.1-dind
alias: docker
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_TLS_VERIFY: "1"
DOCKER_CERT_PATH: "/certs/client"
script:
- chmod +x ci-scripts/docker-build.sh
- ./ci-scripts/docker-build.sh flashcards staging
- docker save flashcards:staging -o flashcards-staging.tar
artifacts:
paths:
- flashcards-staging.tar
rules:
- if: '$CI_COMMIT_BRANCH == "staging"'
#!/bin/bash
set -euo pipefail
IMAGE_NAME=${1:-flashcards}
IMAGE_TAG=${2:-latest}
echo "== Building Docker image: ${IMAGE_NAME}:${IMAGE_TAG} =="
docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
#!/bin/sh
set -eu
IMAGE_NAME=${1:-flashcards}
IMAGE_TAG=${2:-latest}
DOCKERFILE=${3:-Dockerfile}
echo "====================================="
echo "Building Docker image"
echo "Image : ${IMAGE_NAME}:${IMAGE_TAG}"
echo "Dockerfile : ${DOCKERFILE}"
echo "====================================="
docker build \
-f "${DOCKERFILE}" \
-t "${IMAGE_NAME}:${IMAGE_TAG}" \
.
echo "Docker image successfully built"
staging-trivy-image:
stage: security-image
needs:
- job: staging-docker-build
artifacts: true
image:
name: aquasec/trivy:0.72.0
entrypoint: [""]
cache:
- key: trivy-db
paths:
- .trivycache
policy: pull-push
variables:
TRIVY_CACHE_DIR: ".trivycache"
script:
- trivy image \
--input flashcards-staging.tar \
--severity HIGH,CRITICAL \
--exit-code 0 \
--ignore-unfixed \
--timeout 10m \
--cache-dir "$TRIVY_CACHE_DIR" \
--format table \
--no-progress
rules:
- if: '$CI_COMMIT_BRANCH == "staging"'
$ chmod +x ci-scripts/trivy-image.sh
$ ./ci-scripts/trivy-image.sh flashcards-staging.tar 0
2026-09-23T11:23:07Z INFO [vulndb] Need to update DB
2026-09-23T11:23:07Z INFO [vulndb] Downloading vulnerability DB...
2026-09-23T11:23:07Z INFO [vulndb] Downloading artifact... repo="mirror.gcr.io/aquasec/trivy-db:2"
2026-09-23T11:23:32Z INFO [vulndb] Artifact successfully downloaded repo="mirror.gcr.io/aquasec/trivy-db:2"
2026-09-23T11:23:32Z INFO [vuln] Vulnerability scanning is enabled
2026-09-23T11:23:32Z INFO [secret] Secret scanning is enabled
2026-09-23T11:23:45Z INFO [javadb] Downloading Java DB...
2026-09-23T11:23:45Z INFO [javadb] Downloading artifact... repo="mirror.gcr.io/aquasec/trivy-java-db:1"
2026-09-23T11:25:34Z INFO [javadb] Artifact successfully downloaded repo="mirror.gcr.io/aquasec/trivy-java-db:1"
2026-09-23T11:25:34Z INFO [javadb] Java DB is cached for 3 days. If you want to update the database more frequently, "trivy clean --java-db" command clears the DB cache.
2026-09-23T11:25:36Z INFO Detected OS family="debian" version="13.6"
2026-09-23T11:25:36Z INFO [debian] Detecting vulnerabilities... os_version="13" pkg_num=25
2026-09-23T11:25:36Z INFO Number of language-specific files num=1
2026-09-23T11:25:36Z INFO [jar] Detecting vulnerabilities...
2026-09-23T11:25:37Z WARN Using severities from other vendors for some vulnerabilities. Read https://trivy.dev/docs/v0.74/guide/scanner/vulnerability#severity-selection for details.
0 vulnérabilité et 0 secret détectés sur l'OS Debian.
0 vulnérabilité détectée dans les dépendances du JAR applicatif
L'image de staging est certifiée 100 % saine (Clean)
Report Summary
┌──────────────────────────────────────┬────────┬─────────────────┬─────────┐
│ Target │ Type │ Vulnerabilities │ Secrets │
├──────────────────────────────────────┼────────┼─────────────────┼─────────┤
│ flashcards-staging.tar (debian 13.6) │ debian │ 0 │ - │
├──────────────────────────────────────┼────────┼─────────────────┼─────────┤
│ app/app.jar │ jar │ 0 │ - │
└──────────────────────────────────────┴────────┴─────────────────┴─────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)
Saving cache for successful job
Creating cache trivy-db-4-protected...
.trivycache/: found 9 matching artifact files and directories
Created cache
Cleaning up project directory and file based variables
Job succeeded
staging-functional:
stage: functional
needs:
- job: staging-docker-build
artifacts: true
- staging-trivy-image
image: docker:29.6.1
services:
- name: docker:29.6.1-dind
alias: docker
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_TLS_VERIFY: "1"
DOCKER_CERT_PATH: "/certs/client"
script:
- docker load -i flashcards-staging.tar
- docker network create flashcards-net
- |
docker run -d \
--name postgres \
--network flashcards-net \
-e POSTGRES_DB=flashcardsdb \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
postgres:18.6-alpine3.23
- |
until docker exec postgres pg_isready -U postgres; do
echo "Waiting for PostgreSQL..."
sleep 2
done
- |
docker run -d \
--name flashcards-staging \
--network flashcards-net \
-e SPRING_PROFILES_ACTIVE=staging \
-e SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/flashcardsdb \
-e SPRING_DATASOURCE_USERNAME=postgres \
-e SPRING_DATASOURCE_PASSWORD=$POSTGRES_PASSWORD \
flashcards:staging
- |
started=false
for i in $(seq 1 30); do
if docker logs flashcards-staging 2>&1 | grep -q "Started FlashcardsApplication"; then
echo "Application started"
started=true
break
fi
sleep 2
done
if [ "$started" != "true" ]; then
echo "Application failed to start"
docker logs flashcards-staging
exit 1
fi
- |
docker run --rm \
--network flashcards-net \
-v "$CI_PROJECT_DIR/postman:/etc/newman" \
postman/newman:alpine \
run /etc/newman/flashcards.postman_collection.json \
-e /etc/newman/local.postman_environment.json \
--env-var "base-url=http://flashcards-staging:8081" \
--reporters cli,junit \
--reporter-junit-export /etc/newman/newman-functional.xml
- |
docker run --rm \
--network flashcards-net \
-v "$CI_PROJECT_DIR/postman:/etc/newman" \
postman/newman:alpine \
run /etc/newman/flashcards_error_cases.postman_collection.json \
-e /etc/newman/local.postman_environment.json \
--env-var "base-url=http://flashcards-staging:8081" \
--reporters cli,junit \
--reporter-junit-export /etc/newman/newman-error.xml
artifacts:
when: always
paths:
- postman/newman-functional.xml
- postman/newman-error.xml
- container.log
reports:
junit:
- postman/newman-functional.xml
- postman/newman-error.xml
after_script:
- docker logs flashcards-staging > container.log 2>&1 || true
- docker rm -f flashcards-staging || true
- docker rm -f postgres || true
rules:
- if: '$CI_COMMIT_BRANCH == "staging"'
..Downloading artifacts for staging-docker-build (16678713644)...
Downloading artifacts from coordinator... ok correlation_id=a3f934366cb1aa7c-BRU id=16678713644 status=302 token=73_TZUsSx
Executing "step_script" stage of the job script
Using effective pull policy of [always] for container docker:29.8.1@sha256:3f3c01aaaebf7cce837356b688b7c059a4749f10bd7660dec7c58fc454a283f0
$ docker load -i flashcards-staging.tar
Loaded image: flashcards:staging
$ docker network create flashcards-net
7b66b06f79becf4a5a4bc8bc8dcb6a542303207d4dec495d15d3318c44d2c9a0
$ docker run -d \
docker.io/library/[MASKED]@sha256:885cf05d376c7cf27afef02073e6bdac3841252537f16e244fd1c1e6a7c99fb1: Pulling from library/[MASKED]
.. Status: Downloaded newer image for [MASKED]@sha256:885cf05d376c7cf27afef02073e6bdac3841252537f16e244fd1c1e6a7c99fb1
$ until docker exec [MASKED] pg_isready -U [MASKED]; do
/var/run/[MASKED]ql:5432 - accepting connections
$ docker run -d \
18b2dad0c0f476ec28467bd65a727f7c9acaf847c3c4662dfb8b2bd4b1efb17f
$ started=false
Application started
$ docker run --rm \
alpine: Pulling from postman/newman
Digest: sha256:d9b5e780ead0026bbb37f3759cd7b10fc8a88cd4030c8266a7c5e343d75a5198
Status: Downloaded newer image for postman/newman:alpine
→ add categories
POST http://flashcards-staging:8081/api/categories [200 OK,356B,510ms]
✓ Category created
✓ Response is JSON
✓ Category ID is present
→ get category by id
GET http://flashcards-staging:8081/api/categories/1 [200 OK,356B,66ms]
✓ Category retrieved
✓ Correct category returned
→ modify categories
PUT http://flashcards-staging:8081/api/categories/1 [200 OK,373B,93ms]
✓ categoryId is defined and valid
→ get category by id after update
GET http://flashcards-staging:8081/api/categories/1 [200 OK,373B,19ms]
✓ Category retrieved after an update
✓ Correct category returned
→ get category by name
GET http://flashcards-staging:8081/api/categories/search?name=git
✓ Search categories returns 200 [200 OK,336B,185ms]
✓ Response is JSON array
✓ All categories match search term
✓ Category object structure is valid
→ delete categorie
DELETE http://flashcards-staging:8081/api/categories/1
✓ Category deleted [200 OK,293B,76ms]
→ get categories
GET http://flashcards-staging:8081/api/categories [200 OK,336B,23ms]
✓ Deleted category is not present anymore
Flashcards API
├── Categories
│ ├── get categories (global)
│ ├── add category
│ ├── get category by id
│ ├── modify category
│ ├── get category by id (after update)
│ ├── get category by name (by word)
│ ├── delete category
│ └── get category (after delete)
├── Flashcards
│ ├── get flashcards (global)
│ └── etc...
Il exécute ensuite le scénario pour les flashcards┌─────────────────────────┬────────────────────┬───────────────────┐
│ │ executed │ failed │
├─────────────────────────┼────────────────────┼───────────────────┤
│ iterations │ 1 │ 0 │
├─────────────────────────┼────────────────────┼───────────────────┤
│ requests │ 15 │ 0 │
├─────────────────────────┼────────────────────┼───────────────────┤
│ test-scripts │ 30 │ 0 │
├─────────────────────────┼────────────────────┼───────────────────┤
│ prerequest-scripts │ 17 │ 0 │
├─────────────────────────┼────────────────────┼───────────────────┤
│ assertions │ 30 │ 0 │
├─────────────────────────┴────────────────────┴───────────────────┤
│ total run duration: 1977ms │
├──────────────────────────────────────────────────────────────────┤
│ total data received: 441B (approx) │
├──────────────────────────────────────────────────────────────────┤
│ average response time: 84ms [min: 19ms, max: 510ms, s.d.: 120ms] │
└──────────────────────────────────────────────────────────────────┘
| [200 OK, 399B, 19ms] |
| [statut HTTP, taille du message en Octets, temps de réponse en millisecondes] |
$ docker run --rm flashcards_error_cases
→ GET category by invalid id categorie
GET http://flashcards-staging:8081/api/categories/189782 [404 Not Found]
✓ Can't Get: Category Not Found - error 404
✓ GET /categories/{categoryId} returns 404 when category does not exist
→ PUT category by invalid id categorie
PUT http://flashcards-staging:8081/api/categories/189782
✓ Can't Update: Category does not exist - Bad Request 400
→ POST category by invalid name
POST http://flashcards-staging:8081/api/categories/
✓ Can't add: Category Not Found [404 Not Found,534B,37ms]
✓ Returns client error (4xx)
→ GET flashcards by invalid id
GET http://flashcards-staging:8081/api/flashcards/124578
✓ Can't Get: Flashcard Not Found - error 404 [404 Not Found,300B,19ms]
→ POST flashcards by invalid id category
POST http://flashcards-staging:8081/api/flashcards/
✓ Can't add: Flashcard Not Found - error 404 [404 Not Found,534B,18ms]
→ PUT flashcards by invalid id categorie
PUT http://flashcards-staging:8081/api/flashcards/ [404 Not Found]
✓ Can't Update: Flashcard not found - error 404
┌─────────────────────────┬───────────────────┬───────────────────┐
│ │ executed │ failed │
├─────────────────────────┼───────────────────┼───────────────────┤
│ iterations │ 1 │ 0 │
├─────────────────────────┼───────────────────┼───────────────────┤
│ requests │ 6 │ 0 │
├─────────────────────────┼───────────────────┼───────────────────┤
│ test-scripts │ 6 │ 0 │
├─────────────────────────┼───────────────────┼───────────────────┤
│ prerequest-scripts │ 0 │ 0 │
├─────────────────────────┼───────────────────┼───────────────────┤
│ assertions │ 8 │ 0 │
├─────────────────────────┴───────────────────┴───────────────────┤
│ total run duration: 511ms │
├─────────────────────────────────────────────────────────────────┤
│ total data received: 424B (approx) │
├─────────────────────────────────────────────────────────────────┤
│ average response time: 45ms [min: 16ms, max: 116ms, s.d.: 36ms] │
└─────────────────────────────────────────────────────────────────┘
la durée du run de 511 ms avec un temps de réponse moyen de 45 msRunning after script...
..
Uploading artifacts for successful job
postman/newman-functional.xml: found 1 matching artifact files and directories
postman/newman-error.xml: found 1 matching artifact files and directories
container.log: found 1 matching artifact files and directories
Uploading artifacts as "archive" to coordinator... 201 Created correlation_id=a3f938f798d3b753-BRU id=16678713646 status=201 token=73_TZUsSx
Uploading artifacts...
postman/newman-functional.xml: found 1 matching artifact files and directories
postman/newman-error.xml: found 1 matching artifact files and directories
Uploading artifacts as "junit" to coordinator... 201 Created correlation_id=a3f9390228e1e0ca-BRU id=16678713646 status=201 token=73_TZUsSx
Cleaning up project directory and file based variables
Job succeeded
staging-load-test:
stage: performance
when: manual
allow_failure: true
needs:
- job: staging-docker-build
artifacts: true
- staging-trivy-image
image: docker:29.6.1
services:
- name: docker:29.6.1-dind
alias: docker
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_TLS_VERIFY: "1"
DOCKER_CERT_PATH: "/certs/client"
script:
- docker load -i flashcards-staging.tar
- docker network create flashcards-net
- |
docker run -d \
--name postgres \
--network flashcards-net \
-e POSTGRES_DB=flashcardsdb \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
postgres:16.14-alpine3.23
- |
until docker exec postgres pg_isready -U postgres; do
sleep 2
done
- |
docker run -d \
--name flashcards-staging \
--network flashcards-net \
-p 8081:8081 \
-e SPRING_PROFILES_ACTIVE=staging \
-e SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/flashcardsdb \
-e SPRING_DATASOURCE_USERNAME=postgres \
-e SPRING_DATASOURCE_PASSWORD=$POSTGRES_PASSWORD \
flashcards:staging
- |
for i in $(seq 1 30); do
if docker logs flashcards-staging 2>&1 | grep -q "Started FlashcardsApplication"; then
echo "Application ready after ${i} attempt(s)"
break
fi
if [ "$i" -eq 30 ]; then
echo "Application failed to start after 60 seconds"
docker logs flashcards-staging
exit 1
fi
sleep 2
done
- |
docker run --rm \
--network flashcards-net \
-e BASE_URL=http://flashcards-staging:8081 \
-v "$CI_PROJECT_DIR/load-test:/scripts" \
grafana/k6:2.2.0\
run \
--vus 50 \
--duration 2m \
--out json=/scripts/k6-report.json \
/scripts/flashcards.js
artifacts:
when: always
paths:
- load-test/k6-report.json
- container-loadtest.log
after_script:
- docker logs flashcards-staging > container-loadtest.log 2>&1 || true
- docker rm -f flashcards-staging || true
- docker rm -f postgres || true
rules:
- if: '$CI_COMMIT_BRANCH == "staging"'
Downloading artifacts
Downloading artifacts for staging-docker-build (15578506279)...
Direct download from cdn.artifacts.gitlab-static.net correlation_id=a22531ce3d2e90b8-BRU id=15578506279 status=302 token=71_ZAtRjf
Downloading artifacts from coordinator... ok correlation_id=a22531ce3d2e90b8-BRU id=15578506279 status=302 token=71_ZAtRjf
Executing "step_script" stage of the job script
Using effective pull policy of [always] for container docker:29.6.1@sha256:66d292e5c26bd33a6f6f61cacb880de2186339a524ecba1ce098dbbaceed6515
Using docker image sha256:66d292e5c26bd33a6f6f61cacb880de2186339a524ecba1ce098dbbaceed6515 ..
$ docker load -i flashcards-staging.tar
Loaded image: flashcards:staging
$ docker network create flashcards-net
0807c33c91fcbaf7fbbb1ec8d714a9341aeff0d7673b514ff21c7401a6ec3321
$ docker run -d
..
Digest: sha256:42b8b8b29c8a4e933d88943e5b03001a78794905cf786e6e7634e9f2abd5a0d3
Status: Downloaded newer image for [MASKED]@sha256:42b8b8b29c8a4e933d88943e5b03001a78794905cf786e6e7634e9f2abd5a0d3
203df52b9949ff8f903223c40a3bf6957d3c462e09f4f500fcd2dd59893dcabb
$ until docker exec [MASKED] pg_isready -U [MASKED]; do
..
/var/run/[MASKED]ql:5432 - accepting connections
$ docker run -d
0c24d0c815b06fd5f6105ced0d03f123abca7759f0ff1086b982c67d1d669228
$ for i in $(seq 1 30); do
Application ready after 8 attempt(s)
$ docker run --rm
91627df7da4b: Download complete
Digest: sha256:65c920dc067d5e2e00befbf982af6ad6ad0117034e8b1c65817c7975c52d4669
Status: Downloaded newer image for grafana/k6@sha256:65c920dc067d5e2e00befbf982af6ad6ad0117034e8b1c65817c7975c52d4669
/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/
execution: local
script: /scripts/flashcards.js
output: json (/scripts/k6-report.json)
scenarios: (100.00%) 1 scenario, 50 max VUs, 2m30s max duration (incl. graceful stop):
* default: 50 looping VUs for 2m0s (gracefulStop: 30s)
running (0m01.0s), 50/50 VUs, 0 complete and 0 interrupted iterations
default [ 1% ] 50 VUs 0m01.0s/2m0s
running (0m02.0s), 50/50 VUs, 0 complete and 0 interrupted iterations
default [ 2% ] 50 VUs 0m02.0s/2m0s
running (0m03.0s), 50/50 VUs, 50 complete and 0 interrupted iterations
...
running (2m01.0s), 03/50 VUs, 4417 complete and 0 interrupted iterations
default ↓ [ 100% ] 50 VUs 2m0s
█ THRESHOLDS
http_req_duration
✓ 'p(95)<500' p(95)=242.19ms
http_req_failed
✓ 'rate<0.01' rate=0.00%
█ TOTAL RESULTS
checks_total.......: 17680 146.039257 /s
checks_succeeded...: 100.00% 17680 out of 17680
checks_failed......: 0.00% 0 out of 17680
✓ create category OK
✓ create flashcard OK
✓ get categories OK
✓ get flashcards OK
HTTP
http_req_duration..............: avg=77.74ms min=4.16ms med=48.23ms max=1.16s p(90)=169.18ms p(95)=242.19ms
{ expected_response:true }...: avg=77.74ms min=4.16ms med=48.23ms max=1.16s p(90)=169.18ms p(95)=242.19ms
http_req_failed................: 0.00% 0 out of 17680
http_reqs......................: 17680 146.039257/s
EXECUTION
iteration_duration.............: avg=1.36s min=1.11s med=1.27s max=2.97s p(90)=1.65s p(95)=1.84s
iterations.....................: 4420 36.509814/s
vus............................: 3 min=3 max=50
vus_max........................: 50 min=50 max=50
NETWORK
data_received..................: 1.0 GB 8.4 MB/s
data_sent......................: 2.6 MB 21 kB/s
running (2m01.1s), 00/50 VUs, 4420 complete and 0 interrupted iterations
default ✓ [ 100% ] 50 VUs 2m0s
Running after script...
$ docker logs flashcards-staging > container-loadtest.log 2>&1 || true
$ docker rm -f flashcards-staging || true
flashcards-staging
$ docker rm -f [MASKED] || true
[MASKED]
Uploading artifacts for successful job
Uploading artifacts...
load-test/k6-report.json: found 1 matching artifact files and directories
container-loadtest.log: found 1 matching artifact files and directories
Uploading artifacts as "archive" to coordinator... 201 Created correlation_id=a22538f76b80ba5e-BRU id=15580439447 status=201 token=71_ZAtRjf
Cleaning up project directory and file based variables
Job succeeded
staging-build
↓ (Compilation & Tests Unitaires)
staging-package
↓ (Packaging de l'application)
staging-docker-build
↓ (Construction unique de l'image)
staging-functional
↓ (Tests d'intégration d'API)
staging-load-test
(Tests de performance à la demande)
docker build (Image: flashcards:staging)
▼
docker save → flashcards-staging.tar
▼
GitLab Artifact (Persistance temporaire)
▼
┌───────────────────────┴───────────────────────┐
▼ [staging-functional] ▼ [staging-load-test]
download artefact download artefact
↓ ↓
docker load docker load
↓ ↓
docker run docker run
↓ ↓
Newman (Postman) k6 (Grafana)

