feat(database): initialize investigation database
This commit is contained in:
parent
08d568a002
commit
fbe1cd11fb
10 changed files with 1430 additions and 25 deletions
18
Makefile
18
Makefile
|
|
@ -33,6 +33,7 @@ TEST_NODE = tests/test_investigation_node
|
||||||
TEST_TREE_MODEL = tests/test_investigation_tree_model
|
TEST_TREE_MODEL = tests/test_investigation_tree_model
|
||||||
TEST_TREE_BUILDER = tests/test_investigation_tree_builder
|
TEST_TREE_BUILDER = tests/test_investigation_tree_builder
|
||||||
TEST_PROJECT = tests/test_investigation_project
|
TEST_PROJECT = tests/test_investigation_project
|
||||||
|
TEST_DATABASE = tests/test_database
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
|
||||||
|
|
@ -60,19 +61,27 @@ $(TEST_TREE_BUILDER): \
|
||||||
|
|
||||||
$(TEST_PROJECT): \
|
$(TEST_PROJECT): \
|
||||||
tests/test_investigation_project.c \
|
tests/test_investigation_project.c \
|
||||||
src/core/investigation_project.c
|
src/core/investigation_project.c \
|
||||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
src/database/database.c
|
||||||
|
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
|
||||||
|
|
||||||
|
$(TEST_DATABASE): \
|
||||||
|
tests/test_database.c \
|
||||||
|
src/database/database.c
|
||||||
|
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
|
||||||
|
|
||||||
test: \
|
test: \
|
||||||
$(TEST_NODE) \
|
$(TEST_NODE) \
|
||||||
$(TEST_TREE_MODEL) \
|
$(TEST_TREE_MODEL) \
|
||||||
$(TEST_TREE_BUILDER) \
|
$(TEST_TREE_BUILDER) \
|
||||||
$(TEST_PROJECT)
|
$(TEST_PROJECT) \
|
||||||
|
$(TEST_DATABASE)
|
||||||
@echo "Exécution des tests..."
|
@echo "Exécution des tests..."
|
||||||
@./$(TEST_NODE)
|
@./$(TEST_NODE)
|
||||||
@./$(TEST_TREE_MODEL)
|
@./$(TEST_TREE_MODEL)
|
||||||
@./$(TEST_TREE_BUILDER)
|
@./$(TEST_TREE_BUILDER)
|
||||||
@./$(TEST_PROJECT)
|
@./$(TEST_PROJECT)
|
||||||
|
@./$(TEST_DATABASE)
|
||||||
@echo "Tous les tests sont valides."
|
@echo "Tous les tests sont valides."
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|
@ -86,6 +95,7 @@ clean:
|
||||||
$(TEST_NODE) \
|
$(TEST_NODE) \
|
||||||
$(TEST_TREE_MODEL) \
|
$(TEST_TREE_MODEL) \
|
||||||
$(TEST_TREE_BUILDER) \
|
$(TEST_TREE_BUILDER) \
|
||||||
@./$(TEST_PROJECT)
|
$(TEST_PROJECT) \
|
||||||
|
$(TEST_DATABASE)
|
||||||
|
|
||||||
.PHONY: clean run test
|
.PHONY: clean run test
|
||||||
|
|
|
||||||
368
docs/tickets/closed/TICKET-022.md
Normal file
368
docs/tickets/closed/TICKET-022.md
Normal file
|
|
@ -0,0 +1,368 @@
|
||||||
|
# Ticket #022
|
||||||
|
|
||||||
|
## Titre
|
||||||
|
|
||||||
|
Initialiser la base SQLite d'une enquête.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
Créer et initialiser correctement le fichier :
|
||||||
|
|
||||||
|
```text
|
||||||
|
00_BaseDeDonnees/Enquete.sqlite
|
||||||
|
```
|
||||||
|
|
||||||
|
avec un premier schéma SQLite versionné.
|
||||||
|
|
||||||
|
La base doit contenir les métadonnées minimales permettant d'identifier
|
||||||
|
l'enquête et la version du schéma.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```text
|
||||||
|
InvestigationProject
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Database
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
SQLite
|
||||||
|
```
|
||||||
|
|
||||||
|
`InvestigationProject` orchestre la création de l'enquête.
|
||||||
|
|
||||||
|
Le module `Database` est seul responsable de l'ouverture de SQLite,
|
||||||
|
de l'exécution du schéma et de la fermeture de la base.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsabilités
|
||||||
|
|
||||||
|
### InvestigationProject
|
||||||
|
|
||||||
|
Le module doit :
|
||||||
|
|
||||||
|
- créer l'arborescence ;
|
||||||
|
- demander au module `Database` d'initialiser `Enquete.sqlite` ;
|
||||||
|
- considérer la création comme échouée si l'initialisation SQLite échoue ;
|
||||||
|
- nettoyer toute l'enquête créée en cas d'échec.
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
Le module doit :
|
||||||
|
|
||||||
|
- ouvrir ou créer le fichier SQLite ;
|
||||||
|
- démarrer une transaction ;
|
||||||
|
- créer le schéma initial ;
|
||||||
|
- insérer les métadonnées ;
|
||||||
|
- valider la transaction ;
|
||||||
|
- annuler la transaction en cas d'erreur ;
|
||||||
|
- fermer proprement la connexion.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nouveaux fichiers
|
||||||
|
|
||||||
|
```text
|
||||||
|
include/database/database.h
|
||||||
|
src/database/database.c
|
||||||
|
```
|
||||||
|
|
||||||
|
Éventuellement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
include/database/schema.h
|
||||||
|
src/database/schema.c
|
||||||
|
```
|
||||||
|
|
||||||
|
si le schéma devient trop volumineux pour rester dans `database.c`.
|
||||||
|
|
||||||
|
Pour ce ticket, un seul module `database.c` est acceptable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interface publique attendue
|
||||||
|
|
||||||
|
```c
|
||||||
|
bool database_initialize(
|
||||||
|
const char *database_path
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
La fonction retourne :
|
||||||
|
|
||||||
|
```c
|
||||||
|
true
|
||||||
|
```
|
||||||
|
|
||||||
|
si la base a été correctement initialisée.
|
||||||
|
|
||||||
|
Elle retourne :
|
||||||
|
|
||||||
|
```c
|
||||||
|
false
|
||||||
|
```
|
||||||
|
|
||||||
|
en cas d'erreur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schéma initial
|
||||||
|
|
||||||
|
### Table `metadata`
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE metadata
|
||||||
|
(
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT NOT NULL
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Métadonnées obligatoires
|
||||||
|
|
||||||
|
```text
|
||||||
|
schema_version
|
||||||
|
application
|
||||||
|
created_at
|
||||||
|
investigation_uuid
|
||||||
|
```
|
||||||
|
|
||||||
|
Valeurs attendues :
|
||||||
|
|
||||||
|
```text
|
||||||
|
schema_version = 1
|
||||||
|
application = Labfy Investigation
|
||||||
|
created_at = date UTC ISO 8601
|
||||||
|
investigation_uuid = UUID unique
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemple :
|
||||||
|
|
||||||
|
```text
|
||||||
|
2026-07-14T18:42:15Z
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table `investigation`
|
||||||
|
|
||||||
|
Créer également une table minimale représentant l'enquête :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE investigation
|
||||||
|
(
|
||||||
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
root_path TEXT NOT NULL,
|
||||||
|
created_at TEXT NOT NULL
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
La table contient une seule ligne correspondant à l'enquête courante.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Données nécessaires
|
||||||
|
|
||||||
|
`database_initialize()` doit recevoir suffisamment d'informations pour
|
||||||
|
initialiser correctement la base.
|
||||||
|
|
||||||
|
L'interface pourra donc évoluer vers :
|
||||||
|
|
||||||
|
```c
|
||||||
|
bool database_initialize(
|
||||||
|
const char *database_path,
|
||||||
|
const char *investigation_name,
|
||||||
|
const char *investigation_root_path
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette signature est préférée pour éviter que le module `Database`
|
||||||
|
reconstruise ou devine des informations métier.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## UUID
|
||||||
|
|
||||||
|
L'UUID doit être généré avec GLib :
|
||||||
|
|
||||||
|
```c
|
||||||
|
g_uuid_string_random()
|
||||||
|
```
|
||||||
|
|
||||||
|
La chaîne retournée doit être libérée avec :
|
||||||
|
|
||||||
|
```c
|
||||||
|
g_free(uuid);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Date de création
|
||||||
|
|
||||||
|
La date doit être produite en UTC avec GLib.
|
||||||
|
|
||||||
|
Format attendu :
|
||||||
|
|
||||||
|
```text
|
||||||
|
YYYY-MM-DDTHH:MM:SSZ
|
||||||
|
```
|
||||||
|
|
||||||
|
La date doit être enregistrée à la fois :
|
||||||
|
|
||||||
|
- dans `metadata.created_at` ;
|
||||||
|
- dans `investigation.created_at`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Transaction
|
||||||
|
|
||||||
|
Toute l'initialisation doit se dérouler dans une transaction :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
BEGIN IMMEDIATE;
|
||||||
|
```
|
||||||
|
|
||||||
|
Puis :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
COMMIT;
|
||||||
|
```
|
||||||
|
|
||||||
|
En cas d'erreur :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ROLLBACK;
|
||||||
|
```
|
||||||
|
|
||||||
|
Une base partiellement initialisée ne doit jamais être considérée comme valide.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Intégration avec InvestigationProject
|
||||||
|
|
||||||
|
`investigation_project_create()` ne doit plus créer un fichier vide avec :
|
||||||
|
|
||||||
|
```c
|
||||||
|
g_file_set_contents(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
Il doit construire le chemin de la base puis appeler :
|
||||||
|
|
||||||
|
```c
|
||||||
|
database_initialize(
|
||||||
|
database_path,
|
||||||
|
investigation_name,
|
||||||
|
investigation_path
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Si l'appel échoue :
|
||||||
|
|
||||||
|
- le fichier SQLite éventuel est supprimé ;
|
||||||
|
- tous les dossiers créés sont supprimés ;
|
||||||
|
- `investigation_project_create()` retourne `NULL`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
À partir de ce ticket, `investigation_project_validate()` doit toujours
|
||||||
|
vérifier la présence du fichier SQLite, mais pas encore son contenu SQL.
|
||||||
|
|
||||||
|
La validation du schéma sera ajoutée dans un prochain ticket dédié.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hors périmètre
|
||||||
|
|
||||||
|
Ce ticket ne doit pas :
|
||||||
|
|
||||||
|
- créer les tables Preuves ;
|
||||||
|
- créer les tables Entites ;
|
||||||
|
- créer les relations ;
|
||||||
|
- gérer les migrations ;
|
||||||
|
- ouvrir une enquête existante ;
|
||||||
|
- modifier GTK ;
|
||||||
|
- exposer directement `sqlite3 *` hors du module Database.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contraintes techniques
|
||||||
|
|
||||||
|
- C17 ;
|
||||||
|
- SQLite3 ;
|
||||||
|
- GLib ;
|
||||||
|
- aucune dépendance GTK ;
|
||||||
|
- aucun état global ;
|
||||||
|
- requêtes SQL centralisées dans `database.c` ;
|
||||||
|
- fermeture garantie de la connexion ;
|
||||||
|
- transaction obligatoire ;
|
||||||
|
- documentation Doxygen ;
|
||||||
|
- compilation sans warning.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Créer :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tests/test_database.c
|
||||||
|
```
|
||||||
|
|
||||||
|
Le test doit :
|
||||||
|
|
||||||
|
- créer un dossier temporaire ;
|
||||||
|
- initialiser une base SQLite ;
|
||||||
|
- vérifier que le fichier existe ;
|
||||||
|
- ouvrir la base en lecture ;
|
||||||
|
- vérifier la présence de la table `metadata` ;
|
||||||
|
- vérifier la présence de la table `investigation` ;
|
||||||
|
- vérifier `schema_version = 1` ;
|
||||||
|
- vérifier `application = Labfy Investigation` ;
|
||||||
|
- vérifier que `created_at` n'est pas vide ;
|
||||||
|
- vérifier que l'UUID n'est pas vide ;
|
||||||
|
- vérifier la ligne unique de la table `investigation` ;
|
||||||
|
- vérifier le nom et le chemin racine ;
|
||||||
|
- vérifier qu'une initialisation sur un chemin invalide échoue ;
|
||||||
|
- nettoyer complètement les fichiers temporaires.
|
||||||
|
|
||||||
|
Faire également évoluer :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tests/test_investigation_project.c
|
||||||
|
```
|
||||||
|
|
||||||
|
pour vérifier que la base créée n'est plus vide.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Critères d'acceptation
|
||||||
|
|
||||||
|
- [ ] Le projet compile sans warning.
|
||||||
|
- [ ] `make test` reste entièrement valide.
|
||||||
|
- [ ] `Enquete.sqlite` est une vraie base SQLite.
|
||||||
|
- [ ] La table `metadata` existe.
|
||||||
|
- [ ] La table `investigation` existe.
|
||||||
|
- [ ] `schema_version` vaut `1`.
|
||||||
|
- [ ] Une date UTC est enregistrée.
|
||||||
|
- [ ] Un UUID est généré.
|
||||||
|
- [ ] L'enquête est enregistrée dans la base.
|
||||||
|
- [ ] L'initialisation est transactionnelle.
|
||||||
|
- [ ] Toute erreur provoque un nettoyage complet.
|
||||||
|
- [ ] Aucun type `sqlite3 *` n'est exposé publiquement.
|
||||||
|
- [ ] Aucune dépendance GTK.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit attendu
|
||||||
|
|
||||||
|
```text
|
||||||
|
feat(database): initialize investigation database
|
||||||
|
```
|
||||||
46
include/database/database.h
Normal file
46
include/database/database.h
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file database.h
|
||||||
|
* @brief Interface publique d'initialisation de la base SQLite d'une enquête.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef LABFY_INVESTIGATION_DATABASE_H
|
||||||
|
#define LABFY_INVESTIGATION_DATABASE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialise la base SQLite d'une enquête.
|
||||||
|
*
|
||||||
|
* La fonction crée ou ouvre le fichier SQLite indiqué, puis initialise
|
||||||
|
* transactionnellement le schéma minimal de l'enquête.
|
||||||
|
*
|
||||||
|
* Les informations suivantes sont enregistrées :
|
||||||
|
*
|
||||||
|
* - version du schéma ;
|
||||||
|
* - nom de l'application ;
|
||||||
|
* - date de création UTC ;
|
||||||
|
* - UUID de l'enquête ;
|
||||||
|
* - nom de l'enquête ;
|
||||||
|
* - chemin racine de l'enquête.
|
||||||
|
*
|
||||||
|
* Aucun handle sqlite3 n'est exposé au code appelant.
|
||||||
|
*
|
||||||
|
* @param database_path
|
||||||
|
* Chemin complet du fichier Enquete.sqlite.
|
||||||
|
*
|
||||||
|
* @param investigation_name
|
||||||
|
* Nom de l'enquête.
|
||||||
|
*
|
||||||
|
* @param investigation_root_path
|
||||||
|
* Chemin complet du dossier racine de l'enquête.
|
||||||
|
*
|
||||||
|
* @return true si la base a été correctement initialisée,
|
||||||
|
* sinon false.
|
||||||
|
*/
|
||||||
|
bool database_initialize(
|
||||||
|
const char *database_path,
|
||||||
|
const char *investigation_name,
|
||||||
|
const char *investigation_root_path
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
|
|
@ -4,6 +4,7 @@
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include "core/investigation_project.h"
|
#include "core/investigation_project.h"
|
||||||
|
#include "database/database.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -328,7 +329,6 @@ char *investigation_project_create(
|
||||||
char *database_path = NULL;
|
char *database_path = NULL;
|
||||||
|
|
||||||
GPtrArray *created_paths = NULL;
|
GPtrArray *created_paths = NULL;
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
if (!investigation_project_validate_create_parameters(
|
if (!investigation_project_validate_create_parameters(
|
||||||
parent_directory,
|
parent_directory,
|
||||||
|
|
@ -460,29 +460,12 @@ char *investigation_project_create(
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (!database_initialize(
|
||||||
* Pour ce ticket, on crée seulement un fichier vide.
|
|
||||||
*
|
|
||||||
* Le schéma SQLite sera ajouté dans un prochain ticket.
|
|
||||||
*/
|
|
||||||
if (!g_file_set_contents(
|
|
||||||
database_path,
|
database_path,
|
||||||
"",
|
investigation_name,
|
||||||
0,
|
investigation_path
|
||||||
&error
|
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
if (error != NULL)
|
|
||||||
{
|
|
||||||
g_warning(
|
|
||||||
"Impossible de créer '%s' : %s",
|
|
||||||
database_path,
|
|
||||||
error->message
|
|
||||||
);
|
|
||||||
|
|
||||||
g_clear_error(&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(database_path);
|
g_free(database_path);
|
||||||
|
|
||||||
investigation_project_cleanup_created_paths(
|
investigation_project_cleanup_created_paths(
|
||||||
|
|
|
||||||
621
src/database/database.c
Normal file
621
src/database/database.c
Normal file
|
|
@ -0,0 +1,621 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file database.c
|
||||||
|
* @brief Initialisation de la base SQLite d'une enquête.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "database/database.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Version actuelle du schéma SQLite.
|
||||||
|
*/
|
||||||
|
#define DATABASE_SCHEMA_VERSION "1"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Nom de l'application enregistré dans les métadonnées.
|
||||||
|
*/
|
||||||
|
#define DATABASE_APPLICATION_NAME "Labfy Investigation"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Crée la table contenant les métadonnées de l'enquête.
|
||||||
|
*/
|
||||||
|
static const char *const database_create_metadata_table_sql =
|
||||||
|
"CREATE TABLE metadata"
|
||||||
|
"("
|
||||||
|
" key TEXT PRIMARY KEY,"
|
||||||
|
" value TEXT NOT NULL"
|
||||||
|
");";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Crée la table représentant l'enquête courante.
|
||||||
|
*/
|
||||||
|
static const char *const database_create_investigation_table_sql =
|
||||||
|
"CREATE TABLE investigation"
|
||||||
|
"("
|
||||||
|
" id INTEGER PRIMARY KEY CHECK (id = 1),"
|
||||||
|
" name TEXT NOT NULL,"
|
||||||
|
" root_path TEXT NOT NULL,"
|
||||||
|
" created_at TEXT NOT NULL"
|
||||||
|
");";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Requête d'insertion d'une métadonnée.
|
||||||
|
*/
|
||||||
|
static const char *const database_insert_metadata_sql =
|
||||||
|
"INSERT INTO metadata (key, value) VALUES (?, ?);";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Requête d'insertion de l'enquête.
|
||||||
|
*/
|
||||||
|
static const char *const database_insert_investigation_sql =
|
||||||
|
"INSERT INTO investigation"
|
||||||
|
"("
|
||||||
|
" id,"
|
||||||
|
" name,"
|
||||||
|
" root_path,"
|
||||||
|
" created_at"
|
||||||
|
")"
|
||||||
|
"VALUES"
|
||||||
|
"("
|
||||||
|
" 1,"
|
||||||
|
" ?,"
|
||||||
|
" ?,"
|
||||||
|
" ?"
|
||||||
|
");";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Vérifie les paramètres publics de l'initialisation.
|
||||||
|
*/
|
||||||
|
static bool database_validate_initialize_parameters(
|
||||||
|
const char *database_path,
|
||||||
|
const char *investigation_name,
|
||||||
|
const char *investigation_root_path
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (database_path == NULL || database_path[0] == '\0')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (investigation_name == NULL || investigation_name[0] == '\0')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (investigation_root_path == NULL ||
|
||||||
|
investigation_root_path[0] == '\0')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Exécute une requête SQL statique.
|
||||||
|
*
|
||||||
|
* Cette fonction convient uniquement aux requêtes qui ne contiennent
|
||||||
|
* aucune donnée provenant de l'utilisateur.
|
||||||
|
*/
|
||||||
|
static bool database_execute_sql(
|
||||||
|
sqlite3 *database,
|
||||||
|
const char *sql
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char *error_message = NULL;
|
||||||
|
int result = SQLITE_ERROR;
|
||||||
|
|
||||||
|
if (database == NULL || sql == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_exec(
|
||||||
|
database,
|
||||||
|
sql,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&error_message
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Erreur SQLite : %s",
|
||||||
|
error_message != NULL
|
||||||
|
? error_message
|
||||||
|
: sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
sqlite3_free(error_message);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_free(error_message);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Insère une paire clé-valeur dans la table metadata.
|
||||||
|
*/
|
||||||
|
static bool database_insert_metadata(
|
||||||
|
sqlite3 *database,
|
||||||
|
sqlite3_stmt *statement,
|
||||||
|
const char *key,
|
||||||
|
const char *value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int result = SQLITE_ERROR;
|
||||||
|
|
||||||
|
if (database == NULL ||
|
||||||
|
statement == NULL ||
|
||||||
|
key == NULL ||
|
||||||
|
value == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_bind_text(
|
||||||
|
statement,
|
||||||
|
1,
|
||||||
|
key,
|
||||||
|
-1,
|
||||||
|
SQLITE_TRANSIENT
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible de lier la clé '%s' : %s",
|
||||||
|
key,
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_bind_text(
|
||||||
|
statement,
|
||||||
|
2,
|
||||||
|
value,
|
||||||
|
-1,
|
||||||
|
SQLITE_TRANSIENT
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible de lier la valeur de '%s' : %s",
|
||||||
|
key,
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_step(statement);
|
||||||
|
|
||||||
|
if (result != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible d'insérer la métadonnée '%s' : %s",
|
||||||
|
key,
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_reset(statement);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible de réinitialiser la requête metadata : %s",
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_clear_bindings(statement);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible d'effacer les paramètres metadata : %s",
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Insère toutes les métadonnées obligatoires.
|
||||||
|
*/
|
||||||
|
static bool database_insert_all_metadata(
|
||||||
|
sqlite3 *database,
|
||||||
|
const char *created_at,
|
||||||
|
const char *investigation_uuid
|
||||||
|
)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *statement = NULL;
|
||||||
|
int result = SQLITE_ERROR;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
if (database == NULL ||
|
||||||
|
created_at == NULL ||
|
||||||
|
investigation_uuid == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_prepare_v2(
|
||||||
|
database,
|
||||||
|
database_insert_metadata_sql,
|
||||||
|
-1,
|
||||||
|
&statement,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible de préparer l'insertion des métadonnées : %s",
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
success =
|
||||||
|
database_insert_metadata(
|
||||||
|
database,
|
||||||
|
statement,
|
||||||
|
"schema_version",
|
||||||
|
DATABASE_SCHEMA_VERSION
|
||||||
|
) &&
|
||||||
|
database_insert_metadata(
|
||||||
|
database,
|
||||||
|
statement,
|
||||||
|
"application",
|
||||||
|
DATABASE_APPLICATION_NAME
|
||||||
|
) &&
|
||||||
|
database_insert_metadata(
|
||||||
|
database,
|
||||||
|
statement,
|
||||||
|
"created_at",
|
||||||
|
created_at
|
||||||
|
) &&
|
||||||
|
database_insert_metadata(
|
||||||
|
database,
|
||||||
|
statement,
|
||||||
|
"investigation_uuid",
|
||||||
|
investigation_uuid
|
||||||
|
);
|
||||||
|
|
||||||
|
if (sqlite3_finalize(statement) != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible de finaliser la requête metadata : %s",
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Insère la ligne représentant l'enquête courante.
|
||||||
|
*/
|
||||||
|
static bool database_insert_investigation(
|
||||||
|
sqlite3 *database,
|
||||||
|
const char *investigation_name,
|
||||||
|
const char *investigation_root_path,
|
||||||
|
const char *created_at
|
||||||
|
)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *statement = NULL;
|
||||||
|
int result = SQLITE_ERROR;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
if (database == NULL ||
|
||||||
|
investigation_name == NULL ||
|
||||||
|
investigation_root_path == NULL ||
|
||||||
|
created_at == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_prepare_v2(
|
||||||
|
database,
|
||||||
|
database_insert_investigation_sql,
|
||||||
|
-1,
|
||||||
|
&statement,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible de préparer l'insertion de l'enquête : %s",
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_bind_text(
|
||||||
|
statement,
|
||||||
|
1,
|
||||||
|
investigation_name,
|
||||||
|
-1,
|
||||||
|
SQLITE_TRANSIENT
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_bind_text(
|
||||||
|
statement,
|
||||||
|
2,
|
||||||
|
investigation_root_path,
|
||||||
|
-1,
|
||||||
|
SQLITE_TRANSIENT
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_bind_text(
|
||||||
|
statement,
|
||||||
|
3,
|
||||||
|
created_at,
|
||||||
|
-1,
|
||||||
|
SQLITE_TRANSIENT
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_step(statement);
|
||||||
|
|
||||||
|
if (result != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible d'insérer l'enquête : %s",
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sqlite3_finalize(statement) != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible de finaliser la requête investigation : %s",
|
||||||
|
sqlite3_errmsg(database)
|
||||||
|
);
|
||||||
|
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Produit une date UTC au format ISO 8601 attendu.
|
||||||
|
*
|
||||||
|
* @return Une nouvelle chaîne à libérer avec g_free(), ou NULL.
|
||||||
|
*/
|
||||||
|
static char *database_create_utc_timestamp(void)
|
||||||
|
{
|
||||||
|
GDateTime *date_time = NULL;
|
||||||
|
char *timestamp = NULL;
|
||||||
|
|
||||||
|
date_time = g_date_time_new_now_utc();
|
||||||
|
|
||||||
|
if (date_time == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp = g_date_time_format(
|
||||||
|
date_time,
|
||||||
|
"%Y-%m-%dT%H:%M:%SZ"
|
||||||
|
);
|
||||||
|
|
||||||
|
g_date_time_unref(date_time);
|
||||||
|
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool database_initialize(
|
||||||
|
const char *database_path,
|
||||||
|
const char *investigation_name,
|
||||||
|
const char *investigation_root_path
|
||||||
|
)
|
||||||
|
{
|
||||||
|
sqlite3 *database = NULL;
|
||||||
|
char *created_at = NULL;
|
||||||
|
char *investigation_uuid = NULL;
|
||||||
|
|
||||||
|
int result = SQLITE_ERROR;
|
||||||
|
bool transaction_started = false;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
if (!database_validate_initialize_parameters(
|
||||||
|
database_path,
|
||||||
|
investigation_name,
|
||||||
|
investigation_root_path
|
||||||
|
))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
created_at = database_create_utc_timestamp();
|
||||||
|
|
||||||
|
if (created_at == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
investigation_uuid = g_uuid_string_random();
|
||||||
|
|
||||||
|
if (investigation_uuid == NULL)
|
||||||
|
{
|
||||||
|
g_free(created_at);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sqlite3_open_v2(
|
||||||
|
database_path,
|
||||||
|
&database,
|
||||||
|
SQLITE_OPEN_READWRITE |
|
||||||
|
SQLITE_OPEN_CREATE |
|
||||||
|
SQLITE_OPEN_PRIVATECACHE,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible d'ouvrir la base '%s' : %s",
|
||||||
|
database_path,
|
||||||
|
database != NULL
|
||||||
|
? sqlite3_errmsg(database)
|
||||||
|
: sqlite3_errstr(result)
|
||||||
|
);
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!database_execute_sql(
|
||||||
|
database,
|
||||||
|
"PRAGMA foreign_keys = ON;"
|
||||||
|
))
|
||||||
|
{
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!database_execute_sql(
|
||||||
|
database,
|
||||||
|
"BEGIN IMMEDIATE;"
|
||||||
|
))
|
||||||
|
{
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction_started = true;
|
||||||
|
|
||||||
|
if (!database_execute_sql(
|
||||||
|
database,
|
||||||
|
database_create_metadata_table_sql
|
||||||
|
))
|
||||||
|
{
|
||||||
|
goto rollback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!database_execute_sql(
|
||||||
|
database,
|
||||||
|
database_create_investigation_table_sql
|
||||||
|
))
|
||||||
|
{
|
||||||
|
goto rollback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!database_insert_all_metadata(
|
||||||
|
database,
|
||||||
|
created_at,
|
||||||
|
investigation_uuid
|
||||||
|
))
|
||||||
|
{
|
||||||
|
goto rollback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!database_insert_investigation(
|
||||||
|
database,
|
||||||
|
investigation_name,
|
||||||
|
investigation_root_path,
|
||||||
|
created_at
|
||||||
|
))
|
||||||
|
{
|
||||||
|
goto rollback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!database_execute_sql(
|
||||||
|
database,
|
||||||
|
"COMMIT;"
|
||||||
|
))
|
||||||
|
{
|
||||||
|
goto rollback;
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction_started = false;
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
rollback:
|
||||||
|
|
||||||
|
if (transaction_started)
|
||||||
|
{
|
||||||
|
if (!database_execute_sql(
|
||||||
|
database,
|
||||||
|
"ROLLBACK;"
|
||||||
|
))
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Échec du rollback de la base '%s'.",
|
||||||
|
database_path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction_started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
if (database != NULL)
|
||||||
|
{
|
||||||
|
result = sqlite3_close(database);
|
||||||
|
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible de fermer proprement la base '%s' : %s",
|
||||||
|
database_path,
|
||||||
|
sqlite3_errstr(result)
|
||||||
|
);
|
||||||
|
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(investigation_uuid);
|
||||||
|
g_free(created_at);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
BIN
tests/test_database
Executable file
BIN
tests/test_database
Executable file
Binary file not shown.
339
tests/test_database.c
Normal file
339
tests/test_database.c
Normal file
|
|
@ -0,0 +1,339 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file test_database.c
|
||||||
|
* @brief Tests d'intégration du module Database.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "database/database.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lit une valeur unique retournée par une requête SQL.
|
||||||
|
*
|
||||||
|
* @param database Base SQLite ouverte.
|
||||||
|
* @param sql Requête retournant une seule colonne et une seule ligne.
|
||||||
|
*
|
||||||
|
* @return Une nouvelle chaîne à libérer avec g_free(), ou NULL.
|
||||||
|
*/
|
||||||
|
static char *test_database_read_single_text(
|
||||||
|
sqlite3 *database,
|
||||||
|
const char *sql
|
||||||
|
)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *statement = NULL;
|
||||||
|
const unsigned char *text = NULL;
|
||||||
|
char *result_text = NULL;
|
||||||
|
int result = SQLITE_ERROR;
|
||||||
|
|
||||||
|
assert(database != NULL);
|
||||||
|
assert(sql != NULL);
|
||||||
|
|
||||||
|
result = sqlite3_prepare_v2(
|
||||||
|
database,
|
||||||
|
sql,
|
||||||
|
-1,
|
||||||
|
&statement,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
assert(statement != NULL);
|
||||||
|
|
||||||
|
result = sqlite3_step(statement);
|
||||||
|
|
||||||
|
assert(result == SQLITE_ROW);
|
||||||
|
|
||||||
|
text = sqlite3_column_text(
|
||||||
|
statement,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(text != NULL);
|
||||||
|
|
||||||
|
result_text = g_strdup(
|
||||||
|
(const char *)text
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(result_text != NULL);
|
||||||
|
|
||||||
|
result = sqlite3_finalize(statement);
|
||||||
|
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
|
||||||
|
return result_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Vérifie qu'une table existe.
|
||||||
|
*/
|
||||||
|
static void test_database_assert_table_exists(
|
||||||
|
sqlite3 *database,
|
||||||
|
const char *table_name
|
||||||
|
)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *statement = NULL;
|
||||||
|
int result = SQLITE_ERROR;
|
||||||
|
|
||||||
|
assert(database != NULL);
|
||||||
|
assert(table_name != NULL);
|
||||||
|
|
||||||
|
result = sqlite3_prepare_v2(
|
||||||
|
database,
|
||||||
|
"SELECT COUNT(*) "
|
||||||
|
"FROM sqlite_master "
|
||||||
|
"WHERE type = 'table' AND name = ?;",
|
||||||
|
-1,
|
||||||
|
&statement,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
assert(statement != NULL);
|
||||||
|
|
||||||
|
result = sqlite3_bind_text(
|
||||||
|
statement,
|
||||||
|
1,
|
||||||
|
table_name,
|
||||||
|
-1,
|
||||||
|
SQLITE_TRANSIENT
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
|
||||||
|
result = sqlite3_step(statement);
|
||||||
|
|
||||||
|
assert(result == SQLITE_ROW);
|
||||||
|
assert(sqlite3_column_int(statement, 0) == 1);
|
||||||
|
|
||||||
|
result = sqlite3_finalize(statement);
|
||||||
|
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Vérifie l'initialisation complète d'une base.
|
||||||
|
*/
|
||||||
|
static void test_database_initialize_valid_database(void)
|
||||||
|
{
|
||||||
|
char *temporary_directory = NULL;
|
||||||
|
char *database_path = NULL;
|
||||||
|
|
||||||
|
char *schema_version = NULL;
|
||||||
|
char *application_name = NULL;
|
||||||
|
char *created_at = NULL;
|
||||||
|
char *investigation_uuid = NULL;
|
||||||
|
char *investigation_name = NULL;
|
||||||
|
char *investigation_root_path = NULL;
|
||||||
|
char *investigation_created_at = NULL;
|
||||||
|
|
||||||
|
sqlite3 *database = NULL;
|
||||||
|
GError *error = NULL;
|
||||||
|
int result = SQLITE_ERROR;
|
||||||
|
|
||||||
|
temporary_directory = g_dir_make_tmp(
|
||||||
|
"labfy-database-test-XXXXXX",
|
||||||
|
&error
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(temporary_directory != NULL);
|
||||||
|
assert(error == NULL);
|
||||||
|
|
||||||
|
database_path = g_build_filename(
|
||||||
|
temporary_directory,
|
||||||
|
"Enquete.sqlite",
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(database_path != NULL);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
database_initialize(
|
||||||
|
database_path,
|
||||||
|
"Enquete_Test",
|
||||||
|
temporary_directory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
g_file_test(
|
||||||
|
database_path,
|
||||||
|
G_FILE_TEST_IS_REGULAR
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
result = sqlite3_open_v2(
|
||||||
|
database_path,
|
||||||
|
&database,
|
||||||
|
SQLITE_OPEN_READONLY,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
assert(database != NULL);
|
||||||
|
|
||||||
|
test_database_assert_table_exists(
|
||||||
|
database,
|
||||||
|
"metadata"
|
||||||
|
);
|
||||||
|
|
||||||
|
test_database_assert_table_exists(
|
||||||
|
database,
|
||||||
|
"investigation"
|
||||||
|
);
|
||||||
|
|
||||||
|
schema_version = test_database_read_single_text(
|
||||||
|
database,
|
||||||
|
"SELECT value FROM metadata "
|
||||||
|
"WHERE key = 'schema_version';"
|
||||||
|
);
|
||||||
|
|
||||||
|
application_name = test_database_read_single_text(
|
||||||
|
database,
|
||||||
|
"SELECT value FROM metadata "
|
||||||
|
"WHERE key = 'application';"
|
||||||
|
);
|
||||||
|
|
||||||
|
created_at = test_database_read_single_text(
|
||||||
|
database,
|
||||||
|
"SELECT value FROM metadata "
|
||||||
|
"WHERE key = 'created_at';"
|
||||||
|
);
|
||||||
|
|
||||||
|
investigation_uuid = test_database_read_single_text(
|
||||||
|
database,
|
||||||
|
"SELECT value FROM metadata "
|
||||||
|
"WHERE key = 'investigation_uuid';"
|
||||||
|
);
|
||||||
|
|
||||||
|
investigation_name = test_database_read_single_text(
|
||||||
|
database,
|
||||||
|
"SELECT name FROM investigation "
|
||||||
|
"WHERE id = 1;"
|
||||||
|
);
|
||||||
|
|
||||||
|
investigation_root_path = test_database_read_single_text(
|
||||||
|
database,
|
||||||
|
"SELECT root_path FROM investigation "
|
||||||
|
"WHERE id = 1;"
|
||||||
|
);
|
||||||
|
|
||||||
|
investigation_created_at = test_database_read_single_text(
|
||||||
|
database,
|
||||||
|
"SELECT created_at FROM investigation "
|
||||||
|
"WHERE id = 1;"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(strcmp(schema_version, "1") == 0);
|
||||||
|
assert(strcmp(application_name, "Labfy Investigation") == 0);
|
||||||
|
|
||||||
|
assert(created_at[0] != '\0');
|
||||||
|
assert(investigation_uuid[0] != '\0');
|
||||||
|
|
||||||
|
assert(strcmp(investigation_name, "Enquete_Test") == 0);
|
||||||
|
assert(strcmp(investigation_root_path, temporary_directory) == 0);
|
||||||
|
assert(strcmp(investigation_created_at, created_at) == 0);
|
||||||
|
|
||||||
|
result = sqlite3_close(database);
|
||||||
|
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
|
||||||
|
assert(g_remove(database_path) == 0);
|
||||||
|
assert(g_rmdir(temporary_directory) == 0);
|
||||||
|
|
||||||
|
g_free(investigation_created_at);
|
||||||
|
g_free(investigation_root_path);
|
||||||
|
g_free(investigation_name);
|
||||||
|
g_free(investigation_uuid);
|
||||||
|
g_free(created_at);
|
||||||
|
g_free(application_name);
|
||||||
|
g_free(schema_version);
|
||||||
|
g_free(database_path);
|
||||||
|
g_free(temporary_directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Vérifie le refus des paramètres invalides.
|
||||||
|
*/
|
||||||
|
static void test_database_initialize_invalid_parameters(void)
|
||||||
|
{
|
||||||
|
assert(
|
||||||
|
!database_initialize(
|
||||||
|
NULL,
|
||||||
|
"Enquete",
|
||||||
|
"/tmp/Enquete"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
!database_initialize(
|
||||||
|
"",
|
||||||
|
"Enquete",
|
||||||
|
"/tmp/Enquete"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
!database_initialize(
|
||||||
|
"/tmp/Enquete.sqlite",
|
||||||
|
NULL,
|
||||||
|
"/tmp/Enquete"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
!database_initialize(
|
||||||
|
"/tmp/Enquete.sqlite",
|
||||||
|
"",
|
||||||
|
"/tmp/Enquete"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
!database_initialize(
|
||||||
|
"/tmp/Enquete.sqlite",
|
||||||
|
"Enquete",
|
||||||
|
NULL
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
!database_initialize(
|
||||||
|
"/tmp/Enquete.sqlite",
|
||||||
|
"Enquete",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Vérifie l'échec sur un chemin dont le parent n'existe pas.
|
||||||
|
*/
|
||||||
|
static void test_database_initialize_missing_parent(void)
|
||||||
|
{
|
||||||
|
assert(
|
||||||
|
!database_initialize(
|
||||||
|
"/tmp/labfy-missing-parent/database/Enquete.sqlite",
|
||||||
|
"Enquete",
|
||||||
|
"/tmp/labfy-missing-parent"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
test_database_initialize_valid_database();
|
||||||
|
test_database_initialize_invalid_parameters();
|
||||||
|
test_database_initialize_missing_parent();
|
||||||
|
|
||||||
|
printf(
|
||||||
|
"Database : tous les tests sont valides.\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -8,6 +8,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gstdio.h>
|
#include <glib/gstdio.h>
|
||||||
|
|
@ -274,6 +275,43 @@ static void test_create_valid_investigation(void)
|
||||||
) == NULL
|
) == NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sqlite3 *database = NULL;
|
||||||
|
sqlite3_stmt *statement = NULL;
|
||||||
|
int result = SQLITE_ERROR;
|
||||||
|
|
||||||
|
result = sqlite3_open_v2(
|
||||||
|
database_path,
|
||||||
|
&database,
|
||||||
|
SQLITE_OPEN_READONLY,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
|
||||||
|
result = sqlite3_prepare_v2(
|
||||||
|
database,
|
||||||
|
"SELECT COUNT(*) "
|
||||||
|
"FROM sqlite_master "
|
||||||
|
"WHERE type='table' "
|
||||||
|
"AND name='metadata';",
|
||||||
|
-1,
|
||||||
|
&statement,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
|
||||||
|
result = sqlite3_step(statement);
|
||||||
|
|
||||||
|
assert(result == SQLITE_ROW);
|
||||||
|
assert(sqlite3_column_int(statement, 0) == 1);
|
||||||
|
|
||||||
|
result = sqlite3_finalize(statement);
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
|
||||||
|
result = sqlite3_close(database);
|
||||||
|
assert(result == SQLITE_OK);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Nettoyage complet.
|
* Nettoyage complet.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue