feat: add entity type model

This commit is contained in:
grayTerminal-sh 2026-07-20 19:52:11 +02:00
parent ed7011cdc4
commit 12d201cf8a
4 changed files with 940 additions and 0 deletions

View file

@ -25,6 +25,7 @@ TEST_LDFLAGS = $(shell $(PKG_CONFIG) --libs glib-2.0 gio-2.0)
EVIDENCE_RECORD_TEST_CFLAGS := $(TEST_CFLAGS) -Wpedantic
EVIDENCE_TYPE_TEST_CFLAGS := $(TEST_CFLAGS) -Wpedantic
ENTITY_TYPE_TEST_CFLAGS := $(TEST_CFLAGS) -Wpedantic
EVIDENCE_TYPE_DAO_TEST_CFLAGS := $(TEST_CFLAGS) -Wpedantic
EVIDENCE_IMPORT_DIALOG_TEST_CFLAGS := \
-std=c17 \
@ -73,6 +74,7 @@ TEST_EVIDENCE_COPY := tests/test_evidence_copy
TEST_EVIDENCE_IMPORTER := tests/test_evidence_importer
TEST_EVIDENCE_IMPORT_TASK := tests/test_evidence_import_task
TEST_EVIDENCE_TYPE := tests/test_evidence_type
TEST_ENTITY_TYPE := tests/test_entity_type
TEST_EVIDENCE_TYPE_DAO := tests/test_evidence_type_dao
TEST_EVIDENCE_IMPORT_DIALOG := tests/test_evidence_import_dialog
TEST_EVIDENCE_LIST_ITEM := tests/test_evidence_list_item
@ -315,6 +317,11 @@ $(TEST_EVIDENCE_TYPE): \
src/models/evidence_type.c
$(CC) $(EVIDENCE_TYPE_TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_ENTITY_TYPE): \
tests/test_entity_type.c \
src/models/entity_type.c
$(CC) $(ENTITY_TYPE_TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_EVIDENCE_TYPE_DAO): \
tests/test_evidence_type_dao.c \
src/dao/evidence_type_dao.c \
@ -376,6 +383,7 @@ test: \
$(TEST_EVIDENCE_CATEGORY_ITEM) \
$(TEST_EVIDENCE_CATEGORY_MODEL) \
$(TEST_EVIDENCE_TYPE) \
$(TEST_ENTITY_TYPE) \
$(TEST_INVESTIGATION_DAO) \
$(TEST_EVIDENCE_DAO) \
$(TEST_EVIDENCE_TYPE_DAO) \
@ -410,6 +418,7 @@ test: \
@$(TEST_EVIDENCE_CATEGORY_MODEL)
@$(TEST_EVIDENCE_RECORD)
@$(TEST_EVIDENCE_TYPE)
@$(TEST_ENTITY_TYPE)
@$(TEST_INVESTIGATION_DAO)
@$(TEST_EVIDENCE_DAO)
@$(TEST_EVIDENCE_TYPE_DAO)
@ -453,6 +462,7 @@ clean:
$(TEST_EVIDENCE_CATEGORY_ITEM) \
$(TEST_EVIDENCE_CATEGORY_MODEL) \
$(TEST_EVIDENCE_TYPE) \
$(TEST_ENTITY_TYPE) \
$(TEST_EVIDENCE_TYPE_DAO) \
$(TEST_INVESTIGATION_DAO) \
$(TEST_EVIDENCE_DAO) \

View file

