labfy-investigation/tests/test_identity_traceability.c
2026-07-30 11:10:32 +02:00

233 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "dao/identity_traceability_dao.h"
#include "views/person_vocabulary_adapter.h"
#include "dao/identity_ocr_dao.h"
#include "database/database.h"
#include "models/identity_traceability.h"
#include <glib.h>
#include <glib/gstdio.h>
#include <sqlite3.h>
#define EVIDENCE "10000000-0000-4000-8000-000000000018"
#define PERSON "20000000-0000-4000-8000-000000000018"
#define RUN "30000000-0000-4000-8000-000000000018"
#define DOCUMENT "40000000-0000-4000-8000-000000000018"
#define FIELD "50000000-0000-4000-8000-000000000018"
#define ASSESSMENT1 "60000000-0000-4000-8000-000000000018"
#define ASSESSMENT2 "60000000-0000-4000-8000-000000000019"
#define FACT "70000000-0000-4000-8000-000000000018"
#define AT "2026-07-30T10:00:00Z"
typedef struct{char*dir,*path;Database*database;}Fixture;
static void exec_ok(sqlite3*d,const char*sql)
{char*message=NULL;g_assert_cmpint(sqlite3_exec(d,sql,NULL,NULL,&message),==,SQLITE_OK);
sqlite3_free(message);}
static char *scalar(sqlite3*d,const char*sql)
{sqlite3_stmt*s=NULL;g_assert_cmpint(sqlite3_prepare_v2(d,sql,-1,&s,NULL),==,SQLITE_OK);
g_assert_cmpint(sqlite3_step(s),==,SQLITE_ROW);char*r=g_strdup(
(const char*)sqlite3_column_text(s,0));sqlite3_finalize(s);return r;}
static Fixture fixture_new(void)
{
Fixture f={0};GError*e=NULL;f.dir=g_dir_make_tmp("labfy-v18-XXXXXX",&e);
g_assert_no_error(e);f.path=g_build_filename(f.dir,"Enquete.sqlite",NULL);
g_assert_true(database_initialize(f.path,"SPECIMEN V18",f.dir));
sqlite3*d=NULL;g_assert_cmpint(sqlite3_open(f.path,&d),==,SQLITE_OK);
exec_ok(d,"PRAGMA foreign_keys=ON;"
"INSERT INTO preuves(id,name,relative_path,type_id,size_bytes,sha256,"
"imported_at,updated_at,status,locked,original_name) VALUES('" EVIDENCE
"','specimen.png','specimen.png',2,8,"
"'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','" AT
"','" AT "','active',0,'specimen.png');"
"INSERT INTO entites(id,type_id,valeur,label,confiance,created_at,updated_at,status)"
" VALUES('" PERSON "',(SELECT id FROM types_entite WHERE code='person'),"
"'Personne SPECIMEN','Personne SPECIMEN',0,'" AT "','" AT "','active');"
"INSERT INTO identity_ocr_runs(id,evidence_id,expected_sha256,page_number,"
"document_type,document_side,engine,requested_languages,available_languages,"
"parameters,preprocessing_profile,executed_at,status,text_relative_path,"
"text_sha256,tsv_relative_path,tsv_sha256) VALUES('" RUN "','" EVIDENCE "',"
"'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',1,"
"'identity_card','front','tesseract','fra','fra','SPECIMEN','none','" AT
"','success','raw.txt','bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',"
"'raw.tsv','cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc');"
"INSERT INTO identity_document_observations(id,person_id,evidence_id,ocr_run_id,"
"document_type,document_side,page_number,review_state,observed_at) VALUES('"
DOCUMENT "','" PERSON "','" EVIDENCE "','" RUN "','identity_card','front',1,"
"'accepted','" AT "');"
"INSERT INTO identity_field_observations(id,observation_id,field_code,raw_value,"
"normalized_value,review_status,origin,evidence_id,ocr_run_id,page_number,"
"display_order,reviewed_at) VALUES('" FIELD "','" DOCUMENT "','surname',"
"'BRUT SPECIMEN','BRUT SPECIMEN','accepted','ocr','" EVIDENCE "','" RUN
"',1,0,'" AT "');");
sqlite3_close(d);f.database=database_open(f.path);g_assert_nonnull(f.database);
g_assert_true(database_migrate_to_latest(f.database));return f;
}
static void fixture_free(Fixture*f)
{database_close(f->database);g_remove(f->path);g_rmdir(f->dir);
g_free(f->path);g_free(f->dir);}
static void test_models(void)
{
PersonRoleVocabularyEntry justified={.requires_justification=TRUE};
g_assert_false(person_vocabulary_adapter_justification_valid(
&justified," "));
g_assert_true(person_vocabulary_adapter_justification_valid(
&justified,"Justification SPECIMEN"));
g_assert_true(identity_traceability_identification_status_valid("disputed"));
g_assert_false(identity_traceability_identification_status_valid("identified"));
g_assert_false(identity_traceability_relation_type_valid("is_author"));
g_assert_false(identity_traceability_field_is_projectable(
"accepted","uncertain","human_confirmed","SPECIMEN"));
g_assert_true(identity_traceability_field_is_projectable(
"modified","partial","human_confirmed","SPECIMEN"));
g_assert_null(document_authenticity_assessment_new(ASSESSMENT1,EVIDENCE,NULL,
"confirmed_forged",NULL,AT,NULL,NULL));
DocumentAuthenticityAssessment*a=document_authenticity_assessment_new(
ASSESSMENT1,EVIDENCE,NULL,"confirmed_forged","Justification SPECIMEN",AT,NULL,NULL);
g_assert_nonnull(a);DocumentAuthenticityAssessment*c=
document_authenticity_assessment_copy(a);g_assert_cmpstr(c->status,==,a->status);
document_authenticity_assessment_free(c);document_authenticity_assessment_free(a);
}
static void test_dao_history_and_relations(void)
{
Fixture f=fixture_new();GError*e=NULL;
IdentityTraceabilityDao*d=identity_traceability_dao_new(f.database);
DocumentAuthenticityAssessment*a1=document_authenticity_assessment_new(
ASSESSMENT1,EVIDENCE,RUN,"indeterminate",NULL,AT,NULL,"Note SPECIMEN");
DocumentAuthenticityAssessment*a2=document_authenticity_assessment_new(
ASSESSMENT2,EVIDENCE,RUN,"suspicious","Anomalie SPECIMEN",
"2026-07-30T11:00:00Z",ASSESSMENT1,NULL);
g_assert_true(identity_traceability_dao_insert_authenticity(d,a1,&e));
g_assert_no_error(e);g_assert_true(identity_traceability_dao_insert_authenticity(d,a2,&e));
GPtrArray*h=identity_traceability_dao_list_authenticity(d,EVIDENCE,&e);
g_assert_cmpuint(h->len,==,2);g_ptr_array_unref(h);
DocumentAuthenticityAssessment*current=
identity_traceability_dao_current_authenticity(d,EVIDENCE,&e);
g_assert_cmpstr(current->identifier,==,ASSESSMENT2);
PersonEvidenceFactualRelation*r=person_evidence_factual_relation_new(
FACT,PERSON,EVIDENCE,RUN,"identity_observed_in","Observation SPECIMEN",AT,TRUE);
g_assert_true(identity_traceability_dao_insert_factual_relation(d,r,&e));
GPtrArray*relations=identity_traceability_dao_list_factual_relations(d,EVIDENCE,&e);
g_assert_cmpuint(relations->len,==,1);g_ptr_array_unref(relations);
GPtrArray*roles=identity_traceability_dao_list_roles(d,TRUE,&e);
g_assert_cmpuint(roles->len,>=,14);g_ptr_array_unref(roles);
roles=identity_traceability_dao_list_roles(d,FALSE,&e);
g_assert_cmpuint(roles->len,==,10);
for(guint i=0;i<roles->len;i++){
PersonRoleVocabularyEntry*role=g_ptr_array_index(roles,i);
g_assert_true(role->active);g_assert_nonnull(role->code);
g_assert_nonnull(role->label);g_assert_nonnull(role->description);
if(i>0)g_assert_cmpint(((PersonRoleVocabularyEntry*)
g_ptr_array_index(roles,i-1))->display_order,<,role->display_order);
}
g_ptr_array_unref(roles);
GPtrArray*statuses=identity_traceability_dao_list_identification_statuses(
d,FALSE,&e);g_assert_no_error(e);g_assert_cmpuint(statuses->len,==,6);
for(guint i=0;i<statuses->len;i++){
IdentificationStatusVocabularyEntry*status=g_ptr_array_index(statuses,i);
g_assert_true(status->active);g_assert_nonnull(status->description);
if(i>0)g_assert_cmpint(((IdentificationStatusVocabularyEntry*)
g_ptr_array_index(statuses,i-1))->display_order,<,status->display_order);
}
g_ptr_array_unref(statuses);
person_evidence_factual_relation_free(r);
document_authenticity_assessment_free(current);
document_authenticity_assessment_free(a2);document_authenticity_assessment_free(a1);
identity_traceability_dao_free(d);fixture_free(&f);
}
static void test_sqlite_negative_guards(void)
{
Fixture f=fixture_new();database_close(f.database);f.database=NULL;
sqlite3*d=NULL;g_assert_cmpint(sqlite3_open(f.path,&d),==,SQLITE_OK);
exec_ok(d,"PRAGMA foreign_keys=ON;");
char*before=scalar(d,"SELECT raw_value FROM identity_field_observations WHERE id='" FIELD "';");
char*message=NULL;
g_assert_cmpint(sqlite3_exec(d,"INSERT INTO document_authenticity_assessments"
"(id,evidence_id,status,assessed_at,origin) VALUES("
"'80000000-0000-4000-8000-000000000018','" EVIDENCE "',"
"'confirmed_forged','" AT "','human');",NULL,NULL,&message),!=,SQLITE_OK);
sqlite3_free(message);message=NULL;
g_assert_cmpint(sqlite3_exec(d,"INSERT INTO person_evidence_factual_relations"
"(id,person_id,evidence_id,relation_type,observed_at,origin) VALUES("
"'80000000-0000-4000-8000-000000000019','" PERSON "','" EVIDENCE "',"
"'is_author','" AT "','human');",NULL,NULL,&message),!=,SQLITE_OK);
sqlite3_free(message);message=NULL;
g_assert_cmpint(sqlite3_exec(d,"UPDATE identity_field_observations SET "
"confirmed_value='SPECIMEN',confirmation_state='human_confirmed',"
"value_quality='uncertain' WHERE id='" FIELD "';",NULL,NULL,&message),!=,SQLITE_OK);
sqlite3_free(message);
char*raw=scalar(d,"SELECT raw_value FROM identity_field_observations WHERE id='" FIELD "';");
char*confirmed=scalar(d,"SELECT COALESCE(confirmed_value,'NULL') FROM "
"identity_field_observations WHERE id='" FIELD "';");
char*auth=scalar(d,"SELECT COUNT(*) FROM document_authenticity_assessments;");
char*relations=scalar(d,"SELECT COUNT(*) FROM person_evidence_factual_relations;");
g_assert_cmpstr(raw,==,before);g_assert_cmpstr(confirmed,==,"NULL");
g_assert_cmpstr(auth,==,"0");g_assert_cmpstr(relations,==,"0");
exec_ok(d,"UPDATE identity_field_observations SET "
"confirmed_value='CONFIRMÉ SPECIMEN',confirmation_state='human_confirmed',"
"value_quality='partial' WHERE id='" FIELD "';");
g_free(before);g_free(raw);g_free(confirmed);g_free(auth);g_free(relations);
sqlite3_close(d);f.database=database_open(f.path);g_assert_nonnull(f.database);
g_assert_true(database_migrate_to_latest(f.database));
IdentityOcrDao*ocr=identity_ocr_dao_new(f.database);GError*error=NULL;
GPtrArray*confirmed_fields=identity_ocr_dao_list_confirmed_fields(
ocr,DOCUMENT,&error);g_assert_no_error(error);
g_assert_cmpuint(confirmed_fields->len,==,1);
IdentityFieldObservationRecord*record=g_ptr_array_index(confirmed_fields,0);
g_assert_cmpstr(record->confirmed_value,==,"CONFIRMÉ SPECIMEN");
g_ptr_array_unref(confirmed_fields);identity_ocr_dao_free(ocr);fixture_free(&f);
}
static void test_migrate_v17_preserves_ocr(void)
{
Fixture f=fixture_new();database_close(f.database);f.database=NULL;
sqlite3*d=NULL;g_assert_cmpint(sqlite3_open(f.path,&d),==,SQLITE_OK);
exec_ok(d,"PRAGMA foreign_keys=OFF;"
"DROP TABLE person_identification_assessments;"
"DROP TABLE person_evidence_factual_relations;"
"DROP TABLE document_authenticity_assessments;"
"DROP TABLE person_role_vocabulary;DROP TABLE identification_status_vocabulary;"
"ALTER TABLE identity_field_observations RENAME TO identity_fields_v18_old;"
"CREATE TABLE identity_field_observations(id TEXT PRIMARY KEY,"
"observation_id TEXT NOT NULL,field_code TEXT NOT NULL,raw_value TEXT,"
"corrected_value TEXT,normalized_value TEXT,confidence REAL,"
"review_status TEXT NOT NULL,origin TEXT NOT NULL,evidence_id TEXT NOT NULL,"
"ocr_run_id TEXT NOT NULL,page_number INTEGER NOT NULL,source_x INTEGER,"
"source_y INTEGER,source_width INTEGER,source_height INTEGER,"
"source_image_width INTEGER,source_image_height INTEGER,display_order INTEGER,"
"reviewed_at TEXT NOT NULL,review_note TEXT);"
"INSERT INTO identity_field_observations SELECT id,observation_id,field_code,"
"raw_value,'CORRIGÉ SPECIMEN',normalized_value,confidence,review_status,origin,"
"evidence_id,ocr_run_id,page_number,source_x,source_y,source_width,source_height,"
"source_image_width,source_image_height,display_order,reviewed_at,review_note "
"FROM identity_fields_v18_old;DROP TABLE identity_fields_v18_old;"
"UPDATE metadata SET value='17' WHERE key='schema_version';"
"CREATE TABLE identification_status_vocabulary(dummy TEXT);");
sqlite3_close(d);f.database=database_open(f.path);g_assert_nonnull(f.database);
g_test_expect_message(NULL,G_LOG_LEVEL_WARNING,
"*Impossible dinstaller la migration SQLite V18*");
g_assert_false(database_migrate_to_latest(f.database));
g_test_assert_expected_messages();database_close(f.database);
f.database=NULL;g_assert_cmpint(sqlite3_open(f.path,&d),==,SQLITE_OK);
char*rolled_back=scalar(d,
"SELECT value FROM metadata WHERE key='schema_version';");
char*partial=scalar(d,"SELECT COUNT(*) FROM sqlite_master WHERE type='table' "
"AND name='document_authenticity_assessments';");
g_assert_cmpstr(rolled_back,==,"17");g_assert_cmpstr(partial,==,"0");
g_free(rolled_back);g_free(partial);
exec_ok(d,"DROP TABLE identification_status_vocabulary;");
sqlite3_close(d);f.database=database_open(f.path);g_assert_nonnull(f.database);
g_assert_true(database_migrate_to_latest(f.database));database_close(f.database);
f.database=NULL;g_assert_cmpint(sqlite3_open(f.path,&d),==,SQLITE_OK);
char*version=scalar(d,"SELECT value FROM metadata WHERE key='schema_version';");
char*values=scalar(d,"SELECT raw_value||':'||normalized_value||':'||corrected_value"
"||':'||confirmation_state FROM identity_field_observations WHERE id='" FIELD "';");
g_assert_cmpstr(version,==,"18");
g_assert_cmpstr(values,==,"BRUT SPECIMEN:BRUT SPECIMEN:CORRIGÉ SPECIMEN:unconfirmed");
sqlite3_close(d);g_free(version);g_free(values);
f.database=database_open(f.path);g_assert_true(database_migrate_to_latest(f.database));
fixture_free(&f);
}
int main(int argc,char**argv)
{g_test_init(&argc,&argv,NULL);g_test_add_func("/v18/models",test_models);
g_test_add_func("/v18/dao-history-relations",test_dao_history_and_relations);
g_test_add_func("/v18/sqlite-negative-guards",test_sqlite_negative_guards);
g_test_add_func("/v18/migrate-v17",test_migrate_v17_preserves_ocr);
return g_test_run();}