Add functional and unit tests for DB export/import
- 2 functional tests: export_sqlite_to_flatfile, import_flatfile_to_sqlite with XEP-0203 delay timestamps for deterministic verification - 27 unit tests for database_export covering edge cases - Merge test/db-functional-tests: 12 DB persistence tests (test_history) - Group 2 rebalanced: 25 tests (23 base + 2 SQLite-only export/import) - #ifdef HAVE_SQLITE guards for export/import tests - prof_stop() helper in proftest.c - Makefile.am: source ordering cleanup, new test files added
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* Tests are organized into groups for better maintainability and parallel execution:
|
||||
* Group 1: Connect, Ping, Rooms, Software, Last Activity, Autoping
|
||||
* Group 2: Message, Receipts, Roster, DB History
|
||||
* Group 2: Message, Receipts, Roster, DB History (+2 with SQLite)
|
||||
* Group 3: Chat Session, Presence, Disconnect, Disco
|
||||
* Group 4: MUC, Carbons
|
||||
*
|
||||
@@ -56,6 +56,9 @@
|
||||
#include "test_autoping.h"
|
||||
#include "test_disco.h"
|
||||
#include "test_history.h"
|
||||
#ifdef HAVE_SQLITE
|
||||
#include "test_export_import.h"
|
||||
#endif
|
||||
|
||||
/* Macro to wrap each test with setup/teardown functions */
|
||||
#define PROF_FUNC_TEST(test) cmocka_unit_test_setup_teardown(test, init_prof_test, close_prof_test)
|
||||
@@ -128,9 +131,15 @@ main(int argc, char* argv[])
|
||||
/* ============================================================
|
||||
* GROUP 2: Message, Receipts, Roster, DB History
|
||||
* Core messaging and contact management
|
||||
* (23 tests)
|
||||
* (25 tests with SQLite, 23 without)
|
||||
* ============================================================ */
|
||||
const struct CMUnitTest group2_tests[] = {
|
||||
#ifdef HAVE_SQLITE
|
||||
/* Export/Import — cross-backend migration (SQLite ↔ flat-file) */
|
||||
PROF_FUNC_TEST(export_sqlite_to_flatfile),
|
||||
PROF_FUNC_TEST(import_flatfile_to_sqlite),
|
||||
#endif
|
||||
|
||||
/* Basic message send/receive */
|
||||
PROF_FUNC_TEST(message_send),
|
||||
PROF_FUNC_TEST(message_receive_console),
|
||||
@@ -159,9 +168,7 @@ main(int argc, char* argv[])
|
||||
PROF_FUNC_TEST(message_db_history_long_message),
|
||||
PROF_FUNC_TEST(message_db_history_newline),
|
||||
PROF_FUNC_TEST(message_db_history_service_chars),
|
||||
#ifdef HAVE_HISTORY_VERIFY
|
||||
PROF_FUNC_TEST(message_db_history_verify),
|
||||
#endif
|
||||
PROF_FUNC_TEST(message_db_history_lmc),
|
||||
};
|
||||
|
||||
|
||||
@@ -436,6 +436,24 @@ close_prof_test(void **state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
prof_stop(void)
|
||||
{
|
||||
if (fd > 0 && child_pid > 0) {
|
||||
prof_input("/quit");
|
||||
sleep(1);
|
||||
waitpid(child_pid, NULL, 0);
|
||||
close(fd);
|
||||
fd = 0;
|
||||
child_pid = 0;
|
||||
}
|
||||
/* Reset output buffer for clean restart */
|
||||
output_len = 0;
|
||||
output_buffer[0] = '\0';
|
||||
/* Brief delay to let stabber release old connection state */
|
||||
usleep(200000); /* 200ms */
|
||||
}
|
||||
|
||||
void
|
||||
prof_input(const char *input)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ int init_prof_test(void **state);
|
||||
int close_prof_test(void **state);
|
||||
|
||||
void prof_start(void);
|
||||
void prof_stop(void);
|
||||
void prof_connect(void);
|
||||
void prof_connect_with_roster(const char *roster);
|
||||
void prof_input(const char *input);
|
||||
|
||||
114
tests/functionaltests/test_export_import.c
Normal file
114
tests/functionaltests/test_export_import.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stabber.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
/*
|
||||
* Test: SQLite -> flat-file export preserves messages.
|
||||
*
|
||||
* Uses <delay> stamped messages to verify round-trip through export.
|
||||
* Timestamp preservation is verified by unit tests (test_database_export.c);
|
||||
* the history-load path returns NULL GDateTime for timestamps, so
|
||||
* functional tests verify content survival only.
|
||||
*
|
||||
* Flow:
|
||||
* 1. Connect (SQLite backend)
|
||||
* 2. Receive message from buddy1 with delay stamp
|
||||
* 3. Send outgoing message
|
||||
* 4. /close, /history export, /history switch flatfile
|
||||
* 5. Re-open chat -- history loaded from flat-file
|
||||
* 6. Verify message content survives cross-backend migration
|
||||
*/
|
||||
void
|
||||
export_sqlite_to_flatfile(void** state)
|
||||
{
|
||||
/* Phase 1: create message history in SQLite */
|
||||
prof_connect();
|
||||
|
||||
/* Send outgoing message (timestamp = now) */
|
||||
prof_input("/msg buddy1@localhost Hello from SQLite export test");
|
||||
assert_true(prof_output_regex("me: .+Hello from SQLite export test"));
|
||||
|
||||
/* Receive incoming message with a fixed delay timestamp (10:30 UTC) */
|
||||
stbbr_send(
|
||||
"<message id='exp1' to='stabber@localhost' from='buddy1@localhost/res' type='chat'>"
|
||||
"<body>SQLite reply at 1030</body>"
|
||||
"<delay xmlns='urn:xmpp:delay' stamp='2025-06-15T10:30:00Z'/>"
|
||||
"</message>");
|
||||
assert_true(prof_output_regex("Buddy1/res: .+SQLite reply at 1030"));
|
||||
|
||||
/* Close chat window, return to console */
|
||||
prof_input("/close");
|
||||
|
||||
/* Export SQLite history to flat-file format */
|
||||
prof_input("/history export buddy1@localhost");
|
||||
assert_true(prof_output_regex("Export complete: [0-9]+ message\\(s\\) exported\\."));
|
||||
|
||||
/* Phase 2: switch to flat-file backend and verify content survived */
|
||||
prof_input("/history switch flatfile");
|
||||
assert_true(prof_output_regex("Database backend switched to 'flatfile'\\."));
|
||||
|
||||
/* Open chat window -- history loads from flat-file backend */
|
||||
prof_input("/msg buddy1@localhost");
|
||||
|
||||
/* Verify messages survived cross-backend migration */
|
||||
assert_true(prof_output_regex("SQLite reply at 1030"));
|
||||
assert_true(prof_output_regex("Hello from SQLite export test"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Test: flat-file -> SQLite import preserves messages.
|
||||
*
|
||||
* Flow:
|
||||
* 1. Connect, switch to flat-file backend
|
||||
* 2. Receive message with delay stamp -- stored in flat-file
|
||||
* 3. Send outgoing message
|
||||
* 4. /close, switch to SQLite, /history import
|
||||
* 5. Open chat -- history loaded from SQLite
|
||||
* 6. Verify message content survives cross-backend migration
|
||||
*/
|
||||
void
|
||||
import_flatfile_to_sqlite(void** state)
|
||||
{
|
||||
/* Phase 1: connect and switch to flat-file backend */
|
||||
prof_connect();
|
||||
|
||||
prof_input("/history switch flatfile");
|
||||
assert_true(prof_output_regex("Database backend switched to 'flatfile'\\."));
|
||||
|
||||
/* Send outgoing message */
|
||||
prof_input("/msg buddy1@localhost Hello from flatfile import test");
|
||||
assert_true(prof_output_regex("me: .+Hello from flatfile import test"));
|
||||
|
||||
/* Receive incoming message with a fixed delay timestamp (14:45 UTC) */
|
||||
stbbr_send(
|
||||
"<message id='imp1' to='stabber@localhost' from='buddy1@localhost/res' type='chat'>"
|
||||
"<body>Flatfile reply at 1445</body>"
|
||||
"<delay xmlns='urn:xmpp:delay' stamp='2025-06-15T14:45:00Z'/>"
|
||||
"</message>");
|
||||
assert_true(prof_output_regex("Buddy1/res: .+Flatfile reply at 1445"));
|
||||
|
||||
/* Close chat window */
|
||||
prof_input("/close");
|
||||
|
||||
/* Phase 2: switch back to SQLite and import */
|
||||
prof_input("/history switch sqlite");
|
||||
assert_true(prof_output_regex("Database backend switched to 'sqlite'\\."));
|
||||
|
||||
/* Import flat-file history into SQLite */
|
||||
prof_input("/history import buddy1@localhost");
|
||||
assert_true(prof_output_regex("Import complete: [0-9]+ message\\(s\\) imported\\."));
|
||||
|
||||
/* Open chat window -- history loads from SQLite with imported data */
|
||||
prof_input("/msg buddy1@localhost");
|
||||
|
||||
/* Verify messages survived cross-backend migration */
|
||||
assert_true(prof_output_regex("Flatfile reply at 1445"));
|
||||
assert_true(prof_output_regex("Hello from flatfile import test"));
|
||||
}
|
||||
2
tests/functionaltests/test_export_import.h
Normal file
2
tests/functionaltests/test_export_import.h
Normal file
@@ -0,0 +1,2 @@
|
||||
void export_sqlite_to_flatfile(void** state);
|
||||
void import_flatfile_to_sqlite(void** state);
|
||||
@@ -385,7 +385,6 @@ message_db_history_service_chars(void **state)
|
||||
assert_true(prof_output_exact("svc: a\\b c|d e%f {g} [h] i=j"));
|
||||
}
|
||||
|
||||
#ifdef HAVE_HISTORY_VERIFY
|
||||
/*
|
||||
* Test: /history verify reports no issues after normal message writes.
|
||||
*/
|
||||
@@ -416,7 +415,6 @@ message_db_history_verify(void **state)
|
||||
prof_input("/history verify");
|
||||
assert_true(prof_output_exact("Verification complete: no issues found."));
|
||||
}
|
||||
#endif /* HAVE_HISTORY_VERIFY */
|
||||
|
||||
/*
|
||||
* Test: LMC (Last Message Correction, XEP-0308) — incoming correction
|
||||
|
||||
@@ -8,7 +8,5 @@ void message_db_history_empty(void **state);
|
||||
void message_db_history_long_message(void **state);
|
||||
void message_db_history_newline(void **state);
|
||||
void message_db_history_service_chars(void **state);
|
||||
#ifdef HAVE_HISTORY_VERIFY
|
||||
void message_db_history_verify(void **state);
|
||||
#endif
|
||||
void message_db_history_lmc(void **state);
|
||||
|
||||
Reference in New Issue
Block a user