#include "core/evidence_staging.h" #include "core/person_creation_coordinator.h" #include "database/database.h" #include "database/statement.h" #include "database/transaction.h" #include "dao/identity_ocr_dao.h" #include "dao/entity_dao.h" #include #include static guint64 count(Database *database, const char *table) { char *sql = g_strdup_printf("SELECT COUNT(*) FROM %s;", table); DatabaseStatement *statement = database_statement_prepare(database, sql); int64_t value = 0; g_assert_nonnull(statement); g_assert_cmpint(database_statement_step(statement), ==, DATABASE_STATEMENT_STEP_ROW); g_assert_true(database_statement_column_int64(statement, 0, &value)); database_statement_finalize(statement); g_free(sql); return (guint64) value; } static void execute_sql(Database *database, const char *sql) { DatabaseStatement *statement = database_statement_prepare(database, sql); g_assert_nonnull(statement); g_assert_cmpint(database_statement_step(statement), ==, DATABASE_STATEMENT_STEP_DONE); database_statement_finalize(statement); } static void remove_tree(const char *root) { GDir *directory = g_dir_open(root, 0, NULL); const char *name; if (directory == NULL) return; while ((name = g_dir_read_name(directory)) != NULL) { char *path = g_build_filename(root, name, NULL); if (g_file_test(path, G_FILE_TEST_IS_DIR)) remove_tree(path); else g_unlink(path); g_free(path); } g_dir_close(directory); g_rmdir(root); } typedef struct { guint calls; } SessionCounter; static gboolean session_changes_before_commit(gpointer data) { SessionCounter *counter = data; return counter->calls++ == 0; } static void assert_empty_after_reopen(const char *database_path) { static const char *const tables[] = { "entites", "preuves", "preuve_entites", "preuve_entite_sources", "person_role_assignments", "identity_ocr_runs", "identity_document_observations", "identity_field_observations" }; Database *database = database_open(database_path); g_assert_nonnull(database); for (guint i = 0; i < G_N_ELEMENTS(tables); i++) g_assert_cmpuint(count(database, tables[i]), ==, 0); database_close(database); } static void test_failure_matrix(void) { static const struct { PersonCreationFailurePoint point; guint occurrence; } cases[] = { {PERSON_CREATION_FAILURE_VALIDATE,0}, {PERSON_CREATION_FAILURE_SESSION_BEFORE_START,0}, {PERSON_CREATION_FAILURE_SOURCE_HASH,0}, {PERSON_CREATION_FAILURE_CREATE_OCR_DIRECTORY,0}, {PERSON_CREATION_FAILURE_COPY_OCR_TEXT,0}, {PERSON_CREATION_FAILURE_HASH_OCR_TEXT,0}, {PERSON_CREATION_FAILURE_COPY_OCR_TSV,0}, {PERSON_CREATION_FAILURE_HASH_OCR_TSV,0}, {PERSON_CREATION_FAILURE_CREATE_PERSON,0}, {PERSON_CREATION_FAILURE_CREATE_ROLE,0}, {PERSON_CREATION_FAILURE_CREATE_ROLE,1}, {PERSON_CREATION_FAILURE_CREATE_ROLE,2}, {PERSON_CREATION_FAILURE_IMPORT_EVIDENCE,0}, {PERSON_CREATION_FAILURE_IMPORT_EVIDENCE,1}, {PERSON_CREATION_FAILURE_IMPORT_EVIDENCE,2}, {PERSON_CREATION_FAILURE_LINK_EVIDENCE,0}, {PERSON_CREATION_FAILURE_LINK_EVIDENCE,1}, {PERSON_CREATION_FAILURE_LINK_EVIDENCE,2}, {PERSON_CREATION_FAILURE_INSERT_OCR_RUN,0}, {PERSON_CREATION_FAILURE_INSERT_OCR_RUN,1}, {PERSON_CREATION_FAILURE_INSERT_OCR_RUN,2}, {PERSON_CREATION_FAILURE_INSERT_DOCUMENT_OBSERVATION,0}, {PERSON_CREATION_FAILURE_INSERT_DOCUMENT_OBSERVATION,1}, {PERSON_CREATION_FAILURE_INSERT_DOCUMENT_OBSERVATION,2}, {PERSON_CREATION_FAILURE_INSERT_FIELD,0}, {PERSON_CREATION_FAILURE_INSERT_FIELD,1}, {PERSON_CREATION_FAILURE_INSERT_FIELD,2}, {PERSON_CREATION_FAILURE_CREATE_SOURCE,0}, {PERSON_CREATION_FAILURE_CREATE_SOURCE,1}, {PERSON_CREATION_FAILURE_CREATE_SOURCE,2}, {PERSON_CREATION_FAILURE_SESSION_BEFORE_COMMIT,0}, {PERSON_CREATION_FAILURE_ARTIFACT_TEXT_CHANGED,0}, {PERSON_CREATION_FAILURE_ARTIFACT_TSV_CHANGED,0}, {PERSON_CREATION_FAILURE_COMMIT,0}, {PERSON_CREATION_FAILURE_COMMIT,0} }; for (guint scenario = 0; scenario < G_N_ELEMENTS(cases); scenario++) { char *root = g_dir_make_tmp("labfy-coordinator-matrix-XXXXXX", NULL); char *database_directory = g_build_filename( root, "00_BaseDeDonnees", NULL); char *database_path = g_build_filename( database_directory, "Enquete.sqlite", NULL); g_assert_cmpint(g_mkdir_with_parents(database_directory,0700),==,0); g_assert_true(database_initialize(database_path, "Enquête SPECIMEN matrice", root)); EvidenceStaging *staging = evidence_staging_new(NULL); PersonEvidenceSelection *selection = person_evidence_selection_new(); GPtrArray *prepared = g_ptr_array_new_with_free_func( (GDestroyNotify) evidence_staging_result_free); GPtrArray *runs = g_ptr_array_new_with_free_func( (GDestroyNotify) identity_ocr_run_free); GPtrArray *roles = g_ptr_array_new_with_free_func( (GDestroyNotify) person_role_assignment_input_free); for (guint i = 0; i < 3; i++) { char *name = g_strdup_printf("SPECIMEN-%u.txt", i); char *source = g_build_filename(root, name, NULL); char *contents = g_strdup_printf("SPECIMEN SOURCE %u", i); g_assert_true(g_file_set_contents(source, contents, -1, NULL)); EvidenceStagingResult *item = evidence_staging_prepare( staging, source, NULL, NULL); g_assert_nonnull(item); g_ptr_array_add(prepared, item); g_assert_true(person_evidence_selection_add_staged(selection, item->source_path, item->staging_path, item->original_name, item->mime_type, "text", item->size_bytes, item->sha256, NULL, item->prepared_at, NULL)); const PersonEvidenceSelectionItem *selected = person_evidence_selection_get(selection, i); IdentityOcrRun *run = identity_ocr_run_new( person_evidence_selection_item_get_identifier(selected), item->sha256, "identity_card", "front", i + 1, "eng", "none"); identity_ocr_run_set_outputs(run, "SPECIMEN", "eng", "params", "NOM: SPECIMEN\nPRÉNOM: TEST\nNUMÉRO: ABC123", "level\tpage_num\tblock_num\tpar_num\tline_num\tword_num\t" "left\ttop\twidth\theight\tconf\ttext\n"); IdentityFieldObservation *field = identity_field_observation_new("surname", "SPECIMEN", 90, NULL, i); identity_field_observation_accept(field); identity_ocr_run_add_field(run, field); g_ptr_array_add(runs, run); PersonRoleAssignmentInput role = { .role_code = i == 0 ? "victim" : i == 1 ? "witness" : "mentioned_person", .provenance_kind = "manual" }; g_ptr_array_add(roles, person_role_assignment_input_copy(&role)); g_free(name); g_free(source); g_free(contents); } PersonEntityInput person = { .designation = "Personne SPECIMEN matrice", .identification_status = "suspected", .confidence = 10, .role_assignments = roles }; PersonCreationCoordinatorOptions options = { .failure_point = cases[scenario].point, .failure_occurrence = cases[scenario].occurrence, .inject_compensation_failure = scenario == G_N_ELEMENTS(cases) - 1 }; char *ocr_parent=g_build_filename(root,"02_Preuves_Traitees", "OCR",NULL); gboolean parent_preexisted=scenario%2==0; char *parent_marker=g_build_filename(ocr_parent,"SPECIMEN.marker",NULL); if(parent_preexisted){ g_assert_cmpint(g_mkdir_with_parents(ocr_parent,0700),==,0); g_assert_true(g_file_set_contents(parent_marker,"SPECIMEN",-1,NULL)); } Database *database = database_open(database_path); GError *error = NULL; PersonCreationCoordinatorResult *result = person_creation_coordinator_execute_with_options(database, root, &person, selection, runs, &options, NULL, &error); g_assert_null(result); g_assert_nonnull(error); if (options.inject_compensation_failure) g_assert_nonnull(strstr(error->message, "Compensation incomplète")); g_clear_error(&error); database_close(database); assert_empty_after_reopen(database_path); guint residual_runs=0; for(guint i=0;ilen;i++){ IdentityOcrRun *run=g_ptr_array_index(runs,i); char *run_directory=g_build_filename(ocr_parent, identity_ocr_run_get_identifier(run),NULL); if(g_file_test(run_directory,G_FILE_TEST_EXISTS)) residual_runs++; if(!options.inject_compensation_failure&& g_file_test(run_directory,G_FILE_TEST_EXISTS)) g_error("Résidu au scénario %u, point %u occurrence %u, " "run %u",scenario,(guint)cases[scenario].point, cases[scenario].occurrence,i); g_free(run_directory); } g_assert_cmpuint(residual_runs,==, options.inject_compensation_failure?1:0); if(parent_preexisted){ g_assert_true(g_file_test(parent_marker,G_FILE_TEST_IS_REGULAR)); }else if(!options.inject_compensation_failure) g_assert_false(g_file_test(ocr_parent,G_FILE_TEST_EXISTS)); for (guint i = 0; i < prepared->len; i++) { EvidenceStagingResult *item = g_ptr_array_index(prepared, i); char *contents = NULL; g_assert_true(g_file_get_contents(item->source_path, &contents, NULL, NULL)); g_assert_true(g_str_has_prefix(contents, "SPECIMEN SOURCE")); g_free(contents); } g_ptr_array_unref(roles); g_ptr_array_unref(runs); person_evidence_selection_free(selection); g_ptr_array_unref(prepared); evidence_staging_free(staging); g_free(ocr_parent);g_free(parent_marker); g_free(database_directory); g_free(database_path); remove_tree(root); g_free(root); } } static void test_real_session_change_before_commit(void) { char *root = g_dir_make_tmp("labfy-session-before-commit-XXXXXX", NULL); char *database_directory = g_build_filename(root,"00_BaseDeDonnees",NULL); char *database_path = g_build_filename(database_directory, "Enquete.sqlite", NULL); char *source = g_build_filename(root, "SPECIMEN.txt", NULL); g_assert_cmpint(g_mkdir_with_parents(database_directory,0700),==,0); g_assert_true(database_initialize(database_path,"SPECIMEN",root)); g_assert_true(g_file_set_contents(source,"SPECIMEN",-1,NULL)); EvidenceStaging *staging = evidence_staging_new(NULL); EvidenceStagingResult *prepared = evidence_staging_prepare(staging,source,NULL,NULL); PersonEvidenceSelection *selection = person_evidence_selection_new(); g_assert_true(person_evidence_selection_add_staged(selection, prepared->source_path,prepared->staging_path,prepared->original_name, prepared->mime_type,"text",prepared->size_bytes,prepared->sha256, NULL,prepared->prepared_at,NULL)); PersonEntityInput person={.designation="SPECIMEN", .identification_status="unknown"}; SessionCounter counter={0}; PersonCreationCoordinatorOptions options={ .session_check=session_changes_before_commit, .session_check_data=&counter}; Database *database=database_open(database_path); GError *error=NULL; g_assert_null(person_creation_coordinator_execute_with_options(database, root,&person,selection,NULL,&options,NULL,&error)); g_assert_nonnull(error);g_clear_error(&error);database_close(database); assert_empty_after_reopen(database_path); person_evidence_selection_free(selection); evidence_staging_result_free(prepared);evidence_staging_free(staging); g_free(database_directory);g_free(database_path);g_free(source); remove_tree(root);g_free(root); } static void test_existing_person_session_change_compensates(void) { char *root=g_dir_make_tmp("labfy-existing-session-XXXXXX",NULL); char *database_directory=g_build_filename(root,"00_BaseDeDonnees",NULL); char *database_path=g_build_filename(database_directory,"Enquete.sqlite",NULL); char *source=g_build_filename(root,"SPECIMEN-IDENTITE.txt",NULL); g_assert_cmpint(g_mkdir_with_parents(database_directory,0700),==,0); g_assert_true(database_initialize(database_path,"SPECIMEN",root)); g_assert_true(g_file_set_contents(source,"SPECIMEN IDENTITE",-1,NULL)); Database *database=database_open(database_path);GError *error=NULL; EntityDao *entity_dao=entity_dao_new(database,&error); EntityRecord *person=entity_record_new( "22222222-2222-4222-8222-222222222222","person", "PERSONNE SPECIMEN","PERSONNE SPECIMEN",NULL,0, "2026-01-01T00:00:00Z","2026-01-01T00:00:00Z", ENTITY_STATUS_ACTIVE,&error); g_assert_true(entity_dao_insert(entity_dao,person,&error)); entity_record_free(person);entity_dao_free(entity_dao); EvidenceStaging *staging=evidence_staging_new(&error); EvidenceStagingResult *prepared=evidence_staging_prepare( staging,source,NULL,&error); PersonEvidenceSelection *selection=person_evidence_selection_new(); g_assert_true(person_evidence_selection_add_staged(selection, prepared->source_path,prepared->staging_path,prepared->original_name, prepared->mime_type,"document",prepared->size_bytes,prepared->sha256, "SPECIMEN",prepared->prepared_at,&error)); const PersonEvidenceSelectionItem *item= person_evidence_selection_get(selection,0); IdentityOcrRun *run=identity_ocr_run_new( person_evidence_selection_item_get_identifier(item),prepared->sha256, "identity_card","front",1,"fra","none"); identity_ocr_run_set_outputs(run,"SPECIMEN","fra","SPECIMEN", "NOM SPECIMEN","TSV SPECIMEN"); IdentityFieldObservation *field=identity_field_observation_new( "surname","SPECIMEN",99,NULL,0); identity_field_observation_accept(field); identity_ocr_run_add_field(run,field); GPtrArray *runs=g_ptr_array_new_with_free_func( (GDestroyNotify)identity_ocr_run_free); g_ptr_array_add(runs,run); SessionCounter counter={0}; PersonCreationCoordinatorOptions options={ .session_check=session_changes_before_commit, .session_check_data=&counter}; g_assert_null(person_creation_coordinator_attach_to_existing_person( database,root,"22222222-2222-4222-8222-222222222222", selection,runs,NULL,&options,NULL,&error)); g_assert_nonnull(error);g_clear_error(&error); g_assert_cmpuint(count(database,"entites"),==,1); g_assert_cmpuint(count(database,"preuves"),==,0); g_assert_cmpuint(count(database,"preuve_entites"),==,0); g_assert_cmpuint(count(database,"identity_ocr_runs"),==,0); char *ocr_root=g_build_filename(root,"02_Preuves_Traitees","OCR",NULL); g_assert_false(g_file_test(ocr_root,G_FILE_TEST_EXISTS)); g_free(ocr_root);g_ptr_array_unref(runs); person_evidence_selection_free(selection); evidence_staging_result_free(prepared);evidence_staging_free(staging); database_close(database);g_free(source);g_free(database_directory); g_free(database_path);remove_tree(root);g_free(root); } static void test_success_and_rollback(void) { char *root = g_dir_make_tmp("labfy-coordinator-XXXXXX", NULL); char *database_directory = g_build_filename(root, "00_BaseDeDonnees", NULL); char *database_path = g_build_filename(database_directory, "Enquete.sqlite", NULL); char *source_one = g_build_filename(root, "SPECIMEN-one.txt", NULL); char *source_two = g_build_filename(root, "SPECIMEN-two.txt", NULL); EvidenceStaging *staging = NULL; EvidenceStagingResult *prepared_one = NULL, *prepared_two = NULL; PersonEvidenceSelection *selection = NULL; PersonCreationCoordinatorResult *result = NULL; Database *database; PersonEntityInput person = { .designation = "Personne SPECIMEN", .identification_status = "suspected", .confidence = 20 }; GError *error = NULL; g_assert_cmpint(g_mkdir_with_parents(database_directory, 0700), ==, 0); g_assert_true(database_initialize( database_path, "Enquête SPECIMEN", root)); g_assert_true(g_file_set_contents( source_one, "SPECIMEN-ONE", -1, NULL)); g_assert_true(g_file_set_contents( source_two, "SPECIMEN-TWO", -1, NULL)); staging = evidence_staging_new(&error); prepared_one = evidence_staging_prepare( staging, source_one, NULL, &error); prepared_two = evidence_staging_prepare( staging, source_two, NULL, &error); g_assert_no_error(error); selection = person_evidence_selection_new(); g_assert_true(person_evidence_selection_add_staged(selection, prepared_one->source_path, prepared_one->staging_path, prepared_one->original_name, prepared_one->mime_type, "text", prepared_one->size_bytes, prepared_one->sha256, NULL, prepared_one->prepared_at, &error)); GPtrArray *coordinator_runs = g_ptr_array_new_with_free_func( (GDestroyNotify) identity_ocr_run_free); IdentityOcrRun *coordinator_run = identity_ocr_run_new( person_evidence_selection_item_get_identifier( person_evidence_selection_get(selection, 0)), prepared_one->sha256, "identity_card", "front", 1, "eng", "none"); identity_ocr_run_set_outputs(coordinator_run, "tesseract SPECIMEN", "eng", "paramètres SPECIMEN", "NOM SPECIMEN", "TSV SPECIMEN"); IdentityFieldObservation *coordinator_field = identity_field_observation_new("surname", "SPECIMEN", 90, NULL, 0); identity_field_observation_accept(coordinator_field); identity_ocr_run_add_field(coordinator_run, coordinator_field); IdentityFieldObservation *manual_field = identity_field_observation_new_manual( "nationality", "FRANÇAISE", 1); g_assert_nonnull(manual_field); g_assert_true(identity_field_observation_modify(manual_field, "FRANÇAISE", "saisie manuelle contrôlée")); identity_ocr_run_add_field(coordinator_run, manual_field); identity_ocr_run_set_factual_notes(coordinator_run, "Document tronqué ; bord gauche coupé. " "Fragment visible : « publique française »."); g_ptr_array_add(coordinator_runs, coordinator_run); PersonEvidenceSelection *task_selection = person_evidence_selection_copy(selection); database = database_open(database_path); result = person_creation_coordinator_execute(database, root, &person, task_selection, coordinator_runs, NULL, &error); person_evidence_selection_free(task_selection); g_assert_no_error(error); g_assert_nonnull(result); g_assert_cmpuint(result->evidence_identifiers->len, ==, 1); g_assert_cmpuint(count(database, "entites"), ==, 1); g_assert_cmpuint(count(database, "preuves"), ==, 1); g_assert_cmpuint(count(database, "preuve_entites"), ==, 1); g_assert_cmpuint(count(database, "preuve_entite_sources"), ==, 1); g_assert_cmpuint(count(database, "identity_ocr_runs"), ==, 1); g_assert_cmpuint(count(database, "identity_document_observations"), ==, 1); g_assert_cmpuint(count(database, "identity_field_observations"), ==, 2); char *ocr_root = g_build_filename(root, "02_Preuves_Traitees", "OCR", identity_ocr_run_get_identifier(coordinator_run), NULL); char *ocr_text = g_build_filename(ocr_root, "ocr.txt", NULL); char *ocr_tsv = g_build_filename(ocr_root, "ocr.tsv", NULL); g_assert_true(g_file_test(ocr_text, G_FILE_TEST_IS_REGULAR)); g_assert_true(g_file_test(ocr_tsv, G_FILE_TEST_IS_REGULAR)); IdentityOcrRun *ocr = identity_ocr_run_new( person_evidence_selection_item_get_identifier( person_evidence_selection_get(selection, 0)), prepared_one->sha256, "identity_card", "front", 1, "eng", "none"); identity_ocr_run_set_outputs(ocr, "tesseract 5 SPECIMEN", "eng", "paramètres SPECIMEN", "SURNAME SPECIMEN", "TSV SPECIMEN"); IdentitySourceBox box = {.page=1,.x=1,.y=2,.width=30,.height=10, .image_width=80,.image_height=40,.available=TRUE}; IdentityFieldObservation *accepted = identity_field_observation_new("surname", "SPECIMEN", 91, &box, 0); IdentityFieldObservation *modified = identity_field_observation_new("given_names", "ALICE", 80, &box, 1); IdentityFieldObservation *rejected = identity_field_observation_new("birth_place", "TEST", 70, NULL, 2); IdentityFieldObservation *mrz = identity_field_observation_new("document_number", "MRZSPECIMEN", 99, &box, 3); IdentityFieldObservation *conflict = identity_field_observation_new("birth_date", "01/01/1990", 50, NULL, 4); identity_field_observation_accept(accepted); identity_field_observation_modify(modified, "ALICE SPECIMEN", "revue"); identity_field_observation_reject(rejected); identity_field_observation_accept(mrz); g_assert_true(identity_field_observation_set_origin(mrz, "mrz")); identity_field_observation_mark_conflict(conflict); identity_ocr_run_add_field(ocr, accepted); identity_ocr_run_add_field(ocr, modified); identity_ocr_run_add_field(ocr, rejected); identity_ocr_run_add_field(ocr, mrz); identity_ocr_run_add_field(ocr, conflict); IdentityOcrDao *ocr_dao = identity_ocr_dao_new(database); const char *artifact_sha = "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"; g_assert_true(database_transaction_begin(database)); g_assert_true(identity_ocr_dao_insert(ocr_dao, result->person_identifier, g_ptr_array_index(result->evidence_identifiers, 0), ocr, "02_Preuves_Traitees/OCR/run/ocr.txt", artifact_sha, "02_Preuves_Traitees/OCR/run/ocr.tsv", artifact_sha, "2026-07-28T12:00:00Z", &error)); g_assert_true(database_transaction_rollback(database)); g_assert_cmpuint(count(database, "identity_ocr_runs"), ==, 1); g_assert_true(database_transaction_begin(database)); g_assert_true(identity_ocr_dao_insert(ocr_dao, result->person_identifier, g_ptr_array_index(result->evidence_identifiers, 0), ocr, "02_Preuves_Traitees/OCR/run/ocr.txt", artifact_sha, "02_Preuves_Traitees/OCR/run/ocr.tsv", artifact_sha, "2026-07-28T12:00:00Z", &error)); g_assert_true(database_transaction_commit(database)); g_assert_cmpuint(count(database, "identity_ocr_runs"), ==, 2); g_assert_cmpuint(count(database, "identity_document_observations"), ==, 2); g_assert_cmpuint(count(database, "identity_field_observations"), ==, 7); GPtrArray *read_runs = identity_ocr_dao_list_runs_by_evidence(ocr_dao, g_ptr_array_index(result->evidence_identifiers, 0), &error); g_assert_no_error(error); g_assert_cmpuint(read_runs->len, ==, 2); IdentityOcrRunRecord *read_run = g_ptr_array_index(read_runs, 1); g_assert_cmpstr(read_run->engine, ==, "tesseract"); g_assert_cmpstr(read_run->engine_version, ==, "tesseract 5 SPECIMEN"); g_assert_cmpstr(read_run->requested_languages, ==, "eng"); g_assert_cmpstr(read_run->preprocessing_profile, ==, "none"); g_assert_null(read_run->error_message); IdentityOcrRunRecord *found_run = identity_ocr_dao_find_run(ocr_dao, read_run->id, &error); g_assert_no_error(error); g_assert_cmpstr(found_run->id, ==, read_run->id); identity_ocr_run_record_free(found_run); GPtrArray *documents = identity_ocr_dao_list_documents_by_person(ocr_dao, result->person_identifier, &error); g_assert_no_error(error); g_assert_cmpuint(documents->len, ==, 2); IdentityDocumentObservationRecord *document = g_ptr_array_index(documents, 1); g_assert_cmpstr(document->person_id, ==, result->person_identifier); g_assert_cmpstr(document->document_side, ==, "front"); g_assert_null(document->issuing_country_declared); GPtrArray *read_fields = identity_ocr_dao_list_fields_by_document(ocr_dao, document->id, &error); g_assert_no_error(error); g_assert_cmpuint(read_fields->len, ==, 5); IdentityFieldObservationRecord *field0=g_ptr_array_index(read_fields,0); IdentityFieldObservationRecord *field1=g_ptr_array_index(read_fields,1); IdentityFieldObservationRecord *field2=g_ptr_array_index(read_fields,2); IdentityFieldObservationRecord *field3=g_ptr_array_index(read_fields,3); IdentityFieldObservationRecord *field4=g_ptr_array_index(read_fields,4); g_assert_cmpstr(field0->origin, ==, "ocr"); g_assert_cmpstr(field1->origin, ==, "manual_override"); g_assert_cmpstr(field1->raw_value, ==, "ALICE"); g_assert_cmpstr(field1->corrected_value, ==, "ALICE SPECIMEN"); g_assert_cmpstr(field2->review_status, ==, "rejected"); g_assert_cmpstr(field3->origin, ==, "mrz"); g_assert_cmpstr(field4->review_status, ==, "conflict"); g_assert_true(field0->has_source_box); g_assert_null(identity_ocr_dao_find_field(ocr_dao, "00000000-0000-4000-8000-000000000000", &error)); g_assert_no_error(error); g_ptr_array_unref(read_fields);g_ptr_array_unref(documents); g_ptr_array_unref(read_runs); identity_ocr_dao_free(ocr_dao); identity_ocr_run_free(ocr); char *persisted_person = g_strdup(result->person_identifier); char *persisted_evidence = g_strdup( g_ptr_array_index(result->evidence_identifiers, 0)); person_creation_coordinator_result_free(result); person_evidence_selection_free(selection); selection = person_evidence_selection_new(); g_assert_true(person_evidence_selection_add_staged(selection, prepared_two->source_path, prepared_two->staging_path, prepared_two->original_name, prepared_two->mime_type, "text", prepared_two->size_bytes, prepared_two->sha256, NULL, prepared_two->prepared_at, &error)); GPtrArray *failed_runs = g_ptr_array_new_with_free_func( (GDestroyNotify) identity_ocr_run_free); IdentityOcrRun *failed_run = identity_ocr_run_new( person_evidence_selection_item_get_identifier( person_evidence_selection_get(selection, 0)), prepared_two->sha256, "identity_card", "back", 1, "eng", "none"); identity_ocr_run_set_outputs(failed_run, "SPECIMEN", "eng", "params", "SPECIMEN", "TSV"); g_ptr_array_add(failed_runs, failed_run); char *failed_directory = g_build_filename(root, "02_Preuves_Traitees", "OCR", identity_ocr_run_get_identifier(failed_run), NULL); execute_sql(database, "CREATE TEMP TRIGGER fail_identity_ocr " "BEFORE INSERT ON identity_ocr_runs BEGIN SELECT RAISE(ABORT," "'SPECIMEN injected failure'); END;"); result = person_creation_coordinator_execute(database, root, &person, selection, failed_runs, NULL, &error); g_assert_null(result); g_assert_nonnull(error); g_clear_error(&error); g_assert_false(g_file_test(failed_directory, G_FILE_TEST_EXISTS)); g_assert_cmpuint(count(database, "entites"), ==, 1); execute_sql(database, "DROP TRIGGER fail_identity_ocr;"); g_free(failed_directory); g_ptr_array_unref(failed_runs); person_evidence_selection_free(selection); selection = person_evidence_selection_new(); g_assert_true(person_evidence_selection_add_staged(selection, prepared_two->source_path, prepared_two->staging_path, prepared_two->original_name, prepared_two->mime_type, "missing_type", prepared_two->size_bytes, prepared_two->sha256, NULL, prepared_two->prepared_at, &error)); result = person_creation_coordinator_execute(database, root, &person, selection, NULL, NULL, &error); g_assert_null(result); g_assert_nonnull(error); g_clear_error(&error); g_assert_cmpuint(count(database, "entites"), ==, 1); g_assert_cmpuint(count(database, "preuves"), ==, 1); database_close(database); database = database_open(database_path); g_assert_nonnull(database); ocr_dao = identity_ocr_dao_new(database); read_runs = identity_ocr_dao_list_runs_by_evidence(ocr_dao, persisted_evidence, &error); g_assert_no_error(error); g_assert_cmpuint(read_runs->len, ==, 2); documents = identity_ocr_dao_list_documents_by_person(ocr_dao, persisted_person, &error); g_assert_no_error(error); g_assert_cmpuint(documents->len, ==, 2); g_ptr_array_unref(read_runs); g_ptr_array_unref(documents); identity_ocr_dao_free(ocr_dao); database_close(database); g_free(persisted_person); g_free(persisted_evidence); g_ptr_array_unref(coordinator_runs); person_evidence_selection_free(selection); evidence_staging_result_free(prepared_one); evidence_staging_result_free(prepared_two); evidence_staging_free(staging); g_free(database_directory); g_free(database_path); g_free(source_one); g_free(source_two); g_free(ocr_root); g_free(ocr_text); g_free(ocr_tsv); remove_tree(root); g_free(root); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/person-coordinator/success-rollback", test_success_and_rollback); g_test_add_func("/person-coordinator/failure-matrix", test_failure_matrix); g_test_add_func("/person-coordinator/session-before-commit", test_real_session_change_before_commit); g_test_add_func("/person-coordinator/existing-session-compensation", test_existing_person_session_change_compensates); return g_test_run(); }