Files
codebase-memory-mcp/tests/test_store_checkpoint.c
T
Martin Vogel 7bf3aa86a0 test: use portable temp dir/paths so Windows unit tests pass
Four tests hardcoded POSIX /tmp/ paths that don't exist on Windows: test_path_alias used raw mkdtemp("/tmp/...") and the store pragma/checkpoint tests built /tmp/*.db directly. Switch to cbm_mkdtemp (translates /tmp/ -> %TEMP% on Windows) and cbm_tmpdir() (matching test_store_bulk). build/smoke/soak-windows already pass; this makes the Windows unit-test job green too.
2026-05-29 03:37:56 +02:00

80 lines
2.7 KiB
C

/*
* test_store_checkpoint.c — Tests for WAL checkpoint behavior.
*
* Verifies that cbm_store_checkpoint() does not truncate the on-disk
* WAL file. SQLITE_CHECKPOINT_TRUNCATE shrinks the WAL via ftruncate(fd, 0)
* on success; on macOS this can raise SIGBUS in a sibling process that
* has the DB mmap'd through SQLite when it next faults a page in the
* now-shorter region. SQLITE_CHECKPOINT_PASSIVE marks frames as
* checkpointed in the WAL header without changing the file size — disk
* space is reclaimed on the next write cycle, not on every checkpoint.
*/
#include "../src/foundation/compat.h"
#include "test_framework.h"
#include "test_helpers.h"
#include <store/store.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
TEST(checkpoint_does_not_truncate_wal) {
enum { N_ROWS = 100, PATH_BUF = 256, PATH_BUF_EXT = 300 };
char db_path[PATH_BUF];
snprintf(db_path, sizeof(db_path), "%s/cbm_test_ckpt_%d.db", cbm_tmpdir(), (int)getpid());
char wal_path[PATH_BUF_EXT];
snprintf(wal_path, sizeof(wal_path), "%s-wal", db_path);
char shm_path[PATH_BUF_EXT];
snprintf(shm_path, sizeof(shm_path), "%s-shm", db_path);
unlink(db_path);
unlink(wal_path);
unlink(shm_path);
cbm_store_t *s = cbm_store_open_path(db_path);
ASSERT(s != NULL);
/* Grow WAL beyond zero bytes via direct SQL. */
int rc_sql = cbm_store_exec(
s,
"INSERT OR IGNORE INTO projects(name, indexed_at, root_path) "
"VALUES('p', '2026-01-01', '/tmp/p');");
ASSERT_EQ(rc_sql, 0);
for (int i = 0; i < N_ROWS; i++) {
char sql[256];
snprintf(sql, sizeof(sql),
"INSERT INTO nodes(project, label, name, qualified_name, file_path) "
"VALUES('p', 'Function', 'fn', 'p.module.fn_%d', 'f.c');",
i);
rc_sql = cbm_store_exec(s, sql);
ASSERT_EQ(rc_sql, 0);
}
/* WAL must exist and be non-empty before the checkpoint call. */
struct stat st_before;
int rc_stat = stat(wal_path, &st_before);
ASSERT_EQ(rc_stat, 0);
ASSERT(st_before.st_size > 0);
/* Under SQLITE_CHECKPOINT_TRUNCATE the WAL would be ftruncate()d to 0
* bytes on success. Under SQLITE_CHECKPOINT_PASSIVE the file size is
* preserved (frames marked, not removed). */
int rc_ckpt = cbm_store_checkpoint(s);
ASSERT_EQ(rc_ckpt, 0); /* CBM_STORE_OK */
struct stat st_after;
rc_stat = stat(wal_path, &st_after);
ASSERT_EQ(rc_stat, 0);
ASSERT(st_after.st_size > 0);
cbm_store_close(s);
unlink(db_path);
unlink(wal_path);
unlink(shm_path);
PASS();
}
SUITE(store_checkpoint) {
RUN_TEST(checkpoint_does_not_truncate_wal);
}