auto_clea

This commit is contained in:
LeCoffre Deployment 2025-09-25 15:08:13 +00:00
parent d94a8c430e
commit be23ef77c0
15 changed files with 3 additions and 467 deletions

@ -1 +1 @@
Subproject commit 1b5f672f62aad7232606a7c15b9c796617b5bdc9
Subproject commit 09c2d1f96915979ef21b233bd1535225f981a4c5

@ -1 +1 @@
Subproject commit 2a62c2649d10d33be58c1c644e70cac156dffc48
Subproject commit 573a3e49e5391c49ed8169711d14275fd5d70dad

@ -1 +1 @@
Subproject commit 912fe1c96dc9b2032f95405a5a137e3e552fe419
Subproject commit b7ca256fcfb7cf16e19e91f8f10f087e43ced546

View File

@ -1,16 +0,0 @@
### Objet
Axes de tests pour `ihm_client` (sans exemples).
### Couverture prioritaire
- **Chargement iframe**: initialisation, messages parent ↔ iframe
- **Auth/Token**: création, stockage, renouvellement, invalidation
- **Pages**: home, chat, account, process, signature (navigation, états)
- **WebSockets**: connexion, reconnexion, messages, erreurs
- **Modales**: confirmation/creation/waiting/validation-rule
### Performance
- **Workers**: cache et base de données (latences, tailles)
### Sécurité
- **postMessage**: validation dorigine, format messages
- **Stockage**: isolation domain/cookies/localStorage

View File

@ -1,27 +0,0 @@
### Objet
Axes de tests pour `lecoffre-front` (sans exemples dimplémentation).
### Couverture prioritaire
- **Routage**: accessibilité des pages clés sous `basePath` `/lecoffre`
- **Auth**: parcours login client et callbacks (Id360/IdNot)
- **Tableau client**: chargement données, états vides, erreurs
- **Dossiers/Documents**: création/affichage, téléchargements, filigrane
- **Souscription**: parcours complet (erreur/succès/gestion)
- **Notifications/Toasts**: affichage cohérent des erreurs
### Données et intégrations
- **API Back**: validation des URL via `NEXT_PUBLIC_BACK_API_*` et `NEXT_PUBLIC_API_URL`
- **IdNot Auth**: vérifier que lappel se fait en POST `/api/v1/idnot/auth` avec `{ code }` dans le corps, et quaucune URL longue nest utilisée.
- **Idnot/Docaposte**: vérification des redirections et scopes
### Non-régressions UI/UX
- **DesignSystem**: composants critiques (boutons, tabs, formulaires)
- **Accessibilité**: focus, contrastes, navigation clavier
### Performance
- **Chargement initial**: taille bundle avec `React.lazy`/`Suspense` si applicable
- **Rendu**: éviter re-renders via stores et mémoïsations locales
### Sécurité
- **Données sensibles**: absence de secrets dans `NEXT_PUBLIC_*`
- **JWT**: décodage côté client limité aux besoins

View File

@ -1,32 +0,0 @@
### Plan de tests CI/CD
Ce document liste les scénarios de test pour valider la chaîne CI/CD décrite dans `docs/ci.md`.
### Pré-requis
- Accès au registre Scaleway avec droits de push/pull.
- Accès au cluster Kubernetes `lecoffre` et à Vault (lecture des chemins référencés).
- BuildKit activé et agent SSH opérationnel pour laccès `git.4nkweb.com`.
### Tests de build
- Vérifier linstallation des dépendances avec accès SSH aux ressources privées.
- Exécuter `npm run build` et confirmer la génération sans erreurs.
### Tests dimage Docker
- Construire limage avec le forward SSH.
- Valider la taille, les couches, lutilisateur non-root, et lexécution `npm run start`.
- Pousser limage taguée (ex. `ext`) vers le registry `git.4nkweb.com` et vérifier la présence.
### Tests Kubernetes
- Appliquer les manifests/Helm sur un environnement de test.
- Valider la création de l`ExternalSecret` et du `imagePullSecret`.
- Vérifier que le `Deployment` démarre, que Vault injecte les variables, et que le `Service` et `Ingress` sont fonctionnels.
- Vérifier que `NEXT_PUBLIC_4NK_URL` (origin) et `NEXT_PUBLIC_4NK_IFRAME_URL` (URL complète) sont bien injectées au build (présentes dans `/lecoffre-front/.next/standalone/.env` ou via `next.config.js`) et cohérentes avec liframe réellement servie.
### Observabilité et rollback
- Vérifier les logs dinjection Vault et de lapplication.
- Tester un rollback dimage (retag vers version précédente) et vérifier la restauration.

