test(web): cover dashboard failure states
This commit is contained in:
parent
f894f744f3
commit
6f34233751
3 changed files with 53 additions and 3 deletions
|
|
@ -61,7 +61,8 @@ day, an unfinished session, continuous activity, MAX-only sessions, primary
|
|||
zone deduplication, catalogue-without-history exclusion, 90/30-day windows,
|
||||
recent-MAX truncation/`partial`, Footer facts and JSON escaping/overflow. The
|
||||
HTTP lifecycle test additionally proves `GET /api/v1/dashboard`, JSON content
|
||||
type/version, unavailable reasons and `POST` rejection without weakening Host,
|
||||
type/version, unavailable reasons, corrupt-timestamp `invalid_data`, Core-error
|
||||
translation to bounded HTTP 500 and `POST` rejection without weakening Host,
|
||||
CSP, CORS or unknown-route behavior. Vitest validates strict parsing, real
|
||||
Footer population, empty fallback, malformed snapshots and absence of invented
|
||||
tile metrics; the seven tile bodies remain placeholders.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "trainlog/web_dashboard.h"
|
||||
|
||||
|
|
@ -36,4 +40,6 @@ static bool test_empty_and_real_facts(void){
|
|||
|
||||
static bool test_json_escaping_and_limit(void){TrainlogWebDashboardSnapshot s;char json[TRAINLOG_WEB_JSON_CAPACITY];char tiny[8];size_t size;(void)memset(&s,0,sizeof(s));(void)snprintf(s.user_display_name,sizeof(s.user_display_name),"A\"B\\C\nÉ");(void)snprintf(s.generated_at,sizeof(s.generated_at),"2026-09-16T12:00:00Z");s.next_session_reason="no_persisted_executable_plan";s.cardio_reason="no_cardio_data_source";CHECK(trainlog_web_dashboard_serialize(&s,json,sizeof(json),&size)==TRAINLOG_STATUS_OK&&size>0U);CHECK(strstr(json,"A\\\"B\\\\C\\nÉ")!=NULL);CHECK(trainlog_web_dashboard_serialize(&s,tiny,sizeof(tiny),&size)==TRAINLOG_STATUS_SYSTEM_ERROR);s.user_display_name[0]=(char)0xc3;s.user_display_name[1]='\0';CHECK(trainlog_web_dashboard_serialize(&s,json,sizeof(json),&size)==TRAINLOG_STATUS_SYSTEM_ERROR);return true;}
|
||||
|
||||
int main(void){return test_empty_and_real_facts()&&test_json_escaping_and_limit()?0:1;}
|
||||
static bool test_corrupt_timestamp_sets_invalid_data(void){char path[]="/tmp/trainlog-web-dashboard-XXXXXX";int fd=mkstemp(path);TrainlogDatabase*d=NULL;sqlite3*raw=NULL;TrainlogWebDashboardSnapshot s;TrainlogWebDashboardQuery q={INT64_C(1789560000)};CHECK(fd>=0);CHECK(close(fd)==0);CHECK(trainlog_database_open(path,&d)==TRAINLOG_STATUS_OK);CHECK(insert_fact(d,"se_92000000-0000-4000-8000-000000000001","en_92000000-0000-4000-8000-000000000001","2026-09-15T10:00:00+02:00",0)==false);CHECK(trainlog_database_insert_exercise_profiled(d,"ex_90000000-0000-4000-8000-000000000001","Corruptible","corruptible",TRAINLOG_TRACKING_REPS,TRAINLOG_RECORDING_SETS,(TrainlogExerciseDataFields)0)==TRAINLOG_STATUS_OK);CHECK(insert_fact(d,"se_92000000-0000-4000-8000-000000000001","en_92000000-0000-4000-8000-000000000001","2026-09-15T10:00:00+02:00",0));trainlog_database_close(d);d=NULL;CHECK(sqlite3_open(path,&raw)==SQLITE_OK);CHECK(sqlite3_exec(raw,"UPDATE sessions SET started_at='corrupt';",NULL,NULL,NULL)==SQLITE_OK);CHECK(sqlite3_close(raw)==SQLITE_OK);raw=NULL;CHECK(trainlog_database_open(path,&d)==TRAINLOG_STATUS_OK);CHECK(trainlog_web_dashboard_load(d,&q,&s)==TRAINLOG_STATUS_OK);CHECK(s.invalid_data);trainlog_database_close(d);CHECK(unlink(path)==0);return true;}
|
||||
|
||||
int main(void){return test_empty_and_real_facts()&&test_json_escaping_and_limit()&&test_corrupt_timestamp_sets_invalid_data()?0:1;}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "trainlog/database.h"
|
||||
#include "trainlog/web_server.h"
|
||||
#include "web_assets.h"
|
||||
|
|
@ -252,6 +254,46 @@ static bool test_port_in_use(TrainlogDatabase *database)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool test_dashboard_core_error_translation(void)
|
||||
{
|
||||
char path[] = "/tmp/trainlog-web-error-XXXXXX";
|
||||
char response[4096];
|
||||
TrainlogDatabase *database = NULL;
|
||||
sqlite3 *raw = NULL;
|
||||
uint16_t port;
|
||||
pid_t child;
|
||||
int fd = mkstemp(path);
|
||||
int status;
|
||||
CHECK(fd >= 0 && close(fd) == 0);
|
||||
CHECK(trainlog_database_open(path, &database) == TRAINLOG_STATUS_OK);
|
||||
trainlog_database_close(database);
|
||||
database = NULL;
|
||||
CHECK(sqlite3_open(path, &raw) == SQLITE_OK);
|
||||
CHECK(sqlite3_exec(raw, "DROP TABLE sessions;", NULL, NULL, NULL) == SQLITE_OK);
|
||||
CHECK(sqlite3_close(raw) == SQLITE_OK);
|
||||
raw = NULL;
|
||||
CHECK(trainlog_database_open(path, &database) == TRAINLOG_STATUS_OK);
|
||||
CHECK(reserve_port(&port, 0) == -2);
|
||||
child = fork();
|
||||
CHECK(child >= 0);
|
||||
if (child == 0) {
|
||||
volatile sig_atomic_t stop = 0;
|
||||
char diagnostic[256];
|
||||
_exit(trainlog_web_server_run(database, port, &stop, diagnostic,
|
||||
sizeof(diagnostic)) == 0 ? 0 : 1);
|
||||
}
|
||||
CHECK(exchange(port,
|
||||
"GET /api/v1/dashboard HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n",
|
||||
response, sizeof(response)));
|
||||
CHECK(strstr(response, "HTTP/1.1 500") != NULL);
|
||||
CHECK(strstr(response, "\"dashboard_unavailable\"") != NULL);
|
||||
CHECK(kill(child, SIGTERM) == 0);
|
||||
CHECK(waitpid(child, &status, 0) == child);
|
||||
trainlog_database_close(database);
|
||||
CHECK(unlink(path) == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TrainlogDatabase *database = NULL;
|
||||
|
|
@ -264,7 +306,8 @@ int main(void)
|
|||
passed = trainlog_web_server_run(database, 8080U, &stop, diagnostic,
|
||||
sizeof(diagnostic)) != 0 && strstr(diagnostic, "frontend") != NULL;
|
||||
} else {
|
||||
passed = test_http_contract(database) && test_port_in_use(database);
|
||||
passed = test_http_contract(database) && test_port_in_use(database) &&
|
||||
test_dashboard_core_error_translation();
|
||||
}
|
||||
trainlog_database_close(database);
|
||||
return passed ? 0 : 1;
|
||||
|
|
|
|||
Loading…
Reference in a new issue