@ -0,0 +1,132 @@
/******************************************************************************
* @file entity_type.h
* @brief Modèle représentant un type d'entité.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_ENTITY_TYPE_H
#define LABFY_INVESTIGATION_ENTITY_TYPE_H
#include <glib.h>
G_BEGIN_DECLS
/**
* @brief Modèle opaque représentant un type d'entité.
*/
typedef struct EntityType EntityType;
/**
* @brief Codes d'erreur produits par EntityType.
*/
typedef enum
{
/**
* Un argument général est invalide.
*/
ENTITY_TYPE_ERROR_INVALID_ARGUMENT,
/**
* L'identifiant numérique est invalide.
*/
ENTITY_TYPE_ERROR_INVALID_IDENTIFIER,
/**
* Le code métier est invalide.
*/
ENTITY_TYPE_ERROR_INVALID_CODE,
/**
* Le libellé utilisateur est invalide.
*/
ENTITY_TYPE_ERROR_INVALID_LABEL
} EntityTypeError;
/**
* @brief Domaine d'erreur du modèle EntityType.
*/
#define ENTITY_TYPE_ERROR \
entity_type_error_quark()
/**
* @brief Retourne le domaine d'erreur du modèle.
*
* @return Quark GLib associé à EntityType.
*/
GQuark entity_type_error_quark(void);
/**
* @brief Crée un type d'entité.
*
* Les chaînes sont copiées et appartiennent au nouvel objet.
*
* L'identifiant doit être strictement positif.
*
* Le code et le libellé sont obligatoires.
* La description peut être NULL ou vide.
*
* @param identifier Identifiant numérique SQLite.
* @param code Code métier du type.
* @param label Libellé destiné à l'utilisateur.
* @param description Description facultative.
* @param error Emplacement facultatif recevant une erreur.
*
* @return Nouveau modèle, ou NULL lorsque les données sont invalides.
*/
EntityType *entity_type_new(
gint64 identifier,
const char *code,
const char *label,
const char *description,
GError **error
);
/**
* @brief Libère un modèle EntityType.
*
* Cette fonction accepte entity_type == NULL.
*
* @param entity_type Modèle à libérer.
*/
void entity_type_free(
EntityType *entity_type
);
/**
* @brief Retourne l'identifiant numérique du type.
*
* @return Identifiant, ou 0 lorsque entity_type est NULL.
*/
gint64 entity_type_get_identifier(
const EntityType *entity_type
);
/**
* @brief Retourne le code métier du type.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *entity_type_get_code(
const EntityType *entity_type
);
/**
* @brief Retourne le libellé utilisateur du type.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *entity_type_get_label(
const EntityType *entity_type
);
/**
* @brief Retourne la description du type.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *entity_type_get_description(
const EntityType *entity_type
);
G_END_DECLS
#endif

275
src/models/entity_type.c Normal file
View file

@ -0,0 +1,275 @@
/******************************************************************************
* @file entity_type.c
* @brief Implémentation du modèle représentant un type d'entité.
******************************************************************************/
#include "models/entity_type.h"
#include <glib.h>
/**
* @struct EntityType
* @brief Représentation interne d'un type d'entité.
*/
struct EntityType
{
gint64 identifier;
char *code;
char *label;
char *description;
};
/**
* @brief Copie une chaîne après suppression des espaces périphériques.
*
* @param value Chaîne à copier.
*
* @return Nouvelle chaîne normalisée, ou NULL lorsque value vaut NULL.
*/
static char *entity_type_duplicate_trimmed(
const char *value
)
{
char *copy =
NULL;
if (value == NULL)
{
return NULL;
}
copy =
g_strdup(
value
);
g_strstrip(
copy
);
return copy;
}
/**
* @brief Vérifie qu'une chaîne obligatoire contient du texte.
*
* @param value Chaîne déjà normalisée.
*
* @return TRUE lorsque la chaîne contient au moins un caractère.
*/
static gboolean entity_type_required_value_is_valid(
const char *value
)
{
return value != NULL &&
value[0] != '\0';
}
GQuark entity_type_error_quark(void)
{
return g_quark_from_static_string(
"entity-type-error-quark"
);
}
EntityType *entity_type_new(
gint64 identifier,
const char *code,
const char *label,
const char *description,
GError **error
)
{
EntityType *entity_type =
NULL;
char *normalized_code =
NULL;
char *normalized_label =
NULL;
char *normalized_description =
NULL;
g_return_val_if_fail(
error == NULL || *error == NULL,
NULL
);
if (identifier <= 0)
{
g_set_error_literal(
error,
ENTITY_TYPE_ERROR,
ENTITY_TYPE_ERROR_INVALID_IDENTIFIER,
"L'identifiant du type d'entité doit être strictement positif."
);
return NULL;
}
normalized_code =
entity_type_duplicate_trimmed(
code
);
if (!entity_type_required_value_is_valid(
normalized_code
))
{
g_set_error_literal(
error,
ENTITY_TYPE_ERROR,
ENTITY_TYPE_ERROR_INVALID_CODE,
"Le code du type d'entité est obligatoire."
);
g_free(
normalized_code
);
return NULL;
}
normalized_label =
entity_type_duplicate_trimmed(
label
);
if (!entity_type_required_value_is_valid(
normalized_label
))
{
g_set_error_literal(
error,
ENTITY_TYPE_ERROR,
ENTITY_TYPE_ERROR_INVALID_LABEL,
"Le libellé du type d'entité est obligatoire."
);
g_free(
normalized_label
);
g_free(
normalized_code
);
return NULL;
}
normalized_description =
entity_type_duplicate_trimmed(
description
);
/*
* Une description vide ou composée uniquement d'espaces
* est représentée par NULL.
*/
if (normalized_description != NULL &&
normalized_description[0] == '\0')
{
g_free(
normalized_description
);
normalized_description =
NULL;
}
entity_type =
g_new0(
EntityType,
1
);
entity_type->identifier =
identifier;
entity_type->code =
normalized_code;
entity_type->label =
normalized_label;
entity_type->description =
normalized_description;
return entity_type;
}
void entity_type_free(
EntityType *entity_type
)
{
if (entity_type == NULL)
{
return;
}
g_free(
entity_type->description
);
g_free(
entity_type->label
);
g_free(
entity_type->code
);
g_free(
entity_type
);
}
gint64 entity_type_get_identifier(
const EntityType *entity_type
)
{
if (entity_type == NULL)
{
return 0;
}
return entity_type->identifier;
}
const char *entity_type_get_code(
const EntityType *entity_type
)
{
if (entity_type == NULL)
{
return NULL;
}
return entity_type->code;
}
const char *entity_type_get_label(
const EntityType *entity_type
)
{
if (entity_type == NULL)
{
return NULL;
}
return entity_type->label;
}
const char *entity_type_get_description(
const EntityType *entity_type
)
{
if (entity_type == NULL)
{
return NULL;
}
return entity_type->description;
}

