From 137e75722d8649523f0da92586077ba11b48de5e Mon Sep 17 00:00:00 2001 From: grayTerminal-sh Date: Thu, 16 Jul 2026 12:41:34 +0200 Subject: [PATCH] feat(dao): add investigation record loader --- Makefile | 28 +- docs/tickets/{open => closed}/TICKET-026.md | 0 docs/tickets/open/TICKET-027.md | 535 ++++++++++++++++++++ include/dao/investigation_dao.h | 25 + include/database/error.h | 15 + include/models/investigation_record.h | 92 ++++ labfy-investigation | Bin 144768 -> 150216 bytes src/dao/investigation_dao.c | 197 +++++++ src/database/error.c | 13 + src/database/statement.c | 25 +- src/models/investigation_record.c | 199 ++++++++ tests/test_database | Bin 41112 -> 41112 bytes tests/test_database.c | 14 + tests/test_error | Bin 32648 -> 32688 bytes tests/test_investigation_dao | Bin 0 -> 42288 bytes tests/test_investigation_dao.c | 374 ++++++++++++++ tests/test_investigation_project | Bin 50984 -> 50984 bytes tests/test_investigation_record | Bin 0 -> 17392 bytes tests/test_investigation_record.c | 269 ++++++++++ tests/test_statement | Bin 37456 -> 37456 bytes tests/test_transaction | Bin 32720 -> 32760 bytes 21 files changed, 1767 insertions(+), 19 deletions(-) rename docs/tickets/{open => closed}/TICKET-026.md (100%) create mode 100644 docs/tickets/open/TICKET-027.md create mode 100644 include/dao/investigation_dao.h create mode 100644 include/models/investigation_record.h create mode 100644 src/dao/investigation_dao.c create mode 100644 src/models/investigation_record.c create mode 100755 tests/test_investigation_dao create mode 100644 tests/test_investigation_dao.c create mode 100755 tests/test_investigation_record create mode 100644 tests/test_investigation_record.c diff --git a/Makefile b/Makefile index ac4c089..32b3f2b 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,8 @@ TEST_DATABASE = tests/test_database TEST_STATEMENT = tests/test_statement TEST_TRANSACTION = tests/test_transaction TEST_ERROR = tests/test_error +TEST_INVESTIGATION_RECORD = tests/test_investigation_record +TEST_INVESTIGATION_DAO := tests/test_investigation_dao all: $(TARGET) @@ -105,6 +107,22 @@ $(TEST_ERROR): \ src/database/error.c $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3 +$(TEST_INVESTIGATION_RECORD): \ + tests/test_investigation_record.c \ + src/models/investigation_record.c + $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) + +$(TEST_INVESTIGATION_DAO): \ + tests/test_investigation_dao.c \ + src/dao/investigation_dao.c \ + src/models/investigation_record.c \ + src/database/database.c \ + src/database/schema.c \ + src/database/statement.c \ + src/database/transaction.c \ + src/database/error.c + $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3 + test: \ $(TEST_NODE) \ $(TEST_TREE_MODEL) \ @@ -113,7 +131,9 @@ test: \ $(TEST_DATABASE) \ $(TEST_STATEMENT) \ $(TEST_TRANSACTION) \ - $(TEST_ERROR) + $(TEST_ERROR) \ + $(TEST_INVESTIGATION_RECORD) \ + $(TEST_INVESTIGATION_DAO) @echo "Exécution des tests..." @./$(TEST_NODE) @./$(TEST_TREE_MODEL) @@ -123,6 +143,8 @@ test: \ @$(TEST_STATEMENT) @$(TEST_TRANSACTION) @$(TEST_ERROR) + @$(TEST_INVESTIGATION_RECORD) + @$(TEST_INVESTIGATION_DAO) @echo "Tous les tests sont valides." %.o: %.c @@ -140,6 +162,8 @@ clean: $(TEST_DATABASE) \ $(TEST_STATEMENT) \ $(TEST_TRANSACTION) \ - $(TEST_ERROR) + $(TEST_ERROR) \ + $(TEST_INVESTIGATION_RECORD) \ + $(TEST_INVESTIGATION_DAO) .PHONY: clean run test diff --git a/docs/tickets/open/TICKET-026.md b/docs/tickets/closed/TICKET-026.md similarity index 100% rename from docs/tickets/open/TICKET-026.md rename to docs/tickets/closed/TICKET-026.md diff --git a/docs/tickets/open/TICKET-027.md b/docs/tickets/open/TICKET-027.md new file mode 100644 index 0000000..9b22d28 --- /dev/null +++ b/docs/tickets/open/TICKET-027.md @@ -0,0 +1,535 @@ +# Ticket #027 — Ajouter le modèle de lecture et le DAO de l’enquête + +## Contexte + +Les tickets précédents ont permis de mettre en place : + +- le schéma SQLite V1 ; +- l’infrastructure `Database` ; +- les requêtes préparées `DatabaseStatement` ; +- les transactions ; +- la gestion centralisée des erreurs ; +- l’initialisation transactionnelle d’une nouvelle enquête. + +La table SQLite : + +```text +investigation +``` +contient les informations principales de l’enquête courante : + +```text +id +name +root_path +created_at +updated_at +``` + +Le projet possède déjà un type : + +```C +Investigation +``` + +dans le module `core`. + +Cet objet représente actuellement le contexte de travail sur le système de +fichiers : + +- chemin racine de l’enquête ; +- chemin du fichier SQLite. + +Il ne doit pas être transformé directement en représentation d’une ligne SQL. + +L’architecture du projet prévoit une séparation entre : + +```text +Core +Models +DAO +Database +SQLite +``` + +Le DAO doit être responsable des requêtes SQL métier. + +## Objectif + +Créer un modèle de données en lecture seule représentant la ligne de la table +investigation, ainsi qu’un DAO permettant de charger cette ligne depuis une +connexion Database. + +Le ticket doit permettre de lire les informations persistées d’une enquête +sans utiliser directement l’API SQLite hors de la couche Database. + +## Architecture attendue + +``` +InvestigationRecord + ↑ + │ construit par + │ +InvestigationDao + │ + ▼ +DatabaseStatement + │ + ▼ +Database + │ + ▼ +SQLite +``` + +## Travail à réaliser + +### Créer le modèle InvestigationRecord + +Créer les fichiers : +```text +include/models/investigation_record.h +src/models/investigation_record.c +``` + +Le modèle doit être opaque : + +```C +typedef struct InvestigationRecord InvestigationRecord; +``` + +Sa représentation privée doit contenir : + +```C +struct InvestigationRecord +{ + char *id; + char *name; + char *root_path; + char *created_at; + char *updated_at; +}; +``` + +Le modèle ne doit dépendre ni de SQLite, ni de GTK. + +### Ajouter le constructeur + +Créer une fonction : + +```C +InvestigationRecord *investigation_record_new( + const char *id, + const char *name, + const char *root_path, + const char *created_at, + const char *updated_at +); +``` + +Le constructeur doit : + +- refuser les pointeurs `NULL` ; +- refuser les chaînes vides ; +- allouer une copie de chaque chaîne ; +- nettoyer correctement toutes les allocations en cas d’échec ; +- retourner `NULL` si les données sont invalides. + +### Ajouter la fonction de libération + +Créer : + +```C +void investigation_record_free( + InvestigationRecord *record +); +``` + +Cette fonction doit : + +- accepter `NULL` ; +- libérer toutes les chaînes ; +- libérer la structure. + +### Ajouter les accesseurs + +Créer les accesseurs en lecture seule : + +```C +const char *investigation_record_get_id( + const InvestigationRecord *record +); + +const char *investigation_record_get_name( + const InvestigationRecord *record +); + +const char *investigation_record_get_root_path( + const InvestigationRecord *record +); + +const char *investigation_record_get_created_at( + const InvestigationRecord *record +); + +const char *investigation_record_get_updated_at( + const InvestigationRecord *record +); +``` + +Les pointeurs retournés appartiennent au modèle et ne doivent pas être libérés. + +Les accesseurs doivent retourner `NULL` si le modèle reçu est `NULL`. + +### Créer le DAO de l’enquête + +Créer les fichiers : + +```text +include/dao/investigation_dao.h +src/dao/investigation_dao.c +``` + +Déclarer la fonction : + +```C +InvestigationRecord *investigation_dao_load( + Database *database +); +``` + +Cette fonction doit charger l’unique ligne de la table : + +```text +investigation +``` + +La requête doit lire : + +```SQL +SELECT + id, + name, + root_path, + created_at, + updated_at +FROM investigation; +``` + +### Utiliser exclusivement DatabaseStatement + +Le DAO doit utiliser : + +```C +database_statement_prepare() +database_statement_step() +database_statement_column_text() +database_statement_finalize() +``` + +Le DAO ne doit pas utiliser directement : + +```C +sqlite3_prepare_v2() +sqlite3_step() +sqlite3_column_text() +sqlite3_finalize() +``` + +Aucun type SQLite ne doit apparaître dans l’interface publique du DAO. + +### Vérifier le nombre de lignes + +La base d’une enquête doit contenir exactement une ligne dans la table +`investigation`. + +Le DAO doit gérer les cas suivants : + +Une ligne +``` +ROW +DONE +``` +Résultat : + +``` +succès +``` + +Le DAO construit et retourne un `InvestigationRecord`. + +#### Aucune ligne + +`DONE` dès le premier step + +Résultat : + +``` +échec +``` + +Le DAO retourne `NULL` et enregistre : + +``` +DATABASE_ERROR_INVALID_STATE +``` + +#### Plusieurs lignes + +``` +ROW +ROW +``` + +Résultat : + +`échec` + +Le DAO libère le modèle temporaire, retourne `NULL` et enregistre : +```C +DATABASE_ERROR_INVALID_STATE +``` + +### Propager les erreurs + +Les erreurs de préparation ou d’exécution provenant de `DatabaseStatement` +doivent rester disponibles dans le contexte `Database`. + +Le DAO doit enregistrer une erreur explicite pour : + +- une table vide ; +- plusieurs lignes dans investigation ; +- une colonne obligatoire invalide ; +- une allocation impossible ; +- un état incohérent. + +Les codes prévus sont : + +```C +DATABASE_ERROR_INVALID_ARGUMENT +DATABASE_ERROR_INVALID_STATE +DATABASE_ERROR_MEMORY +DATABASE_ERROR_SQLITE +``` + +Une lecture réussie doit laisser : + +```C +DATABASE_ERROR_NONE +``` + +### Gérer proprement la mémoire + +Le DAO doit libérer toutes les chaînes temporaires retournées par : + +```C +database_statement_column_text() +``` + +après la construction du modèle. + +Toutes les sorties d’échec doivent : + +- finaliser la requête préparée ; +- libérer les chaînes déjà lues ; +- libérer le modèle partiellement construit ; +- ne provoquer aucune fuite mémoire. +- Tests à ajouter + +#### Test du modèle + +Créer : + +``` +tests/test_investigation_record.c +``` + +Vérifier : + +- la création avec des données valides ; +- la copie des chaînes ; +- les cinq accesseurs ; +- le refus des paramètres `NULL` ; +- le refus des chaînes vides ; +- `investigation_record_free(NULL)`. + +#### Test du DAO + +Créer : + +``` +tests/test_investigation_dao.c +Chargement valide +``` + +Créer une base temporaire avec : + +```C +database_initialize() +``` + +Puis : + +- ouvrir la base avec `database_open()` ; +- charger l’enquête avec `investigation_dao_load()` ; +- vérifier que le modèle n’est pas `NULL` ; +- vérifier l’UUID ; +- vérifier le nom ; +- vérifier le chemin racine ; +- vérifier `created_at` ; +- vérifier `updated_at` ; +- vérifier que l’erreur Database vaut `DATABASE_ERROR_NONE`. + +#### Paramètre invalide + +Vérifier : + +```C +investigation_dao_load(NULL) == NULL +``` + +#### Table vide + +Créer une base contenant la table investigation sans ligne. + +Vérifier : + +``` +résultat == NULL +DATABASE_ERROR_INVALID_STATE +message non vide +``` + +#### Plusieurs lignes + +Créer une base contenant deux lignes dans la table investigation. + +Vérifier : + +``` +résultat == NULL +DATABASE_ERROR_INVALID_STATE +message non vide +``` + +#### Données invalides + +Créer une situation dans laquelle une colonne obligatoire ne peut pas être +correctement lue. + +Vérifier : + +``` +résultat == NULL +erreur enregistrée +aucune fuite de mémoire +``` + +## Makefile + +Ajouter les exécutables : + +``` +tests/test_investigation_record +tests/test_investigation_dao +``` + +Ajouter leurs règles de compilation. + +Les tests du DAO devront notamment compiler avec : + +``` +src/models/investigation_record.c +src/dao/investigation_dao.c +src/database/database.c +src/database/schema.c +src/database/statement.c +src/database/transaction.c +src/database/error.c +``` + +Ajouter les nouveaux tests aux cibles : + +``` +test +clean +``` +### Critères d’acceptation + +- [ ] Le type `InvestigationRecord` est opaque. +- [ ] Le modèle ne dépend ni de SQLite ni de GTK. +- [ ] Le constructeur valide tous les champs obligatoires. +- [ ] Toutes les chaînes sont copiées. +- [ ] Une fonction de libération complète est disponible. +- [ ] Les cinq accesseurs en lecture seule sont disponibles. +- [ ] `investigation_dao_load()` charge l’enquête courante. +- [ ] Le DAO utilise uniquement l’API `DatabaseStatement`. +- [ ] Aucun appel direct à SQLite n’apparaît dans le DAO. +- [ ] Une base contenant exactement une enquête est acceptée. +- [ ] Une table vide est refusée. +- [ ] Plusieurs lignes sont refusées. +- [ ] Les erreurs sont enregistrées dans `Database`. +- [ ] Toutes les ressources temporaires sont libérées. +- [ ] Les tests du modèle sont valides. +- [ ] Les tests du DAO sont valides. +- [ ] Tous les anciens tests restent valides. +- [ ] `make` réussit sans erreur. +- [ ] `make test` réussit. +- [ ] `git diff --check` ne retourne aucune erreur. + +### Hors périmètre + +Ce ticket ne doit pas ajouter : + +- la création d’une enquête par le DAO ; +- la modification du nom de l’enquête ; +- la modification du chemin racine ; +- la suppression d’une enquête ; +- un CRUD complet ; +- l’ouverture automatique dans l’interface GTK ; +- l’affichage des informations dans la fenêtre ; +- les DAO des preuves, sources ou entités ; +- les migrations de schéma ; +- une nouvelle version du schéma SQLite. + +### Fichiers principalement concernés + +``` +include/models/investigation_record.h +include/dao/investigation_dao.h + +src/models/investigation_record.c +src/dao/investigation_dao.c + +tests/test_investigation_record.c +tests/test_investigation_dao.c + +Makefile +``` +## Résultat attendu + +À la fin du ticket, le projet doit être capable de charger les informations +persistées de l’enquête courante dans un modèle C indépendant de SQLite. + +Le DAO devient le premier point d’accès métier structuré à la base de données. + +### Commit attendu + +Une fois tous les critères validés : + +`feat(dao): add investigation record loader` + +Avant le commit : + +```bash +make clean +make +make test +git diff --check +git status --short +git add . +git diff --cached --stat +git diff --cached +git commit -m "feat(dao): add investigation record loader" +git push +``` diff --git a/include/dao/investigation_dao.h b/include/dao/investigation_dao.h new file mode 100644 index 0000000..486838f --- /dev/null +++ b/include/dao/investigation_dao.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * @file investigation_dao.h + * @brief Accès aux informations persistées de l'enquête. + ******************************************************************************/ + +#ifndef LABFY_INVESTIGATION_INVESTIGATION_DAO_H +#define LABFY_INVESTIGATION_INVESTIGATION_DAO_H + +#include "database/database.h" +#include "models/investigation_record.h" + +/** + * @brief Charge l'unique enquête enregistrée dans la base. + * + * La fonction attend exactement une ligne dans la table investigation. + * + * @param database Connexion ouverte. + * + * @return Un nouveau modèle InvestigationRecord, ou NULL en cas d'échec. + */ +InvestigationRecord *investigation_dao_load( + Database *database +); + +#endif diff --git a/include/database/error.h b/include/database/error.h index 3eff2ff..6fa9860 100644 --- a/include/database/error.h +++ b/include/database/error.h @@ -44,6 +44,21 @@ const char *database_error_get_message( const Database *database ); +/** + * @brief Enregistre une erreur dans une connexion Database. + * + * Le message est copié par la connexion. + * + * @param database Connexion Database. + * @param error_code Code de l'erreur. + * @param error_message Message descriptif. + */ +void database_error_set( + Database *database, + DatabaseErrorCode error_code, + const char *error_message +); + /** * @brief Efface la dernière erreur enregistrée. * diff --git a/include/models/investigation_record.h b/include/models/investigation_record.h new file mode 100644 index 0000000..33dea10 --- /dev/null +++ b/include/models/investigation_record.h @@ -0,0 +1,92 @@ +/****************************************************************************** + * @file investigation_record.h + * @brief Modèle représentant les informations persistées d'une enquête. + ******************************************************************************/ + +#ifndef LABFY_INVESTIGATION_INVESTIGATION_RECORD_H +#define LABFY_INVESTIGATION_INVESTIGATION_RECORD_H + +/** + * @brief Modèle opaque représentant une ligne de la table investigation. + */ +typedef struct InvestigationRecord InvestigationRecord; + +/** + * @brief Crée un modèle représentant une enquête persistée. + * + * Toutes les chaînes sont copiées par le modèle. + * + * @param id Identifiant UUID de l'enquête. + * @param name Nom de l'enquête. + * @param root_path Chemin racine de l'enquête. + * @param created_at Date de création. + * @param updated_at Date de dernière modification. + * + * @return Un nouveau modèle, ou NULL si les données sont invalides + * ou si une allocation échoue. + */ +InvestigationRecord *investigation_record_new( + const char *id, + const char *name, + const char *root_path, + const char *created_at, + const char *updated_at +); + +/** + * @brief Libère un modèle InvestigationRecord. + * + * Cette fonction accepte NULL. + * + * @param record Modèle à libérer. + */ +void investigation_record_free( + InvestigationRecord *record +); + +/** + * @brief Retourne l'identifiant de l'enquête. + * + * @return Identifiant appartenant au modèle, ou NULL. + */ +const char *investigation_record_get_id( + const InvestigationRecord *record +); + +/** + * @brief Retourne le nom de l'enquête. + * + * @return Nom appartenant au modèle, ou NULL. + */ +const char *investigation_record_get_name( + const InvestigationRecord *record +); + +/** + * @brief Retourne le chemin racine de l'enquête. + * + * @return Chemin appartenant au modèle, ou NULL. + */ +const char *investigation_record_get_root_path( + const InvestigationRecord *record +); + +/** + * @brief Retourne la date de création. + * + * @return Date appartenant au modèle, ou NULL. + */ +const char *investigation_record_get_created_at( + const InvestigationRecord *record +); + +/** + * @brief Retourne la date de dernière modification. + * + * @return Date appartenant au modèle, ou NULL. + */ +const char *investigation_record_get_updated_at( + const InvestigationRecord *record +); + +#endif diff --git a/labfy-investigation b/labfy-investigation index 21dd01225dabba0b9dd581fa20e4e8ea95b5cab8..c28c4e8179c80245578372690058119e5f987ab8 100755 GIT binary patch delta 37157 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 delta 30027 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 NEL=EDp46Vl|38e;ZleGI diff --git a/src/dao/investigation_dao.c b/src/dao/investigation_dao.c new file mode 100644 index 0000000..177700b --- /dev/null +++ b/src/dao/investigation_dao.c @@ -0,0 +1,197 @@ +/****************************************************************************** + * @file investigation_dao.c + * @brief Chargement de l'enquête persistée. + ******************************************************************************/ + +#include "dao/investigation_dao.h" + +#include "database/error.h" +#include "database/statement.h" + +#include + +/** + * @brief Requête de chargement de l'enquête. + */ +static const char *const investigation_dao_load_sql = + "SELECT " + " id, " + " name, " + " root_path, " + " created_at, " + " updated_at " + "FROM investigation;"; + +/** + * @brief Vérifie qu'une valeur obligatoire est présente. + */ +static bool investigation_dao_text_is_valid( + const char *text +) +{ + return text != NULL && text[0] != '\0'; +} + +InvestigationRecord *investigation_dao_load( + Database *database +) +{ + DatabaseStatement *statement = NULL; + InvestigationRecord *record = NULL; + + char *id = NULL; + char *name = NULL; + char *root_path = NULL; + char *created_at = NULL; + char *updated_at = NULL; + + DatabaseStatementStepResult step_result = + DATABASE_STATEMENT_STEP_ERROR; + + if (database == NULL) + { + return NULL; + } + + statement = database_statement_prepare( + database, + investigation_dao_load_sql + ); + + if (statement == NULL) + { + goto cleanup; + } + + step_result = database_statement_step( + statement + ); + + if (step_result == DATABASE_STATEMENT_STEP_ERROR) + { + goto cleanup; + } + + if (step_result == DATABASE_STATEMENT_STEP_DONE) + { + database_error_set( + database, + DATABASE_ERROR_INVALID_STATE, + "La table investigation ne contient aucune ligne." + ); + + goto cleanup; + } + + if (!database_statement_column_text( + statement, + 0, + &id + ) || + !database_statement_column_text( + statement, + 1, + &name + ) || + !database_statement_column_text( + statement, + 2, + &root_path + ) || + !database_statement_column_text( + statement, + 3, + &created_at + ) || + !database_statement_column_text( + statement, + 4, + &updated_at + )) + { + database_error_set( + database, + DATABASE_ERROR_INVALID_STATE, + "Impossible de lire les colonnes de l'enquête." + ); + + goto cleanup; + } + + if (!investigation_dao_text_is_valid(id) || + !investigation_dao_text_is_valid(name) || + !investigation_dao_text_is_valid(root_path) || + !investigation_dao_text_is_valid(created_at) || + !investigation_dao_text_is_valid(updated_at)) + { + database_error_set( + database, + DATABASE_ERROR_INVALID_STATE, + "L'enquête contient une colonne obligatoire invalide." + ); + + goto cleanup; + } + + record = investigation_record_new( + id, + name, + root_path, + created_at, + updated_at + ); + + if (record == NULL) + { + database_error_set( + database, + DATABASE_ERROR_MEMORY, + "Impossible d'allouer le modèle de l'enquête." + ); + + goto cleanup; + } + + /* + * Une seconde ligne indiquerait une base incohérente. + */ + step_result = database_statement_step( + statement + ); + + if (step_result == DATABASE_STATEMENT_STEP_ERROR) + { + investigation_record_free(record); + record = NULL; + + goto cleanup; + } + + if (step_result == DATABASE_STATEMENT_STEP_ROW) + { + database_error_set( + database, + DATABASE_ERROR_INVALID_STATE, + "La table investigation contient plusieurs lignes." + ); + + investigation_record_free(record); + record = NULL; + + goto cleanup; + } + + database_error_clear(database); + +cleanup: + + g_free(updated_at); + g_free(created_at); + g_free(root_path); + g_free(name); + g_free(id); + + database_statement_finalize(statement); + + return record; +} diff --git a/src/database/error.c b/src/database/error.c index 1a77f9a..7df89ff 100644 --- a/src/database/error.c +++ b/src/database/error.c @@ -22,6 +22,19 @@ const char *database_error_get_message( return database_get_error_message_internal(database); } +void database_error_set( + Database *database, + DatabaseErrorCode error_code, + const char *error_message +) +{ + database_set_error( + database, + error_code, + error_message + ); +} + void database_error_clear( Database *database ) diff --git a/src/database/statement.c b/src/database/statement.c index 67b4c03..5825b97 100644 --- a/src/database/statement.c +++ b/src/database/statement.c @@ -590,33 +590,24 @@ void database_statement_finalize( DatabaseStatement *statement ) { - sqlite3 *database_handle = NULL; - int result = SQLITE_OK; - if (statement == NULL) { return; } - database_handle = database_get_handle( - statement->database - ); - if (statement->handle != NULL) { - result = sqlite3_finalize( + /* + * sqlite3_finalize() peut retourner l'erreur produite par le dernier + * sqlite3_step(), même si la requête est correctement détruite. + * + * Cette erreur est déjà enregistrée par database_statement_step(). + */ + sqlite3_finalize( statement->handle ); - if (result != SQLITE_OK) - { - g_warning( - "Impossible de finaliser la requête SQL : %s", - database_handle != NULL - ? sqlite3_errmsg(database_handle) - : sqlite3_errstr(result) - ); - } + statement->handle = NULL; } g_free(statement); diff --git a/src/models/investigation_record.c b/src/models/investigation_record.c new file mode 100644 index 0000000..20f5361 --- /dev/null +++ b/src/models/investigation_record.c @@ -0,0 +1,199 @@ +/****************************************************************************** + * @file investigation_record.c + * @brief Implémentation du modèle InvestigationRecord. + ******************************************************************************/ + +#include "models/investigation_record.h" + +#include +#include + +/** + * @brief Représentation privée d'une enquête persistée. + */ +struct InvestigationRecord +{ + char *id; + char *name; + char *root_path; + char *created_at; + char *updated_at; +}; + +/** + * @brief Vérifie qu'une chaîne obligatoire est présente et non vide. + */ +static gboolean investigation_record_text_is_valid( + const char *text +) +{ + return text != NULL && text[0] != '\0'; +} + +/** + * @brief Copie une chaîne en utilisant une allocation non fatale. + * + * Contrairement à g_strdup(), cette fonction retourne NULL si + * l'allocation échoue au lieu d'interrompre le programme. + */ +static char *investigation_record_copy_text( + const char *text +) +{ + char *text_copy = NULL; + gsize text_length = 0; + + if (text == NULL) + { + return NULL; + } + + text_length = strlen(text); + + if (text_length == G_MAXSIZE) + { + return NULL; + } + + text_copy = g_try_malloc( + text_length + 1 + ); + + if (text_copy == NULL) + { + return NULL; + } + + memcpy( + text_copy, + text, + text_length + 1 + ); + + return text_copy; +} + +InvestigationRecord *investigation_record_new( + const char *id, + const char *name, + const char *root_path, + const char *created_at, + const char *updated_at +) +{ + InvestigationRecord *record = NULL; + + if (!investigation_record_text_is_valid(id) || + !investigation_record_text_is_valid(name) || + !investigation_record_text_is_valid(root_path) || + !investigation_record_text_is_valid(created_at) || + !investigation_record_text_is_valid(updated_at)) + { + return NULL; + } + + record = g_try_new0( + InvestigationRecord, + 1 + ); + + if (record == NULL) + { + return NULL; + } + + record->id = investigation_record_copy_text(id); + record->name = investigation_record_copy_text(name); + record->root_path = investigation_record_copy_text(root_path); + record->created_at = investigation_record_copy_text(created_at); + record->updated_at = investigation_record_copy_text(updated_at); + + if (record->id == NULL || + record->name == NULL || + record->root_path == NULL || + record->created_at == NULL || + record->updated_at == NULL) + { + investigation_record_free(record); + return NULL; + } + + return record; +} + +void investigation_record_free( + InvestigationRecord *record +) +{ + if (record == NULL) + { + return; + } + + g_free(record->updated_at); + g_free(record->created_at); + g_free(record->root_path); + g_free(record->name); + g_free(record->id); + g_free(record); +} + +const char *investigation_record_get_id( + const InvestigationRecord *record +) +{ + if (record == NULL) + { + return NULL; + } + + return record->id; +} + +const char *investigation_record_get_name( + const InvestigationRecord *record +) +{ + if (record == NULL) + { + return NULL; + } + + return record->name; +} + +const char *investigation_record_get_root_path( + const InvestigationRecord *record +) +{ + if (record == NULL) + { + return NULL; + } + + return record->root_path; +} + +const char *investigation_record_get_created_at( + const InvestigationRecord *record +) +{ + if (record == NULL) + { + return NULL; + } + + return record->created_at; +} + +const char *investigation_record_get_updated_at( + const InvestigationRecord *record +) +{ + if (record == NULL) + { + return NULL; + } + + return record->updated_at; +} diff --git a/tests/test_database b/tests/test_database index c402d3f3d89dde2c5a0895c071c7b44eaa2e94f2..2a1ae2522c4f231ed1559cc6b60d12d90f28ed9f 100755 GIT binary patch delta 7418 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 Q4)J#X2c#osXGE+20|cF%r~m)} diff --git a/tests/test_database.c b/tests/test_database.c index 9404a18..1741900 100644 --- a/tests/test_database.c +++ b/tests/test_database.c @@ -133,6 +133,7 @@ static void test_database_initialize_valid_database(void) char *investigation_created_at = NULL; char *investigation_id = NULL; char *investigation_count = NULL; + char *investigation_updated_at = NULL; sqlite3 *database = NULL; GError *error = NULL; @@ -231,6 +232,12 @@ static void test_database_initialize_valid_database(void) "LIMIT 1;" ); + investigation_updated_at = test_database_read_single_text( + database, + "SELECT updated_at FROM investigation " + "LIMIT 1;" + ); + investigation_id = test_database_read_single_text( database, "SELECT id FROM investigation " @@ -253,6 +260,13 @@ static void test_database_initialize_valid_database(void) assert(strcmp(investigation_root_path, temporary_directory) == 0); assert(strcmp(investigation_created_at, created_at) == 0); + assert(investigation_updated_at[0] != '\0'); + + assert(strcmp(investigation_updated_at,created_at) == 0); + + assert(strcmp(investigation_id,investigation_uuid) == 0 + ); + result = sqlite3_close(database); assert(result == SQLITE_OK); diff --git a/tests/test_error b/tests/test_error index 60f0127834c6f2e4a5309baccfb766a0e2cc0847..1185635c8f2fac9033677f79bb831ba8d661b8b9 100755 GIT binary patch delta 2308 zcmZXW2}~4c6vyZLkg}^Ftbof3vb$b@$fYPCpqP5BX;n~21+BUY5)?!Zt%!=P=Ssc$ z(iCj!QLSkWZP3vctAMK(??ybAcwxl~YSp&ZLYrp#pV?(KI?24>e((6toA21ndWls_ zY)de!-Wqa89TqMqR~TtngnFm1eYBItvwBTZntkLX>3EQ4rNiqarNHaV>8v3O4>BfC zuq(XIWz6nhMvYvnsZONi}j=n;I@8+hlLmBWPO2JTt9Y{!CcZ$F$LRerI79 zM`0<=i66-7X;b`QRz=o$?=EUc(f0U0?-gD{^+w-&Uh*TXb%0PWKSFs<>uNdbnCsZ9 zj;BA;bh`S>E~UHjwDwF>s84^ajZo~``j}D@f)OEHr{z<3W zScE^N-3ehH8OWED7$&VxVtR%&PC(ldL zRB-4;Nm>FXUqKzX9;^W$f;P~G^=b$AU|IcK1YtjRT`bsQL%oYoCX8%?030*G3UCEj z1MUKCVDt?LfKj&~fTe5zW5N62RM7P{>cKp)0^EHE_2Bkq)Pr+>K|QE%LA|RW%)F0! zaMQ1-cP$fyLk}PTM@uUNz^z!a8gK+Qf(<+iwu29_>HXdkgl(ANSkN8wI~ClEZM6i9 z!TzoQ&&fvyobn9yU>?{Gt^@tt1mOS}3(A)dbxhRvnsq{w2FSzFZt`Ie`r(eo?PsOI zlLm0zNy?ZM$c|IVBt6TgO4uiK7&eodU=L9TY&GeU_3SjoCF^VT~yU5aeuV6Bu68%-rCdah}rZ7G3lEggny4K*pY1NH*xChOTfii54A zblAC60xMDFWIfk6(~pw_SvWPpHIzDFN0V-fo;6Y&>Js@-k5>;FO=VQ zRClkn{B4i7c%Hi({-FZkx{39wq9)`Hpbps2NSCT--6#&Wh|*#6s3cWy%*IlMzN#@- zkx(>_V#x}rGBw_qdPGt#d2YMn?i?GiEwvbDihre+t0>Y%MJwN5&kUk^M!pMMDVCVW zxv=SE%^b(RCUsT>OBUm@f;cmfIa|wX6KQGoSjNekt!4gVQ+5pFFXE|3&SJhhkv8P4 zV1=Zf_XAr;)_K0(?!xs%xlut-`UU?W%{;A6!bb2C`~^dS5_Fnw6g6L~8-V%fA-D_Q zDZ4`u4379%B|6O!&5yFXlWYuU$g4*Oim+~>&r-JwH{%o-(n9#Of6;MaO+ zRH7|PwAULQK9ZxZDm6MJqldc7At|aaoMo`As64m#yQh XhT2xdsM~YxG8-wREC%PqTo&{{k+U6- delta 2283 zcmZXW3rrMO6o%){@^B50U0y2+T^6Yb%1bF!d4xuhB~oN{wYEXbHXs2hLRg=mV9*AN zrr>F{HEBc}T1>Tpj*V6+N_~>5Wl0rGZ56e35m7e5N=xkYpIKN%Cz=1dbM8I=J#+8O z?zD)kMr8FfSxrOOEu|@vlMfTAS(LKb*M8MZ>Fl&xzubQ1Ao=s5>YA~rgJeL|F=jl6 zYWYxI)!TN5muY79xS1@xMt#C)zjBcN;Sc!qZrha|(Hi%paO!2t(@UE!HQ(EKQ8=O) z_T*QG(;@G<{K9bR@wW0QVbtLh%ZG%~Q=dZj?wK5?uS>P+YcE>&P_b_;+elyf_OWu> z=~vD~VwzZXPl(gZW2~M^ro6$-^wpFj>?kes-^9m;2v__g8SgZmo=q)dA5-bH4g8c~ z>YrB3nrUS~DIce!p94zSSs^K~jptonqfu=vdqB}aKk{CI^f;)1eJ#AJ>viRI-ZVF? zgirCNrm$HETEc=$prCRGasoau>qNuM;9kGW}Ch2#cF|Tl3 zV1TMe6l+t5tG5nw<|KHGaAptmk4VYK&^Iu-E_E2~&(PNI_U%x^zP6)Xg=tPp)1JWT z#92@F(5cxv&rm7DQ}#zrEg9$ME?y|5)TQ>eOZy|)8Xt?Il*aWd5t6#3XvTnC=@4od z;?@Uau7vSKr?JFoq(}&7t0^*J4$B}@g16J*N1{l{3Bm7pU6;!fP}eKvan=y2cWeWD ze;u2BKs_EhFNNgl>Xwh>>Sy`9jt%F`&emOIO9-7c=Yc5hIAhkLB$MQi)oC9usef?X zZ4tWjXhC9x`@jA2Z1NJWCw0aOJzihO8f{`2bBu03wdBLb}khUcT z#}`SnOUvax!S0~vOLcW%-{mzPn2)-eX>C#j-))73AGIX8I*p>64kr0IahO(WxVoTG z-+5MeE%{saT}rf@I{kx^b!BA*?-%6-<>m#gDJZ6^i?u>@MhL5v-n0m;P~%Uc*o=19 zIpl(!=S8s#+;c$`hrrdBL{Y_a+<%>-7!7`MSroHC=Ppq!0fVsa_25dd1w09Mfz8*D z51O%UDh0=F$5Ryz9_vNEf~({_Z@>V7NU$Ev1Y5vTunTnh4F(|l9R}E{yJ(vYv z153c9KadZW-9kQCd>i>-+#TeDkMAPind74VL_V1F7xJC)y6wCV0|bs)VE~%28!ey* z9+fU|KR5&)$3v}h;kZJqcQlxYRn7vhp! zWmg=P-NX}ck6JM{hb+)v$Oc_NYNM8YLov`MGC&VdgE2vuaY~dA>6dXb&hhIY+@A*5 z*}|Z)ZW2|ks$oV!nX^v8YJ|B@ULLC?E!GQ1w$NAfx2y2L?4l3` zH{RuWel~S)iBuYs?SqV@EKg%qWGIhPzw-A_4Tz3p+DDGFjV$F+%I6;e%8GcU~>qoP#MqOquR>Q{{bVkDjxs< diff --git a/tests/test_investigation_dao b/tests/test_investigation_dao new file mode 100755 index 0000000000000000000000000000000000000000..0234ddd470732e3c1dd7ba53ba45d9ccfe826f54 GIT binary patch literal 42288 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 literal 0 HcmV?d00001 diff --git a/tests/test_investigation_dao.c b/tests/test_investigation_dao.c new file mode 100644 index 0000000..5b9cc18 --- /dev/null +++ b/tests/test_investigation_dao.c @@ -0,0 +1,374 @@ +/****************************************************************************** + * @file test_investigation_dao.c + * @brief Tests du DAO de l'enquête. + ******************************************************************************/ + +#include "dao/investigation_dao.h" + +#include "database/database.h" +#include "database/error.h" +#include "database/statement.h" +#include "models/investigation_record.h" + +#include +#include +#include + +#include +#include + +/** + * @brief Crée une table investigation minimale pour les tests du DAO. + */ +static void test_investigation_dao_create_table( + Database *database +) +{ + DatabaseStatement *statement = NULL; + + assert(database != NULL); + + statement = database_statement_prepare( + database, + "CREATE TABLE investigation" + "(" + " id TEXT NOT NULL," + " name TEXT NOT NULL," + " root_path TEXT NOT NULL," + " created_at TEXT NOT NULL," + " updated_at TEXT NOT NULL" + ");" + ); + + assert(statement != NULL); + + assert( + database_statement_step(statement) == + DATABASE_STATEMENT_STEP_DONE + ); + + database_statement_finalize(statement); +} + +/** + * @brief Insère une ligne dans la table investigation de test. + */ +static void test_investigation_dao_insert_row( + Database *database, + const char *id, + const char *name +) +{ + DatabaseStatement *statement = NULL; + + assert(database != NULL); + assert(id != NULL); + assert(name != NULL); + + statement = database_statement_prepare( + database, + "INSERT INTO investigation" + "(" + " id," + " name," + " root_path," + " created_at," + " updated_at" + ")" + "VALUES (?, ?, ?, ?, ?);" + ); + + assert(statement != NULL); + + assert( + database_statement_bind_text( + statement, + 1, + id + ) + ); + + assert( + database_statement_bind_text( + statement, + 2, + name + ) + ); + + assert( + database_statement_bind_text( + statement, + 3, + "/tmp/enquete-dao" + ) + ); + + assert( + database_statement_bind_text( + statement, + 4, + "2026-07-16T12:00:00Z" + ) + ); + + assert( + database_statement_bind_text( + statement, + 5, + "2026-07-16T12:00:00Z" + ) + ); + + assert( + database_statement_step(statement) == + DATABASE_STATEMENT_STEP_DONE + ); + + database_statement_finalize(statement); +} + +/** + * @brief Vérifie le refus d'une table investigation vide. + */ +static void test_investigation_dao_load_empty_table(void) +{ + Database *database = NULL; + InvestigationRecord *record = NULL; + const char *error_message = NULL; + + database = database_open(":memory:"); + + assert(database != NULL); + + test_investigation_dao_create_table(database); + + record = investigation_dao_load(database); + + assert(record == NULL); + + assert( + database_error_get_code(database) == + DATABASE_ERROR_INVALID_STATE + ); + + error_message = database_error_get_message(database); + + assert(error_message != NULL); + assert(error_message[0] != '\0'); + + database_close(database); +} + +/** + * @brief Vérifie le refus de plusieurs enquêtes dans la même base. + */ +static void test_investigation_dao_load_multiple_rows(void) +{ + Database *database = NULL; + InvestigationRecord *record = NULL; + const char *error_message = NULL; + + database = database_open(":memory:"); + + assert(database != NULL); + + test_investigation_dao_create_table(database); + + test_investigation_dao_insert_row( + database, + "11111111-1111-4111-8111-111111111111", + "Premiere_Enquete" + ); + + test_investigation_dao_insert_row( + database, + "22222222-2222-4222-8222-222222222222", + "Seconde_Enquete" + ); + + record = investigation_dao_load(database); + + assert(record == NULL); + + assert( + database_error_get_code(database) == + DATABASE_ERROR_INVALID_STATE + ); + + error_message = database_error_get_message(database); + + assert(error_message != NULL); + assert(error_message[0] != '\0'); + + database_close(database); +} + +/** + * @brief Vérifie le chargement d'une enquête valide. + */ +static void test_investigation_dao_load_valid(void) +{ + char *temporary_directory = NULL; + char *database_path = NULL; + + Database *database = NULL; + InvestigationRecord *record = NULL; + + const char *id = NULL; + const char *created_at = NULL; + const char *updated_at = NULL; + + GError *error = NULL; + + temporary_directory = g_dir_make_tmp( + "labfy-investigation-dao-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_DAO", + temporary_directory + ) + ); + + database = database_open( + database_path + ); + + assert(database != NULL); + + record = investigation_dao_load( + database + ); + + assert(record != NULL); + + id = investigation_record_get_id(record); + created_at = investigation_record_get_created_at(record); + updated_at = investigation_record_get_updated_at(record); + + assert(id != NULL); + assert(g_uuid_string_is_valid(id)); + + assert( + strcmp( + investigation_record_get_name(record), + "Enquete_DAO" + ) == 0 + ); + + assert( + strcmp( + investigation_record_get_root_path(record), + temporary_directory + ) == 0 + ); + + assert(created_at != NULL); + assert(created_at[0] != '\0'); + + assert(updated_at != NULL); + assert(updated_at[0] != '\0'); + + assert( + strcmp( + created_at, + updated_at + ) == 0 + ); + + assert( + database_error_get_code(database) == + DATABASE_ERROR_NONE + ); + + investigation_record_free(record); + database_close(database); + + assert(g_remove(database_path) == 0); + assert(g_rmdir(temporary_directory) == 0); + + g_free(database_path); + g_free(temporary_directory); +} + +/** + * @brief Vérifie le refus d'une connexion absente. + */ +static void test_investigation_dao_load_null_database(void) +{ + assert( + investigation_dao_load(NULL) == NULL + ); +} + +/** + * @brief Vérifie le refus d'une enquête contenant une valeur obligatoire vide. + */ +static void test_investigation_dao_load_invalid_row(void) +{ + Database *database = NULL; + InvestigationRecord *record = NULL; + const char *error_message = NULL; + + database = database_open(":memory:"); + + assert(database != NULL); + + test_investigation_dao_create_table(database); + + /* + * La colonne name est NOT NULL, mais une chaîne vide reste autorisée + * par SQLite. Le DAO doit la considérer comme invalide. + */ + test_investigation_dao_insert_row( + database, + "33333333-3333-4333-8333-333333333333", + "" + ); + + record = investigation_dao_load(database); + + assert(record == NULL); + + assert( + database_error_get_code(database) == + DATABASE_ERROR_INVALID_STATE + ); + + error_message = database_error_get_message(database); + + assert(error_message != NULL); + assert(error_message[0] != '\0'); + + database_close(database); +} + +int main(void) +{ + test_investigation_dao_load_valid(); + test_investigation_dao_load_null_database(); + test_investigation_dao_load_empty_table(); + test_investigation_dao_load_multiple_rows(); + test_investigation_dao_load_invalid_row(); + + printf( + "InvestigationDao : tous les tests sont valides.\n" + ); + + return 0; +} diff --git a/tests/test_investigation_project b/tests/test_investigation_project index ebee53a658b444bd200dad801ff183a8392366a5..ab7a276f93faa9dc940ddf1e3075d95329b6a722 100755 GIT binary patch delta 1738 zcmZ9NeN03p9it^fK4d}}wrpcUWDK1%P?s6dZ4?>V7UQzwVh~E$A2Ja!wsVF! z`~XYK#~U4x;fNoh8#t$TQ;G&?hkQ8(h}&9X;)pm4=ms%M=wfnx?k%NeH|g`c?|aVk zoWnh|*KSkTZHfy@7PhpTldq-S6wS7&YBu}J8p>sTQ7PoFf>iU#QAbz0K^{amI49t# zMb}>IuU1J;DWS>VT_ySWGckvRiq{A7N-WFC`>)LJdg#RO|Jrs4U5dedyfd0A50vva zqv_UxRF)$|A54s-bYoab zTj(xC$bHOl)7_Vid6q{w4jp$d!>;sbU%VrV-O>){RA@; zyePigU#Z11c4-+rr{2Imb8iPi_hPtW8HR#s-lY4sv~e}ChEynQOrbr^|UA+27PU!XaZ+G6UAom?ibiD znBO6aZtxVi3@)A(#W)<$51sIXj`Q$?&wUL)cte06{PkP-6S{bTTh%5nHX{RI4> z9QWoV{NN`)!w-&4!4Dq)1%5F38vHwOBjCmjPFzO-SbYNlN<3mP6Rh_j08E@l0Ci3h8Gd z`+>;!`Deq|@@4Bn@jMfxuR9Lz5~-WCES+@Fb}~R?$lSE^@zYjOzNDX(Fnj`a7XDjf zQD15Bk0Xjpl4|;+=|xs3WK()5zHE;P<*gsr8wY=3o7bLh*>i@H71U%?iw{vSqz2_uxRTbGKme84jE?N zS$Y0P+!#@aDO{u`y;Chl%R>m)#urv@)=W9QSdgL_nzo$g^xa(+(&^6d{qFyN&i9=& z=U(=nuGXEd)_oAdLbD8l@5(aJEVfc|EcTZUGO_NcZ1PvroGUJivQ2iUnT783oPb9` z*RA-gDN?VNQ19u;rn7~kkAeQv72 zgmpcfD~5^60~12&T4Ut_uTUC}aj#}x+{^iBsMRom`(WG~tLC_uB3|irMq-(6ZQ~A2 zhArCYU=O7yN7Je`ArXg>IvU|y2i`I|mS=uqt7f$t+)f6E7XR#Tocrt^2`z0%>t>Al zc07)~j0^h~Va=xl#u<~w`IMf&f;~lh@>j=x(v9iXCt>Y#>U`r@hwZ;|P8Io!at>;P zRdX&S{_m!5_^dc8ta|g)$-Rt51$N!9wS0TEZts#!Qz~PbGHn?goingc&sZRIBZl|Wp@QVFzdhQ1{pY)noCOcAY(b}ToK1!-xIbje zJt2!J)|?!^<3Ay{$EdqtQEE{jH0+)@$CCT@)QcCwcj%*!Vo%-FVNMo@hShJ(1EJ?i zj>N{8c9-ngyVsImR2XkAjNf4?&MUI)EqqDcbHuXt^% z*PuRmg7a+9)x>!jc(#>u8#wR|76li!aqb3pf?lx4&Uq}J+R-!cgUb8xgY(Y954NfB zgB>5iuM;W+FK*muG<=Kz@PY#YIPT6)1b`>cBLI9AkEje>k4J0+TR{g{hxgJA=Hh+z zg1h?Q4-$l?3-E(pFdK~NhaXG@ZD0=Q2&xc-u>l02F*1k%u;L;DW@2G*4d?{3!KE$) zfG=D^0Qmgp2ms~F@PqCv@Po0};13prP1oTEi-+I`n{L1lZvFy(@K4b32yQCBL;%?Q z6#~F#ZXzH=5E9)80K30N0GN3T0pO2RXiZ{0RA-H0MzUL%u@btD`#kbkW%f8pM`dOr zJv53;&>*rv*OCI=Np@&6xu6;3f$B-Bl363^p}}OTlEpjcXjfGnizNlE2C_q2$ptMZ z4-^Yj%i`TmT2URxTF8Xrhx4=x#aU9SWtK~J=w@>H?mbW&Ni{M%PI{l3pp9gKhLKVu zizB_%UK7W*lMAhFqx1UMfV`J*T#w7i*%_r4tw@sB%Gu= znJpwew4F@QRI)%@Nr5(#y-rSe_$uc^_|oD zPEx(hu8|&^PNw?D9xAHhzDxUo48>odPQ%+8gZf0Re;iT560N2W>$kF1Y80get0j$P zIx)Ra9chZ2!ECDZc0>^Shvv4Wuv#)f-D-K;DG@VhUi&uIrIxqvWbC+l`%DC5L-c#c zFXD2udh5O7Xx2}rzLe1PLacxbhS&QxGB>&UQl#n654(^mHCz2tnrS_%>5tGsRMVfa OUTrM!sZ&}1GTpyJ((eQS diff --git a/tests/test_investigation_record b/tests/test_investigation_record new file mode 100755 index 0000000000000000000000000000000000000000..ba2880ba891764294db9386029afaf85e4bc251a GIT binary patch literal 17392 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 +#include +#include + +/** + * @brief Vérifie la création et les accesseurs du modèle. + */ +static void test_investigation_record_valid(void) +{ + InvestigationRecord *record = NULL; + + char id[] = "6e62b9af-2046-4efd-b3b9-29869f816951"; + char name[] = "Enquete_Test"; + char root_path[] = "/tmp/enquete-test"; + char created_at[] = "2026-07-16T10:00:00Z"; + char updated_at[] = "2026-07-16T10:00:00Z"; + + record = investigation_record_new( + id, + name, + root_path, + created_at, + updated_at + ); + + assert(record != NULL); + + assert( + strcmp( + investigation_record_get_id(record), + id + ) == 0 + ); + + assert( + strcmp( + investigation_record_get_name(record), + name + ) == 0 + ); + + assert( + strcmp( + investigation_record_get_root_path(record), + root_path + ) == 0 + ); + + assert( + strcmp( + investigation_record_get_created_at(record), + created_at + ) == 0 + ); + + assert( + strcmp( + investigation_record_get_updated_at(record), + updated_at + ) == 0 + ); + + /* + * Le modèle doit posséder ses propres copies. + */ + id[0] = 'X'; + name[0] = 'X'; + root_path[0] = 'X'; + created_at[0] = 'X'; + updated_at[0] = 'X'; + + assert( + strcmp( + investigation_record_get_id(record), + "6e62b9af-2046-4efd-b3b9-29869f816951" + ) == 0 + ); + + assert( + strcmp( + investigation_record_get_name(record), + "Enquete_Test" + ) == 0 + ); + + assert( + strcmp( + investigation_record_get_root_path(record), + "/tmp/enquete-test" + ) == 0 + ); + + assert( + strcmp( + investigation_record_get_created_at(record), + "2026-07-16T10:00:00Z" + ) == 0 + ); + + assert( + strcmp( + investigation_record_get_updated_at(record), + "2026-07-16T10:00:00Z" + ) == 0 + ); + + investigation_record_free(record); +} + +/** + * @brief Vérifie le refus des pointeurs NULL. + */ +static void test_investigation_record_null_parameters(void) +{ + assert( + investigation_record_new( + NULL, + "Enquete", + "/tmp/enquete", + "2026-07-16T10:00:00Z", + "2026-07-16T10:00:00Z" + ) == NULL + ); + + assert( + investigation_record_new( + "id", + NULL, + "/tmp/enquete", + "2026-07-16T10:00:00Z", + "2026-07-16T10:00:00Z" + ) == NULL + ); + + assert( + investigation_record_new( + "id", + "Enquete", + NULL, + "2026-07-16T10:00:00Z", + "2026-07-16T10:00:00Z" + ) == NULL + ); + + assert( + investigation_record_new( + "id", + "Enquete", + "/tmp/enquete", + NULL, + "2026-07-16T10:00:00Z" + ) == NULL + ); + + assert( + investigation_record_new( + "id", + "Enquete", + "/tmp/enquete", + "2026-07-16T10:00:00Z", + NULL + ) == NULL + ); +} + +/** + * @brief Vérifie le refus des chaînes vides. + */ +static void test_investigation_record_empty_parameters(void) +{ + assert( + investigation_record_new( + "", + "Enquete", + "/tmp/enquete", + "2026-07-16T10:00:00Z", + "2026-07-16T10:00:00Z" + ) == NULL + ); + + assert( + investigation_record_new( + "id", + "", + "/tmp/enquete", + "2026-07-16T10:00:00Z", + "2026-07-16T10:00:00Z" + ) == NULL + ); + + assert( + investigation_record_new( + "id", + "Enquete", + "", + "2026-07-16T10:00:00Z", + "2026-07-16T10:00:00Z" + ) == NULL + ); + + assert( + investigation_record_new( + "id", + "Enquete", + "/tmp/enquete", + "", + "2026-07-16T10:00:00Z" + ) == NULL + ); + + assert( + investigation_record_new( + "id", + "Enquete", + "/tmp/enquete", + "2026-07-16T10:00:00Z", + "" + ) == NULL + ); +} + +/** + * @brief Vérifie les opérations acceptant un modèle NULL. + */ +static void test_investigation_record_null_instance(void) +{ + assert( + investigation_record_get_id(NULL) == NULL + ); + + assert( + investigation_record_get_name(NULL) == NULL + ); + + assert( + investigation_record_get_root_path(NULL) == NULL + ); + + assert( + investigation_record_get_created_at(NULL) == NULL + ); + + assert( + investigation_record_get_updated_at(NULL) == NULL + ); + + investigation_record_free(NULL); +} + +int main(void) +{ + test_investigation_record_valid(); + test_investigation_record_null_parameters(); + test_investigation_record_empty_parameters(); + test_investigation_record_null_instance(); + + printf( + "InvestigationRecord : tous les tests sont valides.\n" + ); + + return 0; +} diff --git a/tests/test_statement b/tests/test_statement index d1be0bcc500b7391725ded6257d7997cfd614986..79b937bc5ded5e08e89ad4858b606f7b0b49b099 100755 GIT binary patch delta 1413 zcmZ{kYe*DP6vywKZOvUY-3$~n#wlvGC=;aDmu69GgC6!F>O)jZn(0=Qi4+OCP?44P zI29B^E=Z+Cuj!qkYm}8`+SAHvH~O#^L=l!@aQn}0jV;u`{D1e(IsbE)d+y%Oda1Ks zIvdTceOWB@^kva-KFOk{gr_Ra;myPS?(l4olq`+2rLGBoHb@I4`@fQRA~|hkIN2ci zB)cu|cV&b0LpnO*OY+Dy-xfd3d{g4?**GS5VC(bQUD_$rLzSARTPKt*b?(aTckWW7 zo!eSUQ;bPfTd?WA5TSXS8m%w4KPxe*-&ys{iCF9dAwI^1qL?jj*JkGWth6_7#reFn z1X{8(rlS9p5Z#=yX=j9J0W;4E!6e3d!Bp_zIU!W=(s?0PfM+xzD#5agLezmrF2M)x zxGaPpYz3K#u{&4c2hG>u2mROK2S+u*589gHH?ay<*@^=AP;(0fz^@=1!q}qQC;(=+ zp#T_n2L-^CyC?uIxQ7B@BiI7o1^r+z$jpqzw8IY`#rCLRZ3p~d=0o_wJh0AO!Pt5X zTJWJ8^n=5?P#}si%Oey3yB?ze=zfX<;7_m;-1`g#z@+E!gI(S5gSMCO4`ob!1wUBW z13!4^HT+=q8~DM`VBH7|%HN^@c-W5u;9TmlhZl zUCNG)lC+qi=(c`h%klciQy}wsBUxBRH(YH4A({Pwtxo*)NKk;@DUnTQLq(JM) z0lh}9(AWdLL0;%4@<9($09r)6Q07-ifgU3V^dq?nWo07T_X+ikb);V7m(}7t43HPO z$H@mhK!MPhua@~cQlJ5HgwzFnPaf!E@rFV%cRu*E1saZ@jU8p~mW2d@Oyb&ae#LVCgket)2Pdl+JuA hu9;#PyvQ05{OiK}P%|agxG43DlUzd*G<8qB=@+cA=P&>O delta 1517 zcmZ{kZ){Ul7{<@J-Nx1kYw4CLnY*nClxbpJ3=lQOOQ7VEs98UZ%r7`(chI#!>z3(s zq$Nzkn4PJwK}{ANW*-<~VckVx!Dt7WBe=mjgs?c-_+pCqC)X@8qH=uN>YYvWCinTB zKkxILn|n^qRKGOUFAWuQYu!4Qed^ZHO8$6Bs4~|L(N_NDN?S13TOzHJ*4S(R9yUon z$^Bo&M^Ie9GnZkKMkTjB8pw^BqcRbKR@ylLx7zJZtKCfP8c$c>7DDavF6M@mV)do&84f};~cba@vJ?q6W7 z&mv=ywXVSYV#fURq!4$k-QESXtI$?s<(?`sHkM1uB;9|~WgCR~PH8p{CE}7kw|Ue$ zT)Wm$v2{;qUrUSsm7wNq)SONJy*q;b7VT9k-MKn)@uUzpIAg`9gh+yXNC+k|HUc_9 zEg^&g9vT*+0gUKEM8Pee3eg8Pe+C~|e@2LT@LiBu82exZe()NofJ5it2Y&*i;H@v< zx3CDSJ&ynkY#&1acmZSujII6(0ia_X0pJ|i0RDIp0ib*d0pJ^868sRH2S-3w$k;6C z1oz>$C}8k1{2;#qKj;Me3L}guXe2Rk9GnMlP9dO(v6&PCz++bt06udK0pJ7}1skp- z08E2P@YoIb!CN!%FJsL1E&SkyS@^-G@8Aa=H{l19U|$&;&(9$M^v@#ztfaBV$GAd9 zV}+DHK~`;z>)xOcXER4};|kj$Y18r|Nq=BjF-kGifj3S*P38|%7+OVfO?KVI{*7Zl zO^4+?&sZ{!Kk$A;Db#*K1~f$WCYc|h8fb`o&=`fGD#f81C7@4H3VN6fXdBsg$^0DE z?2=u7jtcQJ^2~Ll+vb<`;yip!Vbs1xacCnYvh5Ufm<(uw?7K5s13gVXsGGvj0*XW1 zDFJ<*Qdu>iNwWK8*L7^|?|EzfX0F+RBak4UU*<1S7`0ci?eVLdd?LiT1kGBYiNC-46~YW}|toRTz7g*$Kl5p%v5^S^xa|NhT0e;=o4Fz>sLFMU@b zlUO$;nq~eVrJ$W;Ko5{TAUn!%yr%Pvle8&N?dt3o*~f-CE8c(lPhb-&(C^n{feW0X zdk#ygSGVqMv+!E|K-gV~A3V_J;r04V+jCN8ZdLnsiBIe8Z{D@@?&S?CN|tWQ>Yfc&rsYbQM$J9zOO`Io0ZjhuV*a6L{c`g{&Ro#6qrjmn@7OGV E0X`ECaR2}S diff --git a/tests/test_transaction b/tests/test_transaction index 7291ecd12d3923831bc624b835fc5dc28291b9bd..3533f197f356319952e8176b0788354d8f1b182e 100755 GIT binary patch delta 2768 zcmZuz3s6+o89wK(=q?XsVReB8?=DIJ73nInOG(?c9peTRB|=-3L@;7R(JH|-shA~! zJa!n2LW208e^#OfoUFVNR2zmOtE$5^gDNV?}F)_ z+4J4=fB*NL|DN+-xWj@^2!61LPxKXENxoFf^y^YFW$?wx6SnAd!bVkm(E7`|=+r>U zRtl}7v8aJmi>Q$k*HBO?bak$ZI^2%kJR0w&5yfsb$211gP35pn{h+jD{5$)PoO$s# z?5&v=_fpFrsl9-rC&M5sO2n859c4~5X@Cw*qQ zKeVgJo;BeQo|j9V2RS}Vr!n(hpsnTiSAS*GYZ)Ds`zfrG2uS4c@`)$FObjU&id~KS;%T=|1xOO6aE&qz?s%~@;4ll zWbf?F(jjsI^bQjm?P44 zgU4K6$<#L^;qvOgCcC^rVQ)#=iE9ay{s z@%R@)9Q^+#l`sLt*jKaW{s$|V{atUFF=g7cdgt_`zQz>oVD)?+knde7!u`ji$1wa? zA^H(JeL{#a;I@-OFom(ZPYK}!)}0o@2i*I%5RJfJydy*>@XEW;0Y4lTA_TOIKo8sj zWayane}^7e@E-KQCFh_wu>fm+9|j17fc?NLzz|RX$AJ0gVSuB4^#d3HkD_0Ez?Eo^ zM&MRpC-6yNKkzy*1e|{ndZ2m<`Xt6ay9_r80uv8hJ^c_D9$b$ zXEMJr!8gSCd6W735_|{v9O!AvCLAy};gEud?4=PDvz;OsuaLD?<(J8=kF~X`>khPE z`4qX^kT;+`@1#Ct*++#qH6`n~k#z`JLo@=%A5#S5!(_cjZ!k#q?26Sz}dA-p9EC#$w5w*4Zq-l534O4qG7gnr3? z3CZ}y=T3QWn5Sy1>y}#RTP8Dg-utvNT%jf3x68ywwMUwMYT{`W*|L%|s%S3domxwC zp^`mZA=gY?*YPkJES2q|7amy2HHti7SI&5~=;9-Z^!)60BTmQ+||nMuBbA+1)YLSrQHV zuCV&BQbU(zF_2~zvjS4>1wN;cWXCWQ_R*aP3=va zn;vZ5)ZEtgleSF{Hfze>6qAvog$JYL(>740TvBig;j zS3_kyKDKhQSK)b?)m%O6Dw@RMDwy z35||U2`cUq7ZEwM|vmbH?YpKHhe5j(^7W0CK$IlDBrdkmr8Q0 zFH%uLZ157SYU`uuqsG-p{;6+GIufR|diG*VOQ@W{{q#Cpf8sQtOdPg!uvU2#VUYEO-OeU6-58$Lo- zc)nf9y+|p=CgHa6ja|*OX?=Z%$swg#$5cdaAe$fO=T;-(O# zhl-83O;f1y7@itmqpi1nqT`Wn38nj{!9jnw$_;zBb}klu5j5^26oN#;0Hkxw+}8M&C}TcrQmD$4$46{SPhooTwI_Er{Mt& zU}OO5K{F_iFj8k2_0lgwyePY`1jyf~)E`$eu4;NIBcAOeTZWODsVu|DA0DM`83x{R zjOu`E$LXew6Qs#Bus)QSY2bBt^*CBiPIiLjh(u~8Q#d0({7b!q zKjtq7`Sbc-0fCYfjnF?hzA>cEl{-nyHS&aOWX(0O&18e4x=w50JfRBYJ=B^zI=1tG zP=>sj8eBd<2JfK`zRHS>hvGrS!LN2`S39w