view mrjunejune/test/template_renderer_test.c @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -0700
parents 04fee26ecce0
children
line wrap: on
line source

/*
 * template_renderer_test.c — unit tests for the Mjj template renderer.
 *
 * Tests cover:
 *   - Passthrough (no includes)
 *   - Include expansion with real parts files
 *   - Missing include (silently skipped, output is still valid)
 *   - Missing file (Mjj_Template_Render_File returns FALSE)
 *   - Capacity-overflow protection (returns FALSE)
 *   - Page bodies contain no literal {{ / }} / <{{ markers
 *   - Rendered pages contain expected design-system asset references
 */

#include "mrjunejune/template_renderer.h"

#include "dowa/dowa.h"

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* ------------------------------------------------------------------ */
/* Utilities                                                            */
/* ------------------------------------------------------------------ */

#define ASSERT(cond) \
  do { \
    if (!(cond)) { \
      fprintf(stderr, "FAIL [%s:%d]: %s\n", __FILE__, __LINE__, #cond); \
      abort(); \
    } \
  } while (0)

#define TEST(name) \
  do { fprintf(stdout, "  %-60s", name); fflush(stdout); } while (0)

#define PASS() \
  do { fprintf(stdout, "PASS\n"); } while (0)

/* Capacity used for page-level renders */
#define PAGE_CAP (256 * 1024)

/* ------------------------------------------------------------------ */
/* Tests                                                                */
/* ------------------------------------------------------------------ */

static void test_passthrough(void)
{
  printf("\n[passthrough — no includes]\n");

  Dowa_Arena *arena = Dowa_Arena_Create(4096);
  ASSERT(arena);

  TEST("plain text passes through unchanged");
  {
    char out[64];
    boolean ok = Mjj_Template_Render(out, sizeof(out), "Hello, world!", arena);
    ASSERT(ok);
    ASSERT(strcmp(out, "Hello, world!") == 0);
  }
  PASS();

  TEST("empty template produces empty string");
  {
    char out[8];
    out[0] = 'x';
    boolean ok = Mjj_Template_Render(out, sizeof(out), "", arena);
    ASSERT(ok);
    ASSERT(out[0] == '\0');
  }
  PASS();

  TEST("NULL template returns FALSE");
  {
    char out[8];
    boolean ok = Mjj_Template_Render(out, sizeof(out), NULL, arena);
    ASSERT(!ok);
  }
  PASS();

  Dowa_Arena_Free(arena);
}

static void test_capacity_overflow(void)
{
  printf("\n[capacity-overflow protection]\n");

  Dowa_Arena *arena = Dowa_Arena_Create(4096);
  ASSERT(arena);

  TEST("render into 1-byte buffer returns FALSE");
  {
    char out[1];
    boolean ok = Mjj_Template_Render(out, 1, "Hello", arena);
    ASSERT(!ok);
    /* NUL-termination must still hold */
    ASSERT(out[0] == '\0');
  }
  PASS();

  TEST("render into exactly-fitting buffer (size = len+1) returns TRUE");
  {
    const char *src = "Hi";
    char out[3]; /* 2 chars + NUL */
    boolean ok = Mjj_Template_Render(out, sizeof(out), src, arena);
    ASSERT(ok);
    ASSERT(strcmp(out, "Hi") == 0);
  }
  PASS();

  TEST("render into too-small buffer returns FALSE");
  {
    const char *src = "Hello world";
    char out[5];
    boolean ok = Mjj_Template_Render(out, sizeof(out), src, arena);
    ASSERT(!ok);
    /* Must still be NUL-terminated */
    ASSERT(out[sizeof(out) - 1] == '\0');
  }
  PASS();

  TEST("Render_File into tiny buffer returns FALSE");
  {
    char out[4];
    boolean ok = Mjj_Template_Render_File(out, sizeof(out), "/login/index.html", arena);
    ASSERT(!ok);
    ASSERT(out[sizeof(out) - 1] == '\0' || out[0] == '\0');
  }
  PASS();

  Dowa_Arena_Free(arena);
}

static void test_missing_file(void)
{
  printf("\n[missing file / missing include]\n");

  Dowa_Arena *arena = Dowa_Arena_Create(4096);
  ASSERT(arena);

  TEST("Render_File on non-existent path returns FALSE");
  {
    char out[256];
    boolean ok = Mjj_Template_Render_File(out, sizeof(out), "/does/not/exist.html", arena);
    ASSERT(!ok);
  }
  PASS();

  TEST("missing include in template is skipped silently");
  {
    char out[256];
    boolean ok = Mjj_Template_Render(
        out, sizeof(out),
        "before{{/parts/this_does_not_exist.html}}after",
        arena);
    ASSERT(ok);
    ASSERT(strcmp(out, "beforeafter") == 0);
    /* No literal {{ or }} should remain */
    ASSERT(strstr(out, "{{") == NULL);
    ASSERT(strstr(out, "}}") == NULL);
  }
  PASS();

  Dowa_Arena_Free(arena);
}

static void test_include_expansion(void)
{
  printf("\n[include expansion — real parts files]\n");

  Dowa_Arena *arena = Dowa_Arena_Create(8192);
  ASSERT(arena);

  TEST("{{/parts/base_head.html}} expands to non-empty content");
  {
    char *out = (char *)malloc(PAGE_CAP);
    ASSERT(out);
    boolean ok = Mjj_Template_Render(
        out, PAGE_CAP,
        "A{{/parts/base_head.html}}B",
        arena);
    ASSERT(ok);
    /* Expansion must have happened: output is longer than "AB" */
    ASSERT(strlen(out) > 2);
    /* No literal markers must remain */
    ASSERT(strstr(out, "{{") == NULL);
    ASSERT(strstr(out, "}}") == NULL);
    /* base_head includes charset and stylesheet references */
    ASSERT(strstr(out, "charset") != NULL);
    ASSERT(strstr(out, "design-system") != NULL);
    free(out);
  }
  PASS();

  TEST("{{/parts/header.html}} expands and contains MrJuneJune link");
  {
    char *out = (char *)malloc(PAGE_CAP);
    ASSERT(out);
    boolean ok = Mjj_Template_Render(
        out, PAGE_CAP,
        "{{/parts/header.html}}",
        arena);
    ASSERT(ok);
    ASSERT(strlen(out) > 0);
    ASSERT(strstr(out, "{{") == NULL);
    ASSERT(strstr(out, "MrJuneJune") != NULL);
    free(out);
  }
  PASS();

  Dowa_Arena_Free(arena);
}

/* Check that a fully rendered page contains no literal template markers. */
static void assert_page_clean(const char *path, char *buf)
{
  Dowa_Arena *arena = Dowa_Arena_Create(8192);
  ASSERT(arena);

  boolean ok = Mjj_Template_Render_File(buf, PAGE_CAP, path, arena);
  if (!ok)
  {
    fprintf(stderr, "  FAIL: Mjj_Template_Render_File returned FALSE for '%s'\n", path);
    abort();
  }

  if (strstr(buf, "{{"))
  {
    fprintf(stderr, "  FAIL: rendered '%s' contains '{{'\n", path);
    abort();
  }
  if (strstr(buf, "}}"))
  {
    fprintf(stderr, "  FAIL: rendered '%s' contains '}}'\n", path);
    abort();
  }
  if (strstr(buf, "<{{"))
  {
    fprintf(stderr, "  FAIL: rendered '%s' contains '<{{'\n", path);
    abort();
  }

  Dowa_Arena_Free(arena);
}

static void test_page_bodies(void)
{
  printf("\n[page bodies — no literal markers, design-system assets present]\n");

  char *buf = (char *)malloc(PAGE_CAP);
  ASSERT(buf);

  const char *pages[] = {
    "/login/index.html",
    "/account/password.html",
    "/admin/users/index.html",
    "/index.html",
    "/resume/index.html",
    "/tools/index.html",
    "/blog/index.html",
    "/talk/index.html",
    "/notes/index.html",
    NULL
  };

  for (int i = 0; pages[i]; i++)
  {
    char label[128];
    snprintf(label, sizeof(label), "no {{ in rendered '%s'", pages[i]);
    TEST(label);
    assert_page_clean(pages[i], buf);
    PASS();
  }

  /* Verify design-system stylesheet and component script are referenced
     in the login page (canonical example that contains base_head). */
  TEST("login page references design-system/styles/tokens.css");
  {
    Dowa_Arena *arena = Dowa_Arena_Create(4096);
    ASSERT(arena);
    boolean ok = Mjj_Template_Render_File(buf, PAGE_CAP, "/login/index.html", arena);
    ASSERT(ok);
    ASSERT(strstr(buf, "design-system") != NULL);
    ASSERT(strstr(buf, "tokens.css") != NULL);
    Dowa_Arena_Free(arena);
  }
  PASS();

  TEST("admin page references design-system and header");
  {
    Dowa_Arena *arena = Dowa_Arena_Create(4096);
    ASSERT(arena);
    boolean ok = Mjj_Template_Render_File(buf, PAGE_CAP, "/admin/users/index.html", arena);
    ASSERT(ok);
    ASSERT(strstr(buf, "design-system") != NULL);
    ASSERT(strstr(buf, "MrJuneJune") != NULL);
    Dowa_Arena_Free(arena);
  }
  PASS();

  TEST("password page references design-system and header");
  {
    Dowa_Arena *arena = Dowa_Arena_Create(4096);
    ASSERT(arena);
    boolean ok = Mjj_Template_Render_File(buf, PAGE_CAP, "/account/password.html", arena);
    ASSERT(ok);
    ASSERT(strstr(buf, "design-system") != NULL);
    ASSERT(strstr(buf, "MrJuneJune") != NULL);
    Dowa_Arena_Free(arena);
  }
  PASS();

  free(buf);
}

/* ------------------------------------------------------------------ */
/* main                                                                 */
/* ------------------------------------------------------------------ */

int main(void)
{
  printf("=== template_renderer_test ===\n");

  /* The renderer defaults to "mrjunejune/src"; in Bazel test, CWD is the
     runfiles root where mrjunejune/src/... is directly accessible. */

  test_passthrough();
  test_capacity_overflow();
  test_missing_file();
  test_include_expansion();
  test_page_bodies();

  printf("\nAll template_renderer tests passed.\n");
  return 0;
}