trainlog/tui/tests/test_database.c
2026-09-05 19:59:55 +02:00

258 lines
6.9 KiB
C

/**
* @file test_database.c
* @brief Black-box tests for Trainlog persistence.
*/
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "trainlog/database.h"
#include "trainlog/id.h"
#define CHECK(condition) \
do { \
if (!(condition)) { \
(void)fprintf( \
stderr, \
"CHECK failed at %s:%d: %s\n", \
__FILE__, \
__LINE__, \
#condition \
); \
return false; \
} \
} while (0)
static bool test_database_open_and_schema(void)
{
TrainlogDatabase *database = NULL;
int schema_version = 0;
int foreign_keys = 0;
CHECK(
trainlog_database_open(":memory:", &database) ==
TRAINLOG_STATUS_OK
);
CHECK(
trainlog_database_schema_version(database, &schema_version) ==
TRAINLOG_STATUS_OK
);
CHECK(schema_version == TRAINLOG_DATABASE_SCHEMA_VERSION);
CHECK(
trainlog_database_foreign_keys_enabled(database, &foreign_keys) ==
TRAINLOG_STATUS_OK
);
CHECK(foreign_keys == 1);
trainlog_database_close(database);
return true;
}
static bool test_generated_ids(void)
{
char first[TRAINLOG_GENERATED_ID_CAPACITY];
char second[TRAINLOG_GENERATED_ID_CAPACITY];
CHECK(
trainlog_id_generate("ex", first, sizeof(first)) ==
TRAINLOG_STATUS_OK
);
CHECK(
trainlog_id_generate("ex", second, sizeof(second)) ==
TRAINLOG_STATUS_OK
);
CHECK(strncmp(first, "ex_", 3U) == 0);
CHECK(strlen(first) == TRAINLOG_GENERATED_ID_CAPACITY - 1U);
CHECK(strcmp(first, second) != 0);
CHECK(first[3U + 14U] == '4');
return true;
}
static bool seed_exercise(TrainlogDatabase *database)
{
return trainlog_database_insert_exercise(
database,
"ex_test",
"Presse à cuisses",
"presse à cuisses",
TRAINLOG_TRACKING_REPS
) == TRAINLOG_STATUS_OK;
}
static bool test_session_insert(void)
{
TrainlogDatabase *database = NULL;
TrainlogSetInput sets[2];
TrainlogSessionExerciseInput exercise;
TrainlogSessionInput session;
size_t count = 0U;
CHECK(
trainlog_database_open(":memory:", &database) ==
TRAINLOG_STATUS_OK
);
CHECK(seed_exercise(database));
(void)memset(sets, 0, sizeof(sets));
sets[0].reps = 5;
sets[0].has_weight = true;
sets[0].weight_kg = 80.0;
sets[1] = sets[0];
(void)memset(&exercise, 0, sizeof(exercise));
(void)snprintf(
exercise.exercise_id,
sizeof(exercise.exercise_id),
"%s",
"ex_test"
);
exercise.load_mode = TRAINLOG_LOAD_EXTERNAL;
exercise.rest_seconds = 60;
exercise.target_sets = 2;
exercise.target_reps = 5;
exercise.target_has_weight = true;
exercise.target_weight_kg = 80.0;
exercise.sets = sets;
exercise.set_count = 2U;
(void)memset(&session, 0, sizeof(session));
(void)snprintf(
session.session_id,
sizeof(session.session_id),
"%s",
"se_test"
);
(void)snprintf(
session.started_at,
sizeof(session.started_at),
"%s",
"2026-09-05T18:00:00+02:00"
);
(void)snprintf(
session.ended_at,
sizeof(session.ended_at),
"%s",
"2026-09-05T19:00:00+02:00"
);
session.exercises = &exercise;
session.exercise_count = 1U;
CHECK(
trainlog_database_insert_session(database, &session) ==
TRAINLOG_STATUS_OK
);
CHECK(
trainlog_database_insert_session(database, &session) ==
TRAINLOG_STATUS_CONFLICT
);
CHECK(
trainlog_database_session_count(database, &count) ==
TRAINLOG_STATUS_OK
);
CHECK(count == 1U);
trainlog_database_close(database);
return true;
}
static bool test_body_weight_history(void)
{
TrainlogDatabase *database = NULL;
TrainlogBodyObservationInput observation;
TrainlogWeightPoint points[4];
size_t count = 0U;
CHECK(
trainlog_database_open(":memory:", &database) ==
TRAINLOG_STATUS_OK
);
(void)memset(&observation, 0, sizeof(observation));
(void)snprintf(
observation.observation_id,
sizeof(observation.observation_id),
"%s",
"bo_test"
);
(void)snprintf(
observation.observed_at,
sizeof(observation.observed_at),
"%s",
"2026-09-05T07:00:00+02:00"
);
observation.has_body_weight = true;
observation.body_weight_kg = 82.4;
CHECK(
trainlog_database_insert_body_observation(
database,
&observation
) == TRAINLOG_STATUS_OK
);
CHECK(
trainlog_database_list_weight_points(
database,
points,
4U,
&count
) == TRAINLOG_STATUS_OK
);
CHECK(count == 1U);
CHECK(points[0].body_weight_kg > 82.39);
CHECK(points[0].body_weight_kg < 82.41);
trainlog_database_close(database);
return true;
}
static bool test_transaction_rollback(void)
{
TrainlogDatabase *database = NULL;
size_t count = 0U;
CHECK(
trainlog_database_open(":memory:", &database) ==
TRAINLOG_STATUS_OK
);
CHECK(trainlog_database_begin(database) == TRAINLOG_STATUS_OK);
CHECK(seed_exercise(database));
CHECK(trainlog_database_rollback(database) == TRAINLOG_STATUS_OK);
CHECK(
trainlog_database_exercise_count(database, &count) ==
TRAINLOG_STATUS_OK
);
CHECK(count == 0U);
trainlog_database_close(database);
return true;
}
struct TestCase {
const char *name;
bool (*function)(void);
};
int main(void)
{
static const struct TestCase tests[] = {
{"database_open_and_schema", test_database_open_and_schema},
{"generated_ids", test_generated_ids},
{"session_insert", test_session_insert},
{"body_weight_history", test_body_weight_history},
{"transaction_rollback", test_transaction_rollback},
};
size_t index;
for (index = 0U; index < sizeof(tests) / sizeof(tests[0]); ++index) {
if (!tests[index].function()) {
(void)fprintf(stderr, "FAIL %s\n", tests[index].name);
return 1;
}
(void)printf("PASS %s\n", tests[index].name);
}
return 0;
}