LCOV - code coverage report
Current view: top level - tests/unit - test_watch_json.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 59 59
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 11 11

            Line data    Source code
       1              : /**
       2              :  * @file test_watch_json.c
       3              :  * @brief Unit tests for FEAT-04: --json flag on `watch` subcommand.
       4              :  *
       5              :  * Tests cover:
       6              :  *  1. Argument parsing: `watch --json` sets args.json = 1.
       7              :  *  2. JSON string escaping via json_escape_str().
       8              :  */
       9              : 
      10              : #include "test_helpers.h"
      11              : #include "arg_parse.h"
      12              : #include "json_util.h"
      13              : 
      14              : #include <string.h>
      15              : #include <stdlib.h>
      16              : 
      17              : /* ---- Test: `--json watch` (global flag before subcommand) ---- */
      18            1 : static void test_watch_json_global_flag(void) {
      19            1 :     char *argv[] = {"tg-cli", "--json", "watch", NULL};
      20              :     ArgResult r;
      21            1 :     int rc = arg_parse(3, argv, &r);
      22            1 :     ASSERT(rc == ARG_OK,           "--json watch: must return ARG_OK");
      23            1 :     ASSERT(r.command == CMD_WATCH, "--json watch: command must be CMD_WATCH");
      24            1 :     ASSERT(r.json == 1,            "--json watch: json flag must be 1");
      25              : }
      26              : 
      27              : /* ---- Test: `watch` without --json keeps json=0 ---- */
      28            1 : static void test_watch_no_json_flag(void) {
      29            1 :     char *argv[] = {"tg-cli", "watch", NULL};
      30              :     ArgResult r;
      31            1 :     int rc = arg_parse(2, argv, &r);
      32            1 :     ASSERT(rc == ARG_OK,           "watch (no --json): must return ARG_OK");
      33            1 :     ASSERT(r.command == CMD_WATCH, "watch (no --json): command must be CMD_WATCH");
      34            1 :     ASSERT(r.json == 0,            "watch (no --json): json flag must be 0");
      35              : }
      36              : 
      37              : /* ---- Test: `--json watch --peers X --interval 5` combined ---- */
      38            1 : static void test_watch_json_with_peers_and_interval(void) {
      39            1 :     char *argv[] = {"tg-cli", "--json", "watch", "--peers", "@news",
      40              :                     "--interval", "10", NULL};
      41              :     ArgResult r;
      42            1 :     int rc = arg_parse(7, argv, &r);
      43            1 :     ASSERT(rc == ARG_OK,                     "watch json+peers+interval: ARG_OK");
      44            1 :     ASSERT(r.command == CMD_WATCH,           "watch json+peers+interval: CMD_WATCH");
      45            1 :     ASSERT(r.json == 1,                      "watch json+peers+interval: json=1");
      46            1 :     ASSERT(r.watch_interval == 10,           "watch json+peers+interval: interval=10");
      47            1 :     ASSERT(r.watch_peers != NULL,                   "watch json+peers+interval: watch_peers set");
      48            1 :     ASSERT(strcmp(r.watch_peers, "@news") == 0,     "watch json+peers+interval: watch_peers=@news");
      49              : }
      50              : 
      51              : /* ---- Test: json_escape_str — plain ASCII passes through unchanged ---- */
      52            1 : static void test_json_escape_plain(void) {
      53              :     char buf[64];
      54            1 :     size_t n = json_escape_str(buf, sizeof(buf), "hello world");
      55            1 :     ASSERT(strcmp(buf, "hello world") == 0, "json_escape plain: output matches");
      56            1 :     ASSERT(n == 11,                          "json_escape plain: length correct");
      57              : }
      58              : 
      59              : /* ---- Test: json_escape_str — double-quote and backslash are escaped ---- */
      60            1 : static void test_json_escape_special_chars(void) {
      61              :     char buf[64];
      62              :     /* Input: say "hi"\path  →  say \"hi\"\\path */
      63            1 :     json_escape_str(buf, sizeof(buf), "say \"hi\"\\path");
      64            1 :     ASSERT(strcmp(buf, "say \\\"hi\\\"\\\\path") == 0,
      65              :            "json_escape special: quotes and backslashes escaped");
      66              : }
      67              : 
      68              : /* ---- Test: json_escape_str — control characters use shorthand/unicode ---- */
      69            1 : static void test_json_escape_control_chars(void) {
      70              :     char buf[64];
      71              :     /* newline, tab, carriage-return */
      72            1 :     json_escape_str(buf, sizeof(buf), "a\nb\tc\r");
      73            1 :     ASSERT(strcmp(buf, "a\\nb\\tc\\r") == 0,
      74              :            "json_escape control: \\n \\t \\r expanded");
      75              : }
      76              : 
      77              : /* ---- Test: json_escape_str — other C0 control characters → \\uXXXX ---- */
      78            1 : static void test_json_escape_c0_unicode(void) {
      79              :     char buf[32];
      80              :     /* ASCII 0x01 (SOH) and 0x1f (US) */
      81            1 :     char input[3] = {'\x01', '\x1f', '\0'};
      82            1 :     json_escape_str(buf, sizeof(buf), input);
      83            1 :     ASSERT(strcmp(buf, "\\u0001\\u001f") == 0,
      84              :            "json_escape c0: \\u0001 and \\u001f");
      85              : }
      86              : 
      87              : /* ---- Test: json_escape_str — UTF-8 multibyte passes through ---- */
      88            1 : static void test_json_escape_utf8_passthrough(void) {
      89              :     char buf[64];
      90              :     /* UTF-8 encoded em-dash U+2014: 0xE2 0x80 0x94 */
      91            1 :     const char *input = "\xe2\x80\x94";
      92            1 :     json_escape_str(buf, sizeof(buf), input);
      93            1 :     ASSERT(strcmp(buf, "\xe2\x80\x94") == 0,
      94              :            "json_escape utf8: high bytes pass through");
      95              : }
      96              : 
      97              : /* ---- Test: json_escape_str — NULL input treated as empty string ---- */
      98            1 : static void test_json_escape_null_input(void) {
      99              :     char buf[8];
     100            1 :     size_t n = json_escape_str(buf, sizeof(buf), NULL);
     101            1 :     ASSERT(n == 0,             "json_escape NULL: length is 0");
     102            1 :     ASSERT(buf[0] == '\0',     "json_escape NULL: NUL-terminated empty string");
     103              : }
     104              : 
     105              : /* ---- Test: json_escape_str — output truncated when buffer too small ---- */
     106            1 : static void test_json_escape_truncation(void) {
     107              :     char buf[5]; /* only room for 4 chars + NUL */
     108            1 :     size_t n = json_escape_str(buf, sizeof(buf), "abcdefgh");
     109              :     /* Must be NUL-terminated within the 5-byte buffer. */
     110            1 :     ASSERT(buf[4] == '\0',  "json_escape trunc: NUL-terminated");
     111            1 :     ASSERT(n > 4,           "json_escape trunc: returns true length (>= cap)");
     112              :     (void)n;
     113              : }
     114              : 
     115            1 : void run_watch_json_tests(void) {
     116            1 :     RUN_TEST(test_watch_json_global_flag);
     117            1 :     RUN_TEST(test_watch_no_json_flag);
     118            1 :     RUN_TEST(test_watch_json_with_peers_and_interval);
     119            1 :     RUN_TEST(test_json_escape_plain);
     120            1 :     RUN_TEST(test_json_escape_special_chars);
     121            1 :     RUN_TEST(test_json_escape_control_chars);
     122            1 :     RUN_TEST(test_json_escape_c0_unicode);
     123            1 :     RUN_TEST(test_json_escape_utf8_passthrough);
     124            1 :     RUN_TEST(test_json_escape_null_input);
     125            1 :     RUN_TEST(test_json_escape_truncation);
     126            1 : }
        

Generated by: LCOV version 2.0-1