diff --git a/tui/src/web_sessions.c b/tui/src/web_sessions.c index 1896363..83c360b 100644 --- a/tui/src/web_sessions.c +++ b/tui/src/web_sessions.c @@ -923,18 +923,14 @@ TrainlogStatus trainlog_web_sessions_save_json(TrainlogDatabase *database, if ((preparation_id == NULL || preparation_id[0] == '\0') && source_proposal != NULL) { if (sqlite3_prepare_v2( database->connection, - "UPDATE session_preparations SET source_proposal_id=?1,source_payload_sha256=?2 " - "WHERE preparation_id=?3 AND EXISTS(SELECT 1 FROM ai_session_draft_imports i WHERE " - "i.draft_id=?1 AND i.payload_sha256=?2)", + "SELECT 1 FROM ai_session_draft_imports WHERE draft_id=?1 AND payload_sha256=?2", -1, &statement, NULL) != SQLITE_OK || sqlite3_bind_text(statement, 1, source_proposal, -1, SQLITE_TRANSIENT) != SQLITE_OK || sqlite3_bind_text(statement, 2, source_fingerprint, -1, SQLITE_TRANSIENT) != SQLITE_OK || - sqlite3_bind_text(statement, 3, actual_preparation, -1, SQLITE_TRANSIENT) != - SQLITE_OK || - sqlite3_step(statement) != SQLITE_DONE || sqlite3_changes(database->connection) != 1) { + sqlite3_step(statement) != SQLITE_ROW) { status = TRAINLOG_STATUS_CONFLICT; goto rollback; } @@ -964,7 +960,7 @@ TrainlogStatus trainlog_web_sessions_save_json(TrainlogDatabase *database, statement = NULL; } else if (sqlite3_prepare_v2( database->connection, - "INSERT INTO session_preparations VALUES(?1,?2,?3,?3,?4,'local',NULL,NULL)", + "INSERT INTO session_preparations VALUES(?1,?2,?3,?3,?4,'local',?5,?6)", -1, &statement, NULL) != SQLITE_OK || @@ -973,6 +969,14 @@ TrainlogStatus trainlog_web_sessions_save_json(TrainlogDatabase *database, sqlite3_bind_text(statement, 2, revision, -1, SQLITE_TRANSIENT) != SQLITE_OK || sqlite3_bind_text(statement, 3, now, -1, SQLITE_TRANSIENT) != SQLITE_OK || sqlite3_bind_text(statement, 4, editing_state, -1, SQLITE_TRANSIENT) != SQLITE_OK || + (source_proposal == NULL + ? sqlite3_bind_null(statement, 5) + : sqlite3_bind_text(statement, 5, source_proposal, -1, SQLITE_TRANSIENT)) != + SQLITE_OK || + (source_fingerprint == NULL + ? sqlite3_bind_null(statement, 6) + : sqlite3_bind_text(statement, 6, source_fingerprint, -1, SQLITE_TRANSIENT)) != + SQLITE_OK || sqlite3_step(statement) != SQLITE_DONE) { status = TRAINLOG_STATUS_DATABASE_ERROR; goto rollback; diff --git a/tui/tests/test_web_sessions.c b/tui/tests/test_web_sessions.c index f6efa7c..a28c032 100644 --- a/tui/tests/test_web_sessions.c +++ b/tui/tests/test_web_sessions.c @@ -44,6 +44,13 @@ static bool creation_replay_revision_and_pagination(void) { "{\"title\":\"Réordonnée\",\"session_type\":\"training\"," "\"planned_for\":null,\"notes\":null,\"editing_state\":\"draft\"," "\"occurrences\":[]}"; + static const char DERIVED_BODY[] = + "{\"title\":\"Derived\",\"session_type\":\"training\"," + "\"planned_for\":null,\"notes\":null,\"editing_state\":\"draft\"," + "\"occurrences\":[],\"source_proposal_id\":" + "\"aid_11111111-1111-4111-8111-111111111111\"," + "\"source_payload_sha256\":" + "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}"; TrainlogDatabase *database = NULL; TrainlogWebSessionsPageQuery query = {TRAINLOG_WEB_SESSION_PREPARATIONS, 0U, 1U, "échappée"}; char *created = NULL; @@ -70,6 +77,17 @@ static bool creation_replay_revision_and_pagination(void) { TRAINLOG_TRACKING_REPS, TRAINLOG_RECORDING_SETS, 0U) == TRAINLOG_STATUS_OK); + CHECK(sqlite3_exec(database->connection, + "INSERT INTO ai_session_drafts(draft_id,created_at,session_type,title) " + "VALUES('aid_11111111-1111-4111-8111-111111111111','2026-09-18T00:00:00Z'," + "'training','Source');" + "INSERT INTO ai_session_draft_imports VALUES(" + "last_insert_rowid(),'aid_11111111-1111-4111-8111-111111111111'," + "'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'," + "'2026-09-18T00:00:00Z');", + NULL, + NULL, + NULL) == SQLITE_OK); CHECK(trainlog_web_sessions_save_json(database, NULL, "", @@ -122,6 +140,17 @@ static bool creation_replay_revision_and_pagination(void) { &updated_size) == TRAINLOG_STATUS_OK); CHECK(updated_size > 0U); CHECK(scalar(database, "SELECT COUNT(*) FROM session_preparation_revisions") == 2); + CHECK(trainlog_web_sessions_save_json(database, + NULL, + "", + "request-derived-1", + DERIVED_BODY, + strlen(DERIVED_BODY), + &detail, + &detail_size) == TRAINLOG_STATUS_OK); + CHECK(scalar(database, + "SELECT COUNT(*) FROM session_preparations WHERE source_proposal_id=" + "'aid_11111111-1111-4111-8111-111111111111'") == 1); free(detail); detail = NULL; detail_size = 0U; @@ -133,7 +162,7 @@ static bool creation_replay_revision_and_pagination(void) { strlen(UPDATE_BODY), &detail, &detail_size) == TRAINLOG_STATUS_CONFLICT); - CHECK(scalar(database, "SELECT COUNT(*) FROM session_preparation_revisions") == 2); + CHECK(scalar(database, "SELECT COUNT(*) FROM session_preparation_revisions") == 3); free(created); free(replayed); free(detail);