523
tests/test_entity_type.c Normal file
View file

@ -0,0 +1,523 @@
/******************************************************************************
* @file test_entity_type.c
* @brief Tests unitaires du modèle EntityType.
******************************************************************************/
#include "models/entity_type.h"
#include <glib.h>
/**
* @brief Vérifie la création d'un type valide.
*/
static void test_entity_type_new_valid(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
3,
"email",
"Adresse e-mail",
"Adresse électronique associée à l'enquête.",
&error
);
g_assert_no_error(
error
);
g_assert_nonnull(
entity_type
);
g_assert_cmpint(
entity_type_get_identifier(
entity_type
),
==,
3
);
g_assert_cmpstr(
entity_type_get_code(
entity_type
),
==,
"email"
);
g_assert_cmpstr(
entity_type_get_label(
entity_type
),
==,
"Adresse e-mail"
);
g_assert_cmpstr(
entity_type_get_description(
entity_type
),
==,
"Adresse électronique associée à l'enquête."
);
entity_type_free(
entity_type
);
}
/**
* @brief Vérifie la suppression des espaces périphériques.
*/
static void test_entity_type_values_are_trimmed(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
4,
" iban ",
" IBAN ",
" Identifiant international d'un compte bancaire. ",
&error
);
g_assert_no_error(
error
);
g_assert_nonnull(
entity_type
);
g_assert_cmpstr(
entity_type_get_code(
entity_type
),
==,
"iban"
);
g_assert_cmpstr(
entity_type_get_label(
entity_type
),
==,
"IBAN"
);
g_assert_cmpstr(
entity_type_get_description(
entity_type
),
==,
"Identifiant international d'un compte bancaire."
);
entity_type_free(
entity_type
);
}
/**
* @brief Vérifie qu'une description absente reste facultative.
*/
static void test_entity_type_description_is_optional(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
14,
"other",
"Autre",
NULL,
&error
);
g_assert_no_error(
error
);
g_assert_nonnull(
entity_type
);
g_assert_null(
entity_type_get_description(
entity_type
)
);
entity_type_free(
entity_type
);
}
/**
* @brief Vérifie qu'une description vide est normalisée vers NULL.
*/
static void test_entity_type_empty_description_becomes_null(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
7,
"person",
"Personne",
" ",
&error
);
g_assert_no_error(
error
);
g_assert_nonnull(
entity_type
);
g_assert_null(
entity_type_get_description(
entity_type
)
);
entity_type_free(
entity_type
);
}
/**
* @brief Vérifie le refus d'un identifiant nul.
*/
static void test_entity_type_zero_identifier(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
0,
"email",
"Adresse e-mail",
NULL,
&error
);
g_assert_null(
entity_type
);
g_assert_error(
error,
ENTITY_TYPE_ERROR,
ENTITY_TYPE_ERROR_INVALID_IDENTIFIER
);
g_clear_error(
&error
);
}
/**
* @brief Vérifie le refus d'un identifiant négatif.
*/
static void test_entity_type_negative_identifier(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
-1,
"email",
"Adresse e-mail",
NULL,
&error
);
g_assert_null(
entity_type
);
g_assert_error(
error,
ENTITY_TYPE_ERROR,
ENTITY_TYPE_ERROR_INVALID_IDENTIFIER
);
g_clear_error(
&error
);
}
/**
* @brief Vérifie le refus d'un code absent.
*/
static void test_entity_type_missing_code(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
3,
NULL,
"Adresse e-mail",
NULL,
&error
);
g_assert_null(
entity_type
);
g_assert_error(
error,
ENTITY_TYPE_ERROR,
ENTITY_TYPE_ERROR_INVALID_CODE
);
g_clear_error(
&error
);
}
/**
* @brief Vérifie le refus d'un code vide.
*/
static void test_entity_type_empty_code(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
3,
" ",
"Adresse e-mail",
NULL,
&error
);
g_assert_null(
entity_type
);
g_assert_error(
error,
ENTITY_TYPE_ERROR,
ENTITY_TYPE_ERROR_INVALID_CODE
);
g_clear_error(
&error
);
}
/**
* @brief Vérifie le refus d'un libellé absent.
*/
static void test_entity_type_missing_label(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
3,
"email",
NULL,
NULL,
&error
);
g_assert_null(
entity_type
);
g_assert_error(
error,
ENTITY_TYPE_ERROR,
ENTITY_TYPE_ERROR_INVALID_LABEL
);
g_clear_error(
&error
);
}
/**
* @brief Vérifie le refus d'un libellé vide.
*/
static void test_entity_type_empty_label(void)
{
EntityType *entity_type =
NULL;
GError *error =
NULL;
entity_type =
entity_type_new(
3,
"email",
" ",
NULL,
&error
);
g_assert_null(
entity_type
);
g_assert_error(
error,
ENTITY_TYPE_ERROR,
ENTITY_TYPE_ERROR_INVALID_LABEL
);
g_clear_error(
&error
);
}
/**
* @brief Vérifie le comportement des getters avec NULL.
*/
static void test_entity_type_null_getters(void)
{
g_assert_cmpint(
entity_type_get_identifier(
NULL
),
==,
0
);
g_assert_null(
entity_type_get_code(
NULL
)
);
g_assert_null(
entity_type_get_label(
NULL
)
);
g_assert_null(
entity_type_get_description(
NULL
)
);
entity_type_free(
NULL
);
}
int main(
int argc,
char **argv
)
{
g_test_init(
&argc,
&argv,
NULL
);
g_test_add_func(
"/entity-type/new-valid",
test_entity_type_new_valid
);
g_test_add_func(
"/entity-type/values-are-trimmed",
test_entity_type_values_are_trimmed
);
g_test_add_func(
"/entity-type/description-is-optional",
test_entity_type_description_is_optional
);
g_test_add_func(
"/entity-type/empty-description-becomes-null",
test_entity_type_empty_description_becomes_null
);
g_test_add_func(
"/entity-type/zero-identifier",
test_entity_type_zero_identifier
);
g_test_add_func(
"/entity-type/negative-identifier",
test_entity_type_negative_identifier
);
g_test_add_func(
"/entity-type/missing-code",
test_entity_type_missing_code
);
g_test_add_func(
"/entity-type/empty-code",
test_entity_type_empty_code
);
g_test_add_func(
"/entity-type/missing-label",
test_entity_type_missing_label
);
g_test_add_func(
"/entity-type/empty-label",
test_entity_type_empty_label
);
g_test_add_func(
"/entity-type/null-getters",
test_entity_type_null_getters
);
return g_test_run();
}