feat(database): initialize investigation database

This commit is contained in:
grayTerminal-sh 2026-07-15 13:39:39 +02:00
parent 08d568a002
commit fbe1cd11fb
10 changed files with 1430 additions and 25 deletions

View file

@ -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

View 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
```

View 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 é 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.

View file

@ -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
View 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

Binary file not shown.

339
tests/test_database.c Normal file
View 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.

View file

@ -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.
*/ */