View File

@ -1,28 +0,0 @@
### Tests image "ext"
Objectif: vérifier que l'image démarre et que les URLs d'API proviennent des variables d'environnement.
Plan de test manuel:
1. Vérifier l'injection des variables NEXT_PUBLIC_*
```
docker pull git.4nkweb.com/4nk/lecoffre-front:ext
docker run --rm git.4nkweb.com/4nk/lecoffre-front:ext sh -lc "env | grep '^NEXT_PUBLIC_' | sort"
```
Attendus (exemples):
- `NEXT_PUBLIC_4NK_URL`, `NEXT_PUBLIC_4NK_IFRAME_URL` définies
- `NEXT_PUBLIC_API_URL` et `NEXT_PUBLIC_BACK_API_*` cohérentes
2. Vérifier que l'app démarre
```
docker run --rm -p 3001:3000 git.4nkweb.com/4nk/lecoffre-front:ext
# Ouvrir http://localhost:3001/lecoffre
```
Critères de réussite:
- Le conteneur écoute sur 3000 et répond.
- Les URLs d'API proviennent des variables d'environnement.

View File

@ -1,27 +0,0 @@
## Axes de tests — lecoffre_node
### Pré-requis
- Nginx rechargé et conf active (`dev4.4nkweb.com.conf`)
- Réseau Docker `4nk_node_btcnet` présent
- Volumes montés (`4nk_node_bitcoin_data`, `blindbit_data`, `sdk_data`)
### Démarrage et dépendances
- Ordre de démarrage observé vs recommandé (voir docs/analyse.md)
- Healthchecks: `bitcoin` (CLI), `blindbit` (HTTP), `sdk_relay` (`/health`)
### Routage Nginx
- `/back/*` et `/api/*` → backend 8080 (statuts 200, pas de HTML)
- `/lecoffre/` et `/_next/` → front 3004 avec basePath OK
- `/signer/` WebSocket (101), `/blindbit/` (200)
### CORS
- Origines autorisées: `http://dev3.4nkweb.com`, `https://dev4.4nkweb.com`
- Prévols `OPTIONS` (204) et en-têtes `Access-Control-*`
### Non-régression
- Absence de `localhost:8080` dans les bundles front servis via `/lecoffre/`
- Redirections locales `dev3.4nkweb.com``https://dev4.4nkweb.com/lecoffre/`
### Observabilité
- Journaux Nginx (`error.log`) sans erreurs après reload
- Logs `sdk_relay` présents dans `/home/bitcoin/.4nk/logs/sdk_relay.log`

View File

@ -1,71 +0,0 @@
### Tests de routage API et front (dev4)
Pré-requis: Nginx rechargé et services Docker démarrés.
1) Santé backend via Nginx (HTTP)
Commande:
curl -sS -D - http://127.0.0.1/back/v1/health -o /dev/null | sed -n '1,10p'
Attendu: `HTTP/1.1 200` (ou `302` selon auth), jamais du HTML de front.
2) Santé backend via Nginx (HTTPS)
Commande:
curl -sS -D - https://dev4.4nkweb.com/back/v1/health -o /dev/null | sed -n '1,10p'
Attendu: `HTTP/2 200`.
3) Front `/lecoffre` (HTTPS)
Commande:
curl -sS -D - https://dev4.4nkweb.com/lecoffre/ -o /dev/null | sed -n '1,10p'
Attendu: `HTTP/2 200` et pas de 301/302 en boucle.
### Plan de tests — routage API via Nginx
#### Pré-requis
- Nginx actif avec `conf/nginx/dev4.4nkweb.com.conf` chargé.
- Services: backend (8080), storage (8081), ws relay (8090), blindbit (8000), front (3000/3003).
#### Vérifications HTTP
- GET `https://dev4.4nkweb.com/back/health` → 200
- (Si alias validé) GET `https://dev4.4nkweb.com/api/health` → 200
- GET `https://dev4.4nkweb.com/storage/health` → 200
#### Vérifications front
- Ouvrir `https://dev4.4nkweb.com/lecoffre/` → 200, pas derreurs CORS ni `ERR_BLOCKED_BY_CLIENT`.
- Ouvrir la console réseau: les appels API doivent cibler `/back/...` (ou `/api/...`) sous le même hôte.
#### Non-régression: absence de `localhost:8080` dans les bundles
- Télécharger la page et scanner les scripts:
- `curl -sS https://dev4.4nkweb.com/lecoffre/ -o /tmp/lecoffre_index.html`
- `grep -oP '(?<=<script src=")[^"]+' /tmp/lecoffre_index.html | sed 's#^/#https://dev4.4nkweb.com/#' | while read u; do curl -sS "$u" | grep -q 'localhost:8080' && echo "KO: $u"; done`
- Attendu: aucun `KO:`
#### Vérification réécriture `sub_filter`
- Contrôler quaucune requête ne vise `http://localhost:8080`:
- Recharger avec cache vidé et surveiller longlet Réseau.
- Télécharger une ressource HTML/JS et vérifier labsence de `http://localhost:8080`.
#### Redirections locales
- `curl -I http://dev3.4nkweb.com/` → 301 Location `https://dev4.4nkweb.com/lecoffre/`
- `curl -I http://dev3.4nkweb.com/authorized-client?code=ABC` → 301 Location `https://dev4.4nkweb.com/lecoffre/authorized-client?code=ABC`
#### WebSocket
- `wss://dev4.4nkweb.com/ws` handshake OK (101)
#### Journaux Nginx
- `error.log`: aucun message critique après rechargement.

View File

@ -1,18 +0,0 @@
# Test manuel: import des descripteurs Signet
## Préparation
- Vérifier `miner/.env.signet` (tprv, fingerprint, path)
## Étapes
1. `cd miner && ./tools/import_signet_descriptors.sh`
2. Vérifier sortie:
- Deux entrées `success: true` (external/internal)
- Une entrée `success: true` pour le challenge (warnings admis)
- `COINBASE_ADDRESS=tb1...` affichée
3. `docker compose up -d --build signet_miner`
4. `docker logs --tail=200 signet_miner`
- Attendre `Mined block at height ...`
## Échecs courants
- `Cannot import descriptor without private keys...` => utiliser descripteurs xprv+#checksum, pas la version normalisée (tpub).
- Wallet non-descriptor => recréer via le script (unload + rm -rf + createwallet descriptors=true).

View File

@ -1,27 +0,0 @@
# Tests de fumée dev4
HTTP/HTTPS
- GET / (ihm_client) attend 200 → Observé: 200
- GET /lecoffre/ attend 200 → Observé: 404 (page 404 servie, app OK sous /lecoffre/404)
- GET /back/ attend 200 → Observé: 200
- GET /storage/ attend 200 → Observé: 404 (à valider: endpoint /storage/health?)
WebSocket
- wss://dev4.4nkweb.com/ws handshake OK → À vérifier (outil ws requis côté serveur)
Redirections
- GET http://dev3.4nkweb.com/ attend 301 → Location: https://dev4.4nkweb.com/lecoffre/
- GET http://dev3.4nkweb.com/authorized-client?code=... attend 301 → Location: https://dev4.4nkweb.com/lecoffre/authorized-client?code=...
DNS côté client
- dev3.4nkweb.com doit résoudre vers 92.243.24.12 (pas 127.0.0.1)
- Test: nslookup dev3.4nkweb.com sur machine cliente
Nginx
- Certificats valides
- Pas derreurs critiques dans error.log
Résultats (17/09/2025)
- /lecoffre/ → 404
- /lecoffre/404 → 404 (avec `runtimeConfig` présent et `assetPrefix` = /lecoffre)
- /back/ → 200
- /blindbit/ → 200
- /storage/ → 404

