Skip to content
Open
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
181 changes: 167 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,174 @@
# UR1/ESIR DevOps Course
This repository contains the material and content of the DevOps course at the engineering school ESIR of the University of Rennes 1.
# Compte Rendu — Politique de Tests & CI/CD

## Year 2024-2025
### Membre du groupe
- KACOU Mandodja Abel Romaric
- KOUAKOU Yao Lopez
- KOUAME Amoin Grâce Audrey

### Scheduling
## Sommaire

- Introduction to the course and DevOps: March 25th, 2025
- Quick overview of DevSecOps and MLSecOps: May the 23rd, 2025
- Final presentations: May 16th, 2025 (8h-11h)
1. [Objectif du TP](#2-objectif-du-tp)
2. [Analyse des outils demandés](#3-analyse-des-outils-demandés)
3. [Ce qui a été implémenté](#4-ce-qui-a-été-implémenté)
4. [Problèmes rencontrés et solutions](#5-problèmes-rencontrés-et-solutions)
5. [Lancer les tests localement](#7-lancer-les-tests-localement)

### Material
---

All the material can be found on the Moodle module dedicated to this class.
## 1. Objectif du TP

### Tutorial activities
Mettre en œuvre une **politique complète de tests automatisés et de CI/CD** en intégrant les outils suivants :

Students have to choose a system with micro-services to apply some DevOps related tools on it;
if they cannot think of such system/project, they can go to the [doodle](https://github.com/barais/doodlestudent) github page and use it.
You can also find a "detailled" pull request to launch the application on "dev mode".
This is the kind of pull requests that is expected to be __sent on THIS repo__ for the evaluation of your technical realisation
| Outil demandé | Domaine |
|---|---|
| `hu.advanceweb.scott-gradle-plugin` | Rapports d'échec de tests enrichis |
| `testcontainers` | Tests d'intégration avec vraie base de données |
| `saucelabs` / `perfecto.io` | Tests E2E multi-navigateurs cloud |
| `pitest` | Tests de mutation Java |
| `Stryker Mutator` | Tests de mutation TypeScript/Angular |
| `bazel` (évaluation) | Outil de build alternatif |

---

## 2. Analyse des outils demandés

### scott-gradle-plugin
Scott enrichit les rapports d'échec JUnit en affichant l'état des variables locales au moment de l'échec.
**Problème** : le backend utilise **Maven**, pas Gradle. La variante `scott-maven-plugin` entre en conflit avec le mécanisme de classloader de Quarkus (chargement d'agent Java incompatible avec `quarkus-maven-plugin`).
**Décision** : il est documenté dans [POLITIQUE_TESTS_CI.md](POLITIQUE_TESTS_CI.md), non intégré. Les rapports Surefire (JUnit 5) sont suffisants.

### testcontainers
**Décision** : nous l'avons intégré. Il permet de lancer un vrai conteneur MySQL pendant les tests sans infrastructure externe. De plus, il est bien supporté par Quarkus via `QuarkusTestResourceLifecycleManager`.

### saucelabs / perfecto.io
**Problème** : ces plateformes nécessitent des comptes et des clés d'API payantes.
**Décision** : l'intégration a été documentée dans [POLITIQUE_TESTS_CI.md](POLITIQUE_TESTS_CI.md) avec la configuration Protractor prête à l'emploi. Les tests E2E utilisent `ChromeHeadless` par défaut en attendant les licences.

### pitest
**Décision** : nous l'avons intégré via `pitest-maven` + `pitest-junit5-plugin` dans `api/pom.xml`.

### Stryker Mutator
**Décision** : Egalement intégré via `@stryker-mutator/core` + `@stryker-mutator/karma-runner` dans `front/`.

### bazel
**Décision** : non adopté. La migration vers Bazel obligerait à réécrire tous les fichiers de build (`BUILD.bazel`) pour Maven et Angular CLI. Courbe d'apprentissage élevée sans bénéfice proportionnel pour ce monorepo. **GitHub Actions** remplit le même rôle d'orchestration CI/CD nativement.

---

## 3. Ce qui a été implémenté

### `.github/workflows/ci.yml` — Pipeline CI/CD
Workflow GitHub Actions déclenché à chaque `push` et `pull_request` sur `main`. Deux jobs **parallèles** :

- **Job `api`** : `mvn verify` (tests + SpotBugs) → `mvn pitest:mutationCoverage` → upload rapport
- **Job `front`** : `npm ci` → build production → `ng test` ChromeHeadless → Stryker → upload rapport

### `api/pom.xml` — Backend
- Ajout de `testcontainers:mysql` et `testcontainers:junit-jupiter` (scope `test`)
- Ajout du plugin `pitest-maven` 1.15.3 + `pitest-junit5-plugin` 1.2.1
- Configuration Pitest avec exclusion des tests `*IT` (voir section 5)

### `api/src/test/java/fr/istic/tlc/MySQLTestResource.java`
Implémentation de `QuarkusTestResourceLifecycleManager` qui :
1. Démarre un conteneur `mysql:8.0` via Testcontainers
2. Injecte dynamiquement l'URL JDBC, le username et le password dans la configuration Quarkus
3. Configure Hibernate en `drop-and-create` pour les tests

### `api/src/test/java/fr/istic/tlc/PollRepositoryIT.java`
Test d'intégration `@QuarkusTest` + `@QuarkusTestResource(MySQLTestResource.class)` couvrant :
- `persistAndFindBySlug_shouldWork` — persistance et recherche par slug
- `persistAndFindByAdminSlug_shouldWork` — recherche par slug admin
- `count_shouldIncrementAfterPersist` — comptage après insertion

### `front/stryker.config.json`
Configuration Stryker pour Angular :
- Runner : Karma (réutilise la suite Jasmine existante)
- Cible : `src/app/**/*.ts` (hors `.spec.ts`, `.module.ts`)
- Checker TypeScript activé
- Rapport HTML généré dans `reports/mutation/`

### `front/tsconfig.stryker.json`
Configuration TypeScript dédiée à Stryker (désactive `strictNullChecks` pour la compatibilité avec les mutations générées).

### `front/package.json` — Scripts ajoutés
```json
"test:ci": "ng test --watch=false --browsers=ChromeHeadless",
"test:mutation": "stryker run"
```

---

## 4. Problèmes rencontrés et solutions

### Problème 1 — Pitest échoue avec `Tests failing without mutation`

**Contexte** : lors du premier lancement de `mvn pitest:mutationCoverage`, Pitest retournait :

```
SEVERE : Tests failing without mutation:
fr.istic.tlc.PollRepositoryIT#count_shouldIncrementAfterPersist()
BUILD FAILURE — Mutation testing requires a green suite.
```

**Cause** : Pitest exécute les tests dans son propre classloader, en dehors du cycle de vie Quarkus complet. `PollRepositoryIT` a besoin de Docker via Testcontainers et du contexte CDI Quarkus — deux choses que Pitest ne peut pas fournir.

**Solution** : exclusion des classes `*IT` du scope de Pitest dans `api/pom.xml` :

```xml
<excludedTestClasses>
<param>fr.istic.tlc.*IT</param>
</excludedTestClasses>
```

Les tests d'intégration continuent de s'exécuter normalement via `mvn verify` (Surefire + Failsafe), seul Pitest les ignore.

---

### Problème 2 — scott-maven-plugin incompatible avec Quarkus

**Contexte** : tentative d'intégration de `scott-maven-plugin` pour des rapports d'échec enrichis.

**Cause** : Scott fonctionne en attachant un agent Java (`-javaagent`) au démarrage de la JVM de test. Quarkus utilise son propre classloader d'isolation (`QuarkusClassLoader`) qui entre en conflit avec l'instrumentation bytecode de Scott.

**Solution** : il n'a pas été intégré. Documenté dans [POLITIQUE_TESTS_CI.md](POLITIQUE_TESTS_CI.md) pour une future intégration sur un projet Maven pur (hors Quarkus).

---

### Problème 3 — SauceLabs / Perfecto.io sans licences

**Contexte** : intégration impossible sans compte actif et clés d'API.

**Solution** : configuration complète documentée dans [POLITIQUE_TESTS_CI.md](POLITIQUE_TESTS_CI.md) avec le snippet Protractor prêt à l'emploi et le job GitHub Actions conditionnel. Les tests E2E utilisent `ChromeHeadless` par défaut.

## 5. Lancer les tests localement

> **Prérequis** : Java 17+, Maven, Node 20+, Docker en cours d'exécution.

### Backend

```bash
cd api

# Tests unitaires + tests d'intégration Testcontainers + SpotBugs
mvn clean verify

# Tests de mutation Java (Pitest)
mvn pitest:mutationCoverage
# → Rapport : api/target/pit-reports/index.html
```

### Frontend

```bash
cd front

# Installation des dépendances
npm ci

# Tests unitaires (mode CI, sans interface)
npm run test:ci

# Tests de mutation TypeScript (Stryker)
npm run test:mutation
# → Rapport : front/reports/mutation/mutation.html
```
74 changes: 74 additions & 0 deletions cicd/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:

api:
name: Build, Analyse & Test API
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven

- name: Build, static analysis & tests
run: mvn -B verify --file api/pom.xml

- name: Mutation tests (Pitest)
run: mvn -B pitest:mutationCoverage --file api/pom.xml

- name: Upload Pitest report
uses: actions/upload-artifact@v4
if: always()
with:
name: pitest-report
path: api/target/pit-reports/

front:
name: Build & Test Frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Node 20
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: front/package-lock.json

- name: Install dependencies
run: npm ci
working-directory: front

- name: Build
run: npm run build -- --configuration production
working-directory: front

- name: Unit tests (ChromeHeadless)
run: npm run test:ci
working-directory: front

- name: Mutation tests (Stryker)
run: npm run test:mutation
working-directory: front

- name: Upload Stryker report
uses: actions/upload-artifact@v4
if: always()
with:
name: stryker-report
path: front/reports/mutation/
Loading