Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
npm-debug.log
tests
.git
.github
.gitignore
*.md
jest.config.js
24 changes: 21 additions & 3 deletions .github/workflows/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,31 @@ jobs:
cache: 'npm'

- name: Install dependencies
run: npm install
run: npm ci

- name: Run Tests
- name: Run Unit Tests
run: npm run test:unit

- name: Run Web/Integration Tests
run: npm run test:web

Comment on lines +34 to +39
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI runs the test suite multiple times (unit, web, then all again for coverage), which increases duration and compute cost. Consider running only npm test (coverage) and, if you still want separate visibility, use Jest projects/groups or split by testMatch while generating coverage once.

Suggested change
- name: Run Unit Tests
run: npm run test:unit
- name: Run Web/Integration Tests
run: npm run test:web

Copilot uses AI. Check for mistakes.
- name: Run All Tests with Coverage
run: npm test

- name: Build Docker images
- name: Build Docker image
run: |
docker build -t printalexis/mon-service:1 -f Dockerfile .
continue-on-error: true

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
if: always()
continue-on-error: true
with:
files: ./coverage/lcov.info
flags: unittests
name: mon-service-coverage
fail_ci_if_error: false
verbose: true


16 changes: 13 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install
COPY . .

RUN npm ci --only=production && npm cache clean --force

COPY src/ ./src/
COPY index.js ./

EXPOSE 8080
ENTRYPOINT ["node", "index.js"]

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:8080/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HEALTHCHECK script doesn't consume the response body or explicitly handle connection errors/timeouts, which can lead to hanging checks or noisy/unreliable health reporting. Consider draining the response (r.resume()), adding an error handler, and ensuring the process exits deterministically on failure (or use a lightweight tool like wget if available).

Suggested change
CMD node -e "require('http').get('http://localhost:8080/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
CMD node -e "const http = require('http'); const req = http.get('http://localhost:8080/health', (r) => { r.resume(); process.exit(r.statusCode === 200 ? 0 : 1); }); req.on('error', () => process.exit(1)); req.setTimeout(2500, () => { req.destroy(); process.exit(1); });"

Copilot uses AI. Check for mistakes.

CMD ["node", "index.js"]
157 changes: 157 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Mon Service

Service Node.js avec architecture modulaire (Entities, Services, Controllers)

## Structure du projet

```
mon-service/
├── src/
│ ├── entities/ # Modèles de données (User.js)
│ ├── services/ # Logique métier (UserService.js)
│ ├── controllers/ # Gestion des routes (UserController.js)
├── tests/
│ ├── unit/ # Tests unitaires
│ ├── web/ # Tests d'intégration web
├── index.js # Point d'entrée
├── package.json # Dépendances
├── Dockerfile # Configuration Docker
└── jest.config.js # Configuration Jest
```

## Installation

```bash
npm install
```

## Scripts disponibles

```bash
# Lancer tous les tests avec couverture
npm test

# Lancer uniquement les tests unitaires
npm run test:unit

# Lancer uniquement les tests web/intégration
npm run test:web

# Lancer les tests en mode watch
npm run test:watch

# Démarrer le serveur
npm start
```

## Tests

Le projet inclut :
- **Tests unitaires** : Validation des entités et logique des services
- **Tests d'intégration** : Vérification des endpoints API via HTTP

Couverture minimale requise : 70%

### Couverture de code (Jest + Codecov)

Ce projet est en **Node.js**, donc l'equivalent de JaCoCo est la couverture native de **Jest** (Istanbul).

- Rapport local HTML : `coverage/lcov-report/index.html`
- Rapport local machine-readable : `coverage/lcov.info`

#### Generer un rapport localement

```bash
npm test
```

Puis ouvrir :

- `coverage/lcov-report/index.html`

Vous y verrez :

- couverture globale (statements, branches, functions, lines)
- details par fichier
- lignes couvertes/non couvertes

#### Upload automatique vers Codecov (GitHub Actions)

Le workflow `.github/workflows/action.yml` :

- execute les tests avec couverture
- upload `coverage/lcov.info` vers Codecov
- fonctionne sur `push` et `pull_request` (branches `master` et `develop`)

Pour activer Codecov :

1. Connectez le repository sur https://app.codecov.io.
2. Si le repo est prive, ajoutez le secret GitHub `CODECOV_TOKEN`.
3. Verifiez dans une PR que le statut/rapport de couverture apparait.

#### Bonnes pratiques

- Viser au moins 80% en production
- Tester les cas de succes et d'erreur
- Ne pas gonfler artificiellement la couverture avec des tests sans valeur
- Utiliser la couverture comme indicateur, pas comme seul objectif

### Lancer les tests

```bash
# Tous les tests
npm test

# Uniquement unitaires
npm run test:unit

# Uniquement web
npm run test:web
```

## API Endpoints

| Méthode | Route | Description |
|---------|-------|-------------|
| GET | `/` | Message de bienvenue |
| GET | `/health` | Vérification de santé du service |
| GET | `/users` | Récupérer tous les utilisateurs |
| GET | `/users/:id` | Récupérer un utilisateur |
| POST | `/users` | Créer un utilisateur |
| PUT | `/users/:id` | Mettre à jour un utilisateur |
| DELETE | `/users/:id` | Supprimer un utilisateur |
Comment on lines +114 to +122
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Markdown table has an extra leading | on each line (|| ...), which can break rendering on GitHub. Use standard table syntax with a single leading pipe per row.

Copilot uses AI. Check for mistakes.

### Exemples

```bash
# Créer un utilisateur
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"john@example.com"}'

# Récupérer les utilisateurs
curl http://localhost:8080/users

# Vérifier la santé
curl http://localhost:8080/health
```

## Docker

### Build

```bash
docker build -t mon-service:1 .
```

### Run

```bash
docker run -p 8080:8080 mon-service:1
```

## CI/CD

Les tests s'exécutent automatiquement via GitHub Actions pour chaque push et PR.

Voir `.github/workflows/action.yml` pour la configuration complète.
39 changes: 35 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
const express = require('express');
const UserService = require('./src/services/UserService');
const UserController = require('./src/controllers/UserController');

const app = express();
const PORT = process.env.PORT || 8080;

// Middleware
app.use(express.json());

// Initialize services and controllers
const userService = new UserService();
const userController = new UserController(userService);

// Routes
app.get('/', (req, res) => {
res.send('Hello from Docker!');
res.json({ message: 'Welcome to mon-service API' });
});

app.get('/health', (req, res) => userController.getHealth(req, res));

// User routes
app.get('/users', (req, res) => userController.getAllUsers(req, res));
app.get('/users/:id', (req, res) => userController.getUserById(req, res));
app.post('/users', (req, res) => userController.createUser(req, res));
app.put('/users/:id', (req, res) => userController.updateUser(req, res));
app.delete('/users/:id', (req, res) => userController.deleteUser(req, res));

// Error handling
app.use((req, res) => {
res.status(404).json({ error: 'Not found' });
});

app.listen(8080, () => {
console.log('Server running on port 8080');
});
// Start server only when running this file directly.
if (require.main === module) {
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
}

module.exports = app;
23 changes: 23 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = {
testEnvironment: 'node',
collectCoverageFrom: [
'src/**/*.js',
'!src/**/*.test.js'
],
coveragePathIgnorePatterns: [
'/node_modules/',
'/tests/'
],
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70
}
},
testMatch: [
'**/tests/**/*.test.js'
]
};

Loading
Loading