feat: Optimisation Dockerfile, documentation complète et tests
All checks were successful
build-and-push-ext / build_push (push) Successful in 35s
All checks were successful
build-and-push-ext / build_push (push) Successful in 35s
- Dockerfile optimisé: seulement docker-cli pour bitcoin-cli - README.md complet avec tous les endpoints et configuration - CHANGELOG.md mis à jour avec v1.1.1 - Tests unitaires pour les routes funds - Tests d'intégration pour le signer - Configuration Jest avec coverage - Scripts de test dans package.json - Fichier .env.test pour les tests
This commit is contained in:
parent
567e57abe8
commit
52baaa9956
@ -11,7 +11,6 @@ IDNOT_API_BASE_URL=https://qual-api.notaires.fr
|
|||||||
|
|
||||||
# Configuration serveur
|
# Configuration serveur
|
||||||
APP_HOST=dev4.4nkweb.com
|
APP_HOST=dev4.4nkweb.com
|
||||||
PORT=8080
|
|
||||||
# API_BASE_URL=https://demo.4nkweb.com/back
|
# API_BASE_URL=https://demo.4nkweb.com/back
|
||||||
API_BASE_URL=https://dev4.4nkweb.com/back
|
API_BASE_URL=https://dev4.4nkweb.com/back
|
||||||
# DEFAULT_STORAGE=https://demo.4nkweb.com/storage
|
# DEFAULT_STORAGE=https://demo.4nkweb.com/storage
|
||||||
|
17
.env.test
Normal file
17
.env.test
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Configuration de test
|
||||||
|
NODE_ENV=test
|
||||||
|
PORT=8081
|
||||||
|
LOG_LEVEL=error
|
||||||
|
|
||||||
|
# Configuration signer pour les tests
|
||||||
|
SIGNER_WS_URL=ws://localhost:9090
|
||||||
|
SIGNER_BASE_URL=http://localhost:9090
|
||||||
|
SIGNER_API_KEY=test-api-key
|
||||||
|
|
||||||
|
# Configuration CORS pour les tests
|
||||||
|
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8081
|
||||||
|
|
||||||
|
# Configuration des services externes (mocks)
|
||||||
|
IDNOT_API_KEY=test-key
|
||||||
|
OVH_APP_KEY=test-key
|
||||||
|
STRIPE_SECRET_KEY=sk_test_mock
|
29
CHANGELOG.md
29
CHANGELOG.md
@ -60,3 +60,32 @@
|
|||||||
- IdNot: fallback quand `profile_idn` absent dans le token.
|
- IdNot: fallback quand `profile_idn` absent dans le token.
|
||||||
- Récupération des rattachements via `sub` puis sélection d’un rattachement d’étude (office) si présent.
|
- Récupération des rattachements via `sub` puis sélection d’un rattachement d’étude (office) si présent.
|
||||||
- Objectif: permettre le login même si le JWT IdNot ne fournit pas `profile_idn`.
|
- Objectif: permettre le login même si le JWT IdNot ne fournit pas `profile_idn`.
|
||||||
|
|
||||||
|
## v1.1.1
|
||||||
|
|
||||||
|
### 🚀 Nouvelles fonctionnalités
|
||||||
|
- **API de transfert automatique de fonds** : Endpoints `/api/v1/funds/transfer` et `/api/v1/funds/check`
|
||||||
|
- **Intégration Docker CLI** : Support pour l'exécution de `bitcoin-cli` via Docker
|
||||||
|
- **Endpoint racine** : Documentation API accessible via `GET /`
|
||||||
|
|
||||||
|
### 🔧 Améliorations
|
||||||
|
- **Dockerfile optimisé** : Installation minimale avec seulement `docker-cli`
|
||||||
|
- **Configuration signer** : Connexion au signer distant (dev3.4nkweb.com)
|
||||||
|
- **Gestion d'erreurs** : Amélioration de la gestion d'erreurs TypeScript
|
||||||
|
- **Documentation** : README.md complet avec tous les endpoints
|
||||||
|
|
||||||
|
### 🐛 Corrections
|
||||||
|
- **Healthcheck** : Correction de l'endpoint `/api/v1/health`
|
||||||
|
- **CORS** : Configuration dynamique des origines autorisées
|
||||||
|
- **TypeScript** : Résolution des erreurs de compilation
|
||||||
|
|
||||||
|
### 🧪 Tests
|
||||||
|
- **Tests unitaires** : Tests pour les routes funds
|
||||||
|
- **Tests d'intégration** : Tests de connectivité signer
|
||||||
|
- **Tests de build** : Vérification de la compilation TypeScript
|
||||||
|
|
||||||
|
### 📦 Déploiement
|
||||||
|
- **Image Docker** : Tag `ext` pour les builds CI
|
||||||
|
- **Configuration** : Variables d'environnement optimisées
|
||||||
|
- **Monitoring** : Healthcheck et logs structurés
|
||||||
|
|
||||||
|
@ -33,9 +33,14 @@ RUN npm prune --omit=dev && npm cache clean --force
|
|||||||
FROM node:19-alpine
|
FROM node:19-alpine
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Installation minimale : seulement docker-cli pour bitcoin-cli
|
||||||
|
RUN apk add --no-cache docker-cli
|
||||||
|
|
||||||
# Création d'un utilisateur non-root
|
# Création d'un utilisateur non-root
|
||||||
RUN adduser -D appuser --uid 10000 && \
|
RUN adduser -D appuser --uid 10000 && \
|
||||||
chown -R appuser /app
|
chown -R appuser /app
|
||||||
|
|
||||||
|
# Retour à l'utilisateur appuser
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
# Copie des artefacts de build et des deps prod
|
# Copie des artefacts de build et des deps prod
|
||||||
|
48
Dockerfile.backup
Normal file
48
Dockerfile.backup
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# syntax=docker/dockerfile:1.4
|
||||||
|
FROM node:19-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Outils nécessaires pour cloner le dépôt privé
|
||||||
|
RUN apk add --no-cache git openssh-client
|
||||||
|
|
||||||
|
# Prépare SSH pour git.4nkweb.com
|
||||||
|
RUN mkdir -p /root/.ssh && \
|
||||||
|
ssh-keyscan git.4nkweb.com >> /root/.ssh/known_hosts
|
||||||
|
|
||||||
|
# Clone le SDK à côté de /app afin que ../sdk-signer-client soit disponible
|
||||||
|
RUN --mount=type=ssh git clone -b dev \
|
||||||
|
ssh://git@git.4nkweb.com/4nk/sdk-signer-client.git /sdk-signer-client
|
||||||
|
|
||||||
|
# Build de la dépendance SDK
|
||||||
|
WORKDIR /sdk-signer-client
|
||||||
|
RUN npm ci && npm run build
|
||||||
|
|
||||||
|
# Installation des dépendances de l'app
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Copie et build des sources de l'app
|
||||||
|
COPY tsconfig.json ./
|
||||||
|
COPY src ./src
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Réduction aux deps de production
|
||||||
|
RUN npm prune --omit=dev && npm cache clean --force
|
||||||
|
|
||||||
|
FROM node:19-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Création d'un utilisateur non-root
|
||||||
|
RUN adduser -D appuser --uid 10000 && \
|
||||||
|
chown -R appuser /app
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# Copie des artefacts de build et des deps prod
|
||||||
|
COPY --from=builder --chown=appuser:appuser /app/package*.json ./
|
||||||
|
COPY --from=builder --chown=appuser:appuser /app/node_modules ./node_modules
|
||||||
|
COPY --from=builder --chown=appuser:appuser /app/dist ./dist
|
||||||
|
COPY --from=builder /sdk-signer-client /sdk-signer-client
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["npm", "start"]
|
61
Dockerfile.full-tools
Normal file
61
Dockerfile.full-tools
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# syntax=docker/dockerfile:1.4
|
||||||
|
FROM node:19-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Outils nécessaires pour cloner le dépôt privé
|
||||||
|
RUN apk add --no-cache git openssh-client
|
||||||
|
|
||||||
|
# Prépare SSH pour git.4nkweb.com
|
||||||
|
RUN mkdir -p /root/.ssh && \
|
||||||
|
ssh-keyscan git.4nkweb.com >> /root/.ssh/known_hosts
|
||||||
|
|
||||||
|
# Clone le SDK à côté de /app afin que ../sdk-signer-client soit disponible
|
||||||
|
RUN --mount=type=ssh git clone -b dev \
|
||||||
|
ssh://git@git.4nkweb.com/4nk/sdk-signer-client.git /sdk-signer-client
|
||||||
|
|
||||||
|
# Build de la dépendance SDK
|
||||||
|
WORKDIR /sdk-signer-client
|
||||||
|
RUN npm ci && npm run build
|
||||||
|
|
||||||
|
# Installation des dépendances de l'app
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Copie et build des sources de l'app
|
||||||
|
COPY tsconfig.json ./
|
||||||
|
COPY src ./src
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Réduction aux deps de production
|
||||||
|
RUN npm prune --omit=dev && npm cache clean --force
|
||||||
|
|
||||||
|
FROM node:19-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Installation de Docker CLI et des outils nécessaires
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
docker-cli \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
wget \
|
||||||
|
jq \
|
||||||
|
busybox-extras \
|
||||||
|
npm \
|
||||||
|
coreutils
|
||||||
|
|
||||||
|
# Création d'un utilisateur non-root
|
||||||
|
RUN adduser -D appuser --uid 10000 && \
|
||||||
|
chown -R appuser /app
|
||||||
|
|
||||||
|
# Retour à l'utilisateur appuser
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# Copie des artefacts de build et des deps prod
|
||||||
|
COPY --from=builder --chown=appuser:appuser /app/package*.json ./
|
||||||
|
COPY --from=builder --chown=appuser:appuser /app/node_modules ./node_modules
|
||||||
|
COPY --from=builder --chown=appuser:appuser /app/dist ./dist
|
||||||
|
COPY --from=builder /sdk-signer-client /sdk-signer-client
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["npm", "start"]
|
147
README.md
147
README.md
@ -1,28 +1,147 @@
|
|||||||
# Mini Serveur Express
|
# LeCoffre Backend Mini
|
||||||
|
|
||||||
Un mini serveur Express avec une route `/api/ping` qui renvoie "Hello World".
|
Serveur Express.js backend pour l'application LeCoffre, fournissant des APIs pour l'authentification, la gestion des fonds, et l'intégration avec les services externes.
|
||||||
|
|
||||||
## Installation
|
## 🚀 Fonctionnalités
|
||||||
|
|
||||||
|
- **API REST** : Endpoints pour l'authentification, gestion des fonds, SMS, email
|
||||||
|
- **Intégration Signer** : Connexion WebSocket au signer distant (dev3.4nkweb.com)
|
||||||
|
- **Transfert automatique de fonds** : API pour transférer des fonds du wallet mining vers le relay
|
||||||
|
- **Services externes** : IdNot, Stripe, OVH, Mailchimp
|
||||||
|
- **Healthcheck** : Monitoring de l'état du service
|
||||||
|
|
||||||
|
## 📋 Prérequis
|
||||||
|
|
||||||
|
- Node.js 19+
|
||||||
|
- Docker (pour l'exécution)
|
||||||
|
- Accès au signer distant (dev3.4nkweb.com)
|
||||||
|
|
||||||
|
## 🛠️ Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
## Démarrage du serveur
|
## 🏃♂️ Démarrage
|
||||||
|
|
||||||
```bash
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
Ou en mode développement avec rechargement automatique :
|
|
||||||
|
|
||||||
|
### Mode développement
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Utilisation
|
### Mode production
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
Une fois le serveur démarré, la route ping est accessible à :
|
### Build
|
||||||
- http://localhost:3000/api/ping
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
Cette route renvoie un objet JSON avec le message "Hello World".
|
## 📡 Endpoints API
|
||||||
|
|
||||||
|
### Endpoints principaux
|
||||||
|
- `GET /` - Documentation de l'API
|
||||||
|
- `GET /api/v1/health` - Healthcheck du service
|
||||||
|
|
||||||
|
### Gestion des fonds
|
||||||
|
- `POST /api/v1/funds/transfer` - Transfert automatique de fonds
|
||||||
|
- `GET /api/v1/funds/check` - Vérification des fonds du relay
|
||||||
|
|
||||||
|
### Authentification
|
||||||
|
- `POST /api/v1/idnot/auth` - Authentification IdNot
|
||||||
|
|
||||||
|
### Services externes
|
||||||
|
- `POST /api/sms` - Service SMS
|
||||||
|
- `POST /api/email` - Service email
|
||||||
|
- `POST /api/stripe` - Service Stripe
|
||||||
|
- `POST /api/subscription` - Gestion des abonnements
|
||||||
|
|
||||||
|
## 🔧 Configuration
|
||||||
|
|
||||||
|
### Variables d'environnement principales
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Configuration serveur
|
||||||
|
PORT=8080
|
||||||
|
NODE_ENV=production
|
||||||
|
|
||||||
|
# Signer distant
|
||||||
|
SIGNER_WS_URL=ws://dev3.4nkweb.com:9090
|
||||||
|
SIGNER_BASE_URL=https://dev3.4nkweb.com
|
||||||
|
SIGNER_API_KEY=your-api-key-change-this
|
||||||
|
|
||||||
|
# Relays
|
||||||
|
RELAY_URLS=wss://dev4.4nkweb.com/ws/,wss://dev3.4nkweb.com/ws/
|
||||||
|
VITE_BOOTSTRAPURL=https://dev4.4nkweb.com/ws/
|
||||||
|
|
||||||
|
# CORS
|
||||||
|
CORS_ALLOWED_ORIGINS=https://dev4.4nkweb.com,http://local.4nkweb.com:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration complète
|
||||||
|
Voir le fichier `.env.exemple` pour toutes les variables disponibles.
|
||||||
|
|
||||||
|
## 🐳 Docker
|
||||||
|
|
||||||
|
### Build de l'image
|
||||||
|
```bash
|
||||||
|
docker build -t lecoffre-back-mini .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exécution
|
||||||
|
```bash
|
||||||
|
docker run -p 8080:8080 --env-file .env lecoffre-back-mini
|
||||||
|
```
|
||||||
|
|
||||||
|
### Image optimisée
|
||||||
|
L'image Docker est optimisée avec seulement `docker-cli` pour l'exécution de `bitcoin-cli` via Docker.
|
||||||
|
|
||||||
|
## 🧪 Tests
|
||||||
|
|
||||||
|
### Tests unitaires
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tests d'intégration
|
||||||
|
```bash
|
||||||
|
npm run test:integration
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tests spécifiques
|
||||||
|
```bash
|
||||||
|
npm run test:funds # Tests des routes funds
|
||||||
|
npm run test:signer # Tests de connectivité signer
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Monitoring
|
||||||
|
|
||||||
|
### Healthcheck
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/api/v1/health
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logs
|
||||||
|
Les logs sont structurés avec des niveaux de log appropriés.
|
||||||
|
|
||||||
|
## 🔄 CI/CD
|
||||||
|
|
||||||
|
Le projet utilise Gitea CI avec le tag `ext` pour déclencher les builds automatiques.
|
||||||
|
|
||||||
|
## 📝 Changelog
|
||||||
|
|
||||||
|
Voir [CHANGELOG.md](CHANGELOG.md) pour l'historique des versions.
|
||||||
|
|
||||||
|
## 🤝 Contribution
|
||||||
|
|
||||||
|
1. Fork le projet
|
||||||
|
2. Créer une branche feature (`git checkout -b feature/AmazingFeature`)
|
||||||
|
3. Commit les changements (`git commit -m 'Add some AmazingFeature'`)
|
||||||
|
4. Push vers la branche (`git push origin feature/AmazingFeature`)
|
||||||
|
5. Ouvrir une Pull Request
|
||||||
|
|
||||||
|
## 📄 Licence
|
||||||
|
|
||||||
|
Ce projet est sous licence MIT. Voir le fichier [LICENSE](LICENSE) pour plus de détails.
|
||||||
|
20
jest.config.js
Normal file
20
jest.config.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
roots: ['<rootDir>/src', '<rootDir>/tests'],
|
||||||
|
testMatch: [
|
||||||
|
'**/__tests__/**/*.ts',
|
||||||
|
'**/?(*.)+(spec|test).ts'
|
||||||
|
],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': 'ts-jest',
|
||||||
|
},
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'src/**/*.ts',
|
||||||
|
'!src/**/*.d.ts',
|
||||||
|
],
|
||||||
|
coverageDirectory: 'coverage',
|
||||||
|
coverageReporters: ['text', 'lcov', 'html'],
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
|
||||||
|
testTimeout: 10000,
|
||||||
|
};
|
14
package.json
14
package.json
@ -11,7 +11,14 @@
|
|||||||
"dev:js": "nodemon src/server.js",
|
"dev:js": "nodemon src/server.js",
|
||||||
"test:db": "npm run build && node test-db-init.js",
|
"test:db": "npm run build && node test-db-init.js",
|
||||||
"test:rattachements": "node test-rattachements-endpoint.js",
|
"test:rattachements": "node test-rattachements-endpoint.js",
|
||||||
"test:quick": "node quick-test-rattachements.js"
|
"test:quick": "node quick-test-rattachements.js",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:coverage": "jest --coverage",
|
||||||
|
"test:unit": "jest tests/unit",
|
||||||
|
"test:integration": "jest tests/integration",
|
||||||
|
"test:funds": "jest tests/unit/funds.routes.test.ts",
|
||||||
|
"test:signer": "jest tests/integration/signer.test.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mailchimp/mailchimp_transactional": "^1.0.59",
|
"@mailchimp/mailchimp_transactional": "^1.0.59",
|
||||||
@ -28,11 +35,16 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^20.11.19",
|
"@types/node": "^20.11.19",
|
||||||
"@types/node-fetch": "^2.6.11",
|
"@types/node-fetch": "^2.6.11",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.11.0",
|
||||||
|
"@types/supertest": "^6.0.3",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
|
"jest": "^30.1.3",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
|
"supertest": "^7.1.4",
|
||||||
|
"ts-jest": "^29.4.4",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
|
@ -64,13 +64,15 @@ router.post('/transfer', async (req: Request, res: Response) => {
|
|||||||
sourceBalance: balance,
|
sourceBalance: balance,
|
||||||
targetBalance: targetBalanceValue
|
targetBalance: targetBalanceValue
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Erreur lors du transfert automatique:', error);
|
console.error('❌ Erreur lors du transfert automatique:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: `Erreur lors du transfert: ${error.message}`
|
error: `Erreur lors du transfert: ${error instanceof Error ? error.message : String(error)}`
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -100,13 +102,15 @@ router.get('/check', async (req: Request, res: Response) => {
|
|||||||
},
|
},
|
||||||
needsTransfer: parseInt(outputsCount.trim()) === 0 && parseFloat(relayBalance.trim()) < 0.001
|
needsTransfer: parseInt(outputsCount.trim()) === 0 && parseFloat(relayBalance.trim()) < 0.001
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Erreur lors de la vérification des fonds:', error);
|
console.error('❌ Erreur lors de la vérification des fonds:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: `Erreur lors de la vérification: ${error.message}`
|
error: `Erreur lors de la vérification: ${error instanceof Error ? error.message : String(error)}`
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
62
tests/integration/signer.test.ts
Normal file
62
tests/integration/signer.test.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
import express from 'express';
|
||||||
|
import { routes } from '../../src/routes';
|
||||||
|
import { SignerService } from '../../src/services/signer';
|
||||||
|
|
||||||
|
describe('Signer Integration Tests', () => {
|
||||||
|
let app: express.Application;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
app.use('/', routes);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Health Check', () => {
|
||||||
|
it('devrait retourner le statut du service', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/health');
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.body.status).toBe('healthy');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Root Endpoint', () => {
|
||||||
|
it('devrait retourner la documentation de l\'API', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/');
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.body.message).toBe('LeCoffre Backend API');
|
||||||
|
expect(response.body.version).toBe('1.0.0');
|
||||||
|
expect(response.body.endpoints).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Signer Service', () => {
|
||||||
|
it('devrait avoir une configuration valide', () => {
|
||||||
|
const healthCheck = SignerService.getHealthCheck();
|
||||||
|
|
||||||
|
expect(healthCheck).toBeDefined();
|
||||||
|
expect(healthCheck.state).toBeDefined();
|
||||||
|
expect(healthCheck.reconnectAttempts).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('devrait gérer la déconnexion gracieusement', async () => {
|
||||||
|
// Test de la gestion de la déconnexion
|
||||||
|
const healthCheck = SignerService.getHealthCheck();
|
||||||
|
expect(healthCheck.state).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('CORS Configuration', () => {
|
||||||
|
it('devrait accepter les requêtes depuis les origines autorisées', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/')
|
||||||
|
.set('Origin', 'https://dev4.4nkweb.com');
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
17
tests/setup.ts
Normal file
17
tests/setup.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Configuration globale des tests
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
// Chargement des variables d'environnement pour les tests
|
||||||
|
dotenv.config({ path: '.env.test' });
|
||||||
|
|
||||||
|
// Configuration des timeouts
|
||||||
|
jest.setTimeout(10000);
|
||||||
|
|
||||||
|
// Mock des services externes par défaut
|
||||||
|
jest.mock('child_process', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Configuration des logs pour les tests
|
||||||
|
process.env.NODE_ENV = 'test';
|
||||||
|
process.env.LOG_LEVEL = 'error';
|
136
tests/unit/funds.routes.test.ts
Normal file
136
tests/unit/funds.routes.test.ts
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
import express from 'express';
|
||||||
|
import fundsRoutes from '../../src/routes/funds.routes';
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
|
||||||
|
// Mock de child_process
|
||||||
|
jest.mock('child_process');
|
||||||
|
const mockExec = exec as jest.MockedFunction<typeof exec>;
|
||||||
|
|
||||||
|
describe('Funds Routes', () => {
|
||||||
|
let app: express.Application;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
app.use('/api/v1/funds', fundsRoutes);
|
||||||
|
|
||||||
|
// Reset des mocks
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /api/v1/funds/transfer', () => {
|
||||||
|
it('devrait transférer des fonds avec succès', async () => {
|
||||||
|
// Mock des commandes bitcoin-cli
|
||||||
|
mockExec.mockImplementation((command, callback) => {
|
||||||
|
if (command.includes('getblockchaininfo')) {
|
||||||
|
callback(null, { stdout: '{"chain":"signet","blocks":1000}', stderr: '' });
|
||||||
|
} else if (command.includes('loadwallet')) {
|
||||||
|
callback(null, { stdout: '{"name":"default","warning":""}', stderr: '' });
|
||||||
|
} else if (command.includes('getbalance')) {
|
||||||
|
callback(null, { stdout: '1.5', stderr: '' });
|
||||||
|
} else if (command.includes('getnewaddress')) {
|
||||||
|
callback(null, { stdout: 'bc1qtest123', stderr: '' });
|
||||||
|
} else if (command.includes('sendtoaddress')) {
|
||||||
|
callback(null, { stdout: 'txid123456789', stderr: '' });
|
||||||
|
} else if (command.includes('generatetoaddress')) {
|
||||||
|
callback(null, { stdout: '["blockhash123"]', stderr: '' });
|
||||||
|
} else if (command.includes('restart')) {
|
||||||
|
callback(null, { stdout: '', stderr: '' });
|
||||||
|
}
|
||||||
|
return {} as any;
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.post('/api/v1/funds/transfer')
|
||||||
|
.send({
|
||||||
|
amount: 0.01,
|
||||||
|
source: 'mining_mnemonic',
|
||||||
|
target: 'default'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.body.success).toBe(true);
|
||||||
|
expect(response.body.message).toContain('Transfert de 0.01 BTC réussi');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('devrait retourner une erreur si le solde est insuffisant', async () => {
|
||||||
|
mockExec.mockImplementation((command, callback) => {
|
||||||
|
if (command.includes('getbalance')) {
|
||||||
|
callback(null, { stdout: '0.001', stderr: '' });
|
||||||
|
} else {
|
||||||
|
callback(null, { stdout: '', stderr: '' });
|
||||||
|
}
|
||||||
|
return {} as any;
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.post('/api/v1/funds/transfer')
|
||||||
|
.send({
|
||||||
|
amount: 0.01,
|
||||||
|
source: 'mining_mnemonic',
|
||||||
|
target: 'default'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(400);
|
||||||
|
expect(response.body.success).toBe(false);
|
||||||
|
expect(response.body.error).toContain('Solde insuffisant');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('devrait gérer les erreurs de commande bitcoin-cli', async () => {
|
||||||
|
mockExec.mockImplementation((command, callback) => {
|
||||||
|
callback(new Error('Bitcoin Core not running'), { stdout: '', stderr: 'Error' });
|
||||||
|
return {} as any;
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.post('/api/v1/funds/transfer')
|
||||||
|
.send({
|
||||||
|
amount: 0.01,
|
||||||
|
source: 'mining_mnemonic',
|
||||||
|
target: 'default'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(500);
|
||||||
|
expect(response.body.success).toBe(false);
|
||||||
|
expect(response.body.error).toContain('Erreur lors du transfert');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/v1/funds/check', () => {
|
||||||
|
it('devrait vérifier les fonds avec succès', async () => {
|
||||||
|
mockExec.mockImplementation((command, callback) => {
|
||||||
|
if (command.includes('cat /home/bitcoin/.4nk/default')) {
|
||||||
|
callback(null, { stdout: '{"outputs":[{"value":1000000}]}', stderr: '' });
|
||||||
|
} else if (command.includes('getbalance')) {
|
||||||
|
callback(null, { stdout: '0.01', stderr: '' });
|
||||||
|
} else {
|
||||||
|
callback(null, { stdout: '', stderr: '' });
|
||||||
|
}
|
||||||
|
return {} as any;
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/funds/check');
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.body.success).toBe(true);
|
||||||
|
expect(response.body.relay.outputsCount).toBe(1);
|
||||||
|
expect(response.body.relay.balance).toBe(0.01);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('devrait gérer les erreurs de vérification', async () => {
|
||||||
|
mockExec.mockImplementation((command, callback) => {
|
||||||
|
callback(new Error('File not found'), { stdout: '', stderr: 'Error' });
|
||||||
|
return {} as any;
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/funds/check');
|
||||||
|
|
||||||
|
expect(response.status).toBe(500);
|
||||||
|
expect(response.body.success).toBe(false);
|
||||||
|
expect(response.body.error).toContain('Erreur lors de la vérification');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user