View File

@ -1,38 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
echo "[TEST] sdk_relay: vérification santé"
health=$(curl -sS http://127.0.0.1:8091/health || true)
echo "Health: ${health}"
if [[ "${health}" != *'"status":"ok"'* ]]; then
echo "[ERREUR] Healthcheck non OK" >&2
exit 1
fi
echo "[TEST] sdk_relay: variables d'environnement effectives"
envs=$(docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' sdk_relay | sort)
echo "ENV:\n${envs}"
echo "Vérif HOME=/home/bitcoin"
grep -q '^HOME=/home/bitcoin$' <<<"${envs}"
echo "Vérif RUST_LOG=DEBUG"
grep -q '^RUST_LOG=DEBUG$' <<<"${envs}"
echo "Vérif NODE_OPTIONS --max-old-space-size=2048"
grep -q '^NODE_OPTIONS=--max-old-space-size=2048$' <<<"${envs}"
echo "[TEST] sdk_relay: logs récents (erreurs connues)"
logs=$(docker logs --since=10m sdk_relay 2>&1 || true)
echo "--- DERNIERS LOGS (tronqués) ---"
echo "${logs}" | tail -n 200
echo "--- FIN LOGS ---"
if echo "${logs}" | grep -qi 'failed to open bitcoind cookie file'; then
echo "[ERREUR] Problème d'accès au cookie RPC bitcoin détecté" >&2
exit 1
fi
echo "[SUCCES] Tests sdk_relay passés"

View File

@ -1,23 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
URL="http://localhost:8091/health"
echo "[tests] Vérification /health: $URL"
http_code=$(curl -s -o /tmp/health_body.txt -w "%{http_code}" "$URL")
body=$(cat /tmp/health_body.txt)
echo "HTTP $http_code"
echo "Body: $body"
if [[ "$http_code" != "200" ]]; then
echo "Échec: code HTTP inattendu" >&2
exit 1
fi
if [[ "$body" != '{"status":"ok"}' ]]; then
echo "Échec: corps inattendu" >&2
exit 1
fi
echo "Succès: endpoint /health opérationnel"

View File

@ -1,22 +0,0 @@
use std::time::Duration;
use tokio::time::timeout;
use tokio_tungstenite::connect_async;
#[tokio::test(flavor = "multi_thread")]
async fn ws_connects_on_localhost_8090() {
if std::env::var("RUN_WS_TEST").ok().as_deref() != Some("1") {
// Test conditionnel: désactivé par défaut pour éviter les échecs en l'absence de serveur WS local
return;
}
let url = std::env::var("SDK_RELAY_WS_URL").unwrap_or_else(|_| "ws://0.0.0.0:8090".to_string());
let connect_fut = connect_async(url);
let res = timeout(Duration::from_secs(3), connect_fut).await;
match res {
Ok(Ok((_stream, _resp))) => {
// Succès si la poignée de main WS passe
}
Ok(Err(e)) => panic!("Échec connexion WebSocket: {e}"),
Err(_) => panic!("Timeout connexion WebSocket"),
}
}

View File

@ -1,135 +0,0 @@
use sdk_storage::{StorageService, unix_to_system_time, create_app};
use tempfile::TempDir;
use surf::Client;
#[async_std::test]
async fn store_and_retrieve_hex_in_tempdir() {
let td = TempDir::new().unwrap();
let dir_path = td.path().to_string_lossy().to_string();
let svc = StorageService::new(dir_path);
let key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
let value = b"hello";
let expires = Some(unix_to_system_time(60 + sdk_storage::system_time_to_unix(std::time::SystemTime::now())));
svc.store_data(key, value, expires).await.unwrap();
let got = svc.retrieve_data(key).await.unwrap();
assert_eq!(got, value);
}
#[async_std::test]
async fn conflict_on_duplicate_key() {
let td = TempDir::new().unwrap();
let dir_path = td.path().to_string_lossy().to_string();
let svc = StorageService::new(dir_path);
let key = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
let value = b"data";
svc.store_data(key, value, None).await.unwrap();
let err = svc.store_data(key, value, None).await.err().expect("should error");
assert_eq!(err.status(), tide::StatusCode::Conflict);
}
#[async_std::test]
async fn cleanup_removes_expired() {
let td = TempDir::new().unwrap();
let dir_path = td.path().to_string_lossy().to_string();
let svc = StorageService::new(dir_path.clone());
let key = "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
let value = b"x";
// expiration très proche: maintenant - 1s
let past = sdk_storage::system_time_to_unix(std::time::SystemTime::now()).saturating_sub(1);
let expires = Some(unix_to_system_time(past));
svc.store_data(key, value, expires).await.unwrap();
// cleanup one-shot
svc.cleanup_expired_files_once().await.unwrap();
let res = svc.retrieve_data(key).await;
assert!(res.is_err(), "expired key should be removed");
}
#[async_std::test]
async fn http_store_success_and_conflicts_and_invalids() {
// app with permanent=false so default TTL applies when missing
let td = TempDir::new().unwrap();
let storage = td.path().to_string_lossy().to_string();
let mut app = create_app(false, storage);
let listener = async_std::net::TcpListener::bind("0.0.0.0:0").await.unwrap();
let addr = listener.local_addr().unwrap();
async_std::task::spawn(async move { app.listen(listener).await.unwrap() });
let client = Client::new();
let base = format!("http://{}", addr);
// success
let key = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
let body = serde_json::json!({"key": key, "value": "01aa", "ttl": 120});
let res = client.post(format!("{}/store", base)).body_json(&body).unwrap().await.unwrap();
assert!(res.status().is_success());
// conflict
let res2 = client.post(format!("{}/store", base)).body_json(&body).unwrap().await.unwrap();
assert_eq!(res2.status(), 409);
// invalid key
let bad = serde_json::json!({"key": "xyz", "value": "01"});
let res3 = client.post(format!("{}/store", base)).body_json(&bad).unwrap().await.unwrap();
assert_eq!(res3.status(), 400);
// invalid value
let badv = serde_json::json!({"key": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", "value": "zz"});
let res4 = client.post(format!("{}/store", base)).body_json(&badv).unwrap().await.unwrap();
assert_eq!(res4.status(), 400);
}
#[async_std::test]
async fn http_retrieve_success_and_invalid_and_notfound() {
let td = TempDir::new().unwrap();
let storage = td.path().to_string_lossy().to_string();
let mut app = create_app(true, storage.clone());
let listener = async_std::net::TcpListener::bind("0.0.0.0:0").await.unwrap();
let addr = listener.local_addr().unwrap();
async_std::task::spawn(async move { app.listen(listener).await.unwrap() });
let client = Client::new();
let base = format!("http://{}", addr);
// prepare stored value (permanent mode)
let svc = StorageService::new(storage);
let key = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
svc.store_data(key, b"hi", None).await.unwrap();
// success
let mut res = client.get(format!("{}/retrieve/{}", base, key)).await.unwrap();
assert!(res.status().is_success());
let v: serde_json::Value = res.body_json().await.unwrap();
assert_eq!(v["value"].as_str().unwrap(), hex::encode("hi"));
// invalid key
let res2 = client.get(format!("{}/retrieve/{}", base, "bad")).await.unwrap();
assert_eq!(res2.status(), 400);
// not found
let k2 = "1111111111111111111111111111111111111111111111111111111111111111";
let res3 = client.get(format!("{}/retrieve/{}", base, k2)).await.unwrap();
assert_eq!(res3.status(), 404);
}
#[async_std::test]
async fn http_health_ok() {
let td = TempDir::new().unwrap();
let storage = td.path().to_string_lossy().to_string();
let mut app = create_app(true, storage);
let listener = async_std::net::TcpListener::bind("0.0.0.0:0").await.unwrap();
let addr = listener.local_addr().unwrap();
async_std::task::spawn(async move { app.listen(listener).await.unwrap() });
let client = Client::new();
let base = format!("http://{}", addr);
let mut res = client.get(format!("{}/health", base)).await.unwrap();
assert!(res.status().is_success());
let v: serde_json::Value = res.body_json().await.unwrap();
assert_eq!(v["message"].as_str().unwrap(), "ok");
}