Lab 4 - Version Control, Build Systems, and Automated Testing
Objectifs
Le but de ce lab est d’acquérir une expérience pratique avec les outils DevOps essentiels :
- Gérer du code avec Git et collaborer via GitHub
- Configurer un build system avec NPM
- Écrire des tests automatisés avec Jest et SuperTest
- Tester du code d’infrastructure avec OpenTofu
Section 1 : Gestion de version avec Git
Nous avons initialisé un dépôt Git local, effectué des commits, créé des branches et effectué des merges.
mkdir /tmp/git-practice
cd /tmp/git-practice
echo 'Hello, World!' > example.txt
git init
git add example.txt
git commit -m "Initial commit"Exercise 1 : Créer un tag v1.0
git tag v1.0
git tag # vérifie que le tag a bien été crééExercise 2 : git rebase vs git merge
git checkout -b feature
echo 'Feature work' >> example.txt
git commit -am "Feature commit"
git checkout main
git rebase featuregit mergecrée un commit de fusion qui préserve l’historique des deux branchesgit rebaseréécrit l’historique pour un historique linéaire plus propre
Section 2 : Collaboration avec GitHub
Nous avons poussé notre dépôt local sur GitHub et travaillé avec des pull requests.
git remote add origin https://github.com/<username>/devops-lab.git
git push -u origin mainExercise 3 : Branch protection
- Settings → Branches → Add branch protection rule
- “Require a pull request before merging”
- “Require approvals” avec au moins 1 reviewer
Exercise 4 : Signed commits
gpg --full-generate-key
git config --global user.signingkey <KEY_ID>
git config --global commit.gpgsign trueSection 3 : Build System avec NPM
Configuration de NPM pour une app Node.js avec Express.
"scripts": {
"start": "node server.js",
"dockerize": "./build-docker-image.sh",
"test": "jest --verbose",
"test:coverage": "jest --coverage"
}Exercise 5 : Pinning des versions dans le Dockerfile
FROM node:21.7.3-alpineExercise 6 : Script pour lancer l’app dans Docker
"scripts": {
"run-docker": "docker run -p 8080:8080 app:1.0.0"
}Usage :
npm run run-docker
# Puis accéder à http://localhost:8080Section 4 : Gestion des dépendances avec NPM
Exercise 7 : Endpoint /name/:name
app.get('/name/:name', (req, res) => {
res.send(`Hello, ${req.params.name}!`);
});Exercise 8 : dependencies vs devDependencies
npm install express --save # dependency
npm install --save-dev jest nodemon # devDependencydependencies: packages nécessaires en productiondevDependencies: packages pour le développement uniquement
Section 5 : Tests automatisés
Installation de Jest et SuperTest, séparation du code en deux fichiers :
app.js: logique de l’appserver.js: démarre le serveur
Exercise 9 : Endpoint /add/:a/:b avec tests
app.get('/add/:a/:b', (req, res) => {
const a = parseFloat(req.params.a);
const b = parseFloat(req.params.b);
if (isNaN(a) || isNaN(b)) {
return res.status(400).send('Invalid input: a and b must be numbers');
}
res.send(`${a + b}`);
});Tests associés :
describe('GET /add/:a/:b', () => {
it('should return the sum of two numbers', async () => {
const res = await request(app).get('/add/2/3');
expect(res.statusCode).toBe(200);
expect(res.text).toBe('5');
});
it('should return 400 for invalid inputs', async () => {
const res = await request(app).get('/add/abc/3');
expect(res.statusCode).toBe(400);
});
it('should handle negative numbers', async () => {
const res = await request(app).get('/add/-5/3');
expect(res.statusCode).toBe(200);
expect(res.text).toBe('-2');
});
it('should handle decimal numbers', async () => {
const res = await request(app).get('/add/1.5/2.5');
expect(res.statusCode).toBe(200);
expect(res.text).toBe('4');
});
});Exercise 10 : Code coverage
npm run test:coverageSection 6 : Tests automatisés pour le code OpenTofu
Tests d’infrastructure pour le module Lambda avec l’API Gateway.
Exercise 11 : Réponse JSON
exports.handler = (event, context, callback) => {
callback(null, {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: "Hello, World!" })
});
};Exercise 12 : Test négatif (404)
Test qui vérifie qu’une route inexistante retourne bien un 404.
Section 7 : Bonnes pratiques de test
Exercise 13 : TDD (Test-Driven Development)
Test écrit avant l’implémentation :
describe('GET /multiply/:a/:b', () => {
test('It should return the product of two numbers', async () => {
const response = await request(app).get('/multiply/3/4');
expect(response.statusCode).toBe(200);
expect(response.text).toBe('12');
});
});Implémentation :
app.get('/multiply/:a/:b', (req, res) => {
const a = parseFloat(req.params.a);
const b = parseFloat(req.params.b);
if (isNaN(a) || isNaN(b)) {
return res.status(400).send('Invalid input');
}
res.send(`${a * b}`);
});Exercise 14 : Analyse du coverage
Après avoir exécuté npm run test:coverage, nous avons obtenu un coverage proche de 100% sur app.js.
Conclusion
Ce lab nous a permis de comprendre et mettre en pratique :
- La gestion de version avec Git (branches, tags, rebase)
- La collaboration via GitHub (pull requests, branch protection)
- L’automatisation des tâches avec NPM (build, test, Docker)
- Les tests automatisés avec Jest et SuperTest pour une app Node.js
- Les tests d’infrastructure avec OpenTofu sur AWS
La combinaison de ces pratiques améliore la qualité du code, facilite la collaboration en équipe et sécurise les déploiements.