changeset 126:e7899c93da77

Remove playground.
author June Park <parkjune1995@gmail.com>
date Thu, 08 Jan 2026 18:03:34 -0800
parents f236c895604e
children 9af248484ba2
files mrjunejune/BUILD mrjunejune/test/integration_test.c mrjunejune/test/snapshots/resume.snapshot mrjunejune/test/snapshots/resume_index.html.snapshot mrjunejune/test/snapshots/root.snapshot mrjunejune/test/snapshots/tools.snapshot mrjunejune/test/snapshots/tools_file_converter.snapshot mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot mrjunejune/test/snapshots/tools_index.html.snapshot mrjunejune/test/snapshots/tools_markdown_to_html.snapshot mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot playground/.gitignore playground/BUILD playground/README.md playground/foo/foo.ts playground/hello.ts playground/june.tsx playground/main playground/main.c playground/main.ts playground/new.txt seobeo/BUILD seobeo/snapshot_creator.c
diffstat 23 files changed, 299 insertions(+), 2083 deletions(-) [+]
line wrap: on
line diff
--- a/mrjunejune/BUILD	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/BUILD	Thu Jan 08 18:03:34 2026 -0800
@@ -23,6 +23,7 @@
 filegroup(
   name = "src_files",
   srcs = glob(["src/**"]) + [":compiled_ts", ":compiled_ts_games"],
+  visibility = ["//mrjunejune/test:__pkg__"],
 )
 
 # Server binary
@@ -31,6 +32,7 @@
   srcs = ["main.c"],
   deps = ["//seobeo:seobeo_tcp_server_ws"],
   data = [":src_files"],
+  visibility = ["//mrjunejune/test:__pkg__"],
 )
 
 cc_binary(
@@ -51,47 +53,6 @@
   binary = ":mrjunejune_server_dev",
 )
 
-# Tests
-# TODO: Move this in a folder.
-cc_test(
-  name = "integration_test",
-  srcs = ["test/integration_test.c"],
-  deps = ["//seobeo:seobeo_min"],
-  data = [
-    "//mrjunejune:mrjunejune_server",
-    "//mrjunejune:src_files",
-    "//mrjunejune:test_snapshots",
-    "//mrjunejune:test_files",
-  ],
-  size = "large",
-  timeout = "long",
-  args = ["$(location //mrjunejune:mrjunejune_server)"],
-)
-
-cc_binary(
-  name = "create_snapshots",
-  srcs = ["test/create_snapshots.c"],
-  deps = ["//seobeo:seobeo_tcp_client"],
-  data = [
-    "//mrjunejune:mrjunejune_server",
-    "//mrjunejune:src_files",
-  ],
-  args = ["$(location //mrjunejune:mrjunejune_server)"],
-)
-
-filegroup(
-  name = "test_snapshots",
-  srcs = glob(["test/snapshots/**"]),
-)
-
-filegroup(
-  name = "test_files",
-  srcs = [
-    "test/shiba.webp",
-    "test/test_avi.avi",
-  ],
-)
-
 # Experimenting with python to see if I can call it as ffi.
 # load("@rules_python//python:py_binary.bzl", "py_binary")
 # This was to use python ffi, but w/e
--- a/mrjunejune/test/integration_test.c	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/integration_test.c	Thu Jan 08 18:03:34 2026 -0800
@@ -1,19 +1,4 @@
-#include "seobeo/seobeo.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <assert.h>
-
-#define TEST_PORT "6969"
-#define TEST_HOST "127.0.0.1"
-#define MAX_RESPONSE_SIZE (1024 * 1024)
-#ifndef SNAPSHOT_DIR
-  // TODO: Make it as current directory /snapshots...
-  #define SNAPSHOT_DIR "mrjunejune/test/snapshots"
-#endif
+#include "seobeo/test/test.h"
 
 // Test case structure
 typedef struct {
@@ -117,274 +102,67 @@
   return 0;
 }
 
-// Helper: Create test client
-Seobeo_Handle* create_test_client()
-{
-  Seobeo_Handle *client = Seobeo_Stream_Handle_Client_Create(TEST_HOST, TEST_PORT, FALSE);
-  if (!client || client->socket < 0)
-  {
-    if (client)
-    {
-      Seobeo_Handle_Destroy(client);
-    }
-    return NULL;
-  }
-  return client;
-}
-
-// Helper: Generate default HTTP GET request
-int generate_http_get_request(char *buffer, size_t buffer_size, const char *path)
+int execute_test_case(TestCase *test, pid_t server_pid)
 {
-  return snprintf(
-    buffer, buffer_size,
-    "GET %s HTTP/1.1\r\n"
-    "Host: %s\r\n"
-    "Connection: close\r\n"
-    "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
-    "User-Agent: SeobeoTestClient/1.0\r\n"
-    "\r\n",
-    path, TEST_HOST
-  );
-}
-
-// Helper: Send HTTP request
-int send_http_request(Seobeo_Handle *client, const char *path, const char *custom_request)
-{
-  char request_buffer[4096];
-  int request_len;
-
-  if (custom_request)
+  printf("  Testing: GET %s (expecting %d)\n", test->path, test->expected_status);
+  
+  int32 max_url_length = 1024*3;
+  char *url = malloc(sizeof(char)*max_url_length);
+  snprintf(url, max_url_length, "%s%s", TEST_URL, test->path);
+  Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url);
+  if (!p_req)
   {
-    request_len = snprintf(request_buffer, sizeof(request_buffer), "%s", custom_request);
-  }
-  else
-  {
-    request_len = generate_http_get_request(request_buffer, sizeof(request_buffer), path);
-  }
-
-  if (request_len < 0 || request_len >= sizeof(request_buffer))
-  {
-    fprintf(stderr, "Request buffer too small\n");
-    return -1;
-  }
-
-  Seobeo_Handle_Queue(client, (uint8*)request_buffer, (uint32)request_len);
-  return Seobeo_Handle_Flush(client);
-}
-
-// Helper: Read HTTP response
-int read_http_response(Seobeo_Handle *client, char **response_out, size_t *response_len_out)
-{
-  char *response = malloc(MAX_RESPONSE_SIZE);
-  if (!response)
-  {
+    printf("Can't create requests");
     return -1;
   }
 
-  size_t total_bytes = 0;
-  int attempts = 0;
-  const int max_attempts = 100;
-
-  while (attempts++ < max_attempts && total_bytes < MAX_RESPONSE_SIZE - 1)
+  Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req);
+  if (!p_resp)
   {
-    int bytes_read = Seobeo_Handle_Read(client);
-
-    if (bytes_read > 0)
-    {
-      size_t to_copy = client->read_buffer_len;
-      if (total_bytes + to_copy > MAX_RESPONSE_SIZE - 1)
-      {
-        to_copy = MAX_RESPONSE_SIZE - 1 - total_bytes;
-      }
-
-      memcpy(response + total_bytes, client->read_buffer, to_copy);
-      total_bytes += to_copy;
-      Seobeo_Handle_Consume(client, (uint32)to_copy);
-    }
-    else if (bytes_read == -2)
-    {
-      // Connection closed
-      break;
-    }
-    else if (bytes_read == 0)
-    {
-      // Would block
-      usleep(10000);
-      continue;
-    }
-    else
-    {
-      free(response);
-      return -1;
-    }
-  }
-
-  response[total_bytes] = '\0';
-  *response_out = response;
-  *response_len_out = total_bytes;
-
-  return (total_bytes > 0) ? 0 : -1;
-}
-
-// Helper: Parse HTTP status code
-int parse_http_status(const char *response)
-{
-  if (!response || strlen(response) < 12)
-  {
-    return -1;
-  }
-
-  const char *status_start = strstr(response, "HTTP/1.1 ");
-  if (!status_start)
-  {
-    status_start = strstr(response, "HTTP/1.0 ");
-  }
-
-  if (!status_start)
-  {
+    printf("No response");
     return -1;
   }
 
-  int status_code;
-  if (sscanf(status_start + 9, "%d", &status_code) == 1)
+  if (p_resp->status_code != test->expected_status)
   {
-    return status_code;
-  }
-
-  return -1;
-}
-
-// Helper: Check if status is a redirect
-int is_redirect_status(int status)
-{
-  return (status >= 300 && status < 400);
-}
-
-// Helper: Execute a test case
-int execute_test_case(TestCase *test, pid_t server_pid)
-{
-  printf("  Testing: GET %s (expecting %d)\n", test->path, test->expected_status);
-
-  Seobeo_Handle *client = create_test_client();
-  if (!client)
-  {
-    printf("    ✗ Failed to create client connection\n");
-    return -1;
-  }
-
-  if (send_http_request(client, test->path, NULL) < 0)
-  {
-    printf("    ✗ Failed to send request\n");
-    Seobeo_Handle_Destroy(client);
+    printf("    ✗ Status mismatch: expected %d, got %d\n",
+           test->expected_status, p_resp->status_code);
+    Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  char *response = NULL;
-  size_t response_len = 0;
-  if (read_http_response(client, &response, &response_len) < 0)
-  {
-    printf("    ✗ Failed to read response\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  test->actual_response = response;
-  test->response_len = response_len;
+  printf("    ✓ Status code: %d\n", p_resp->status_code);
 
-  int actual_status = parse_http_status(response);
-  if (actual_status != test->expected_status)
-  {
-    printf("    ✗ Status mismatch: expected %d, got %d\n",
-           test->expected_status, actual_status);
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  printf("    ✓ Status code: %d\n", actual_status);
-
-  // For redirects, skip content comparison
-  if (is_redirect_status(actual_status))
-  {
-    printf("    ⚠ Redirect status - skipping content comparison\n");
-    Seobeo_Handle_Destroy(client);
-    return 0;
-  }
-
-  // Only verify 200 OK responses against snapshots
-  if (actual_status == 200)
+  if (p_resp->status_code == 200)
   {
     if (!test->expected_content)
     {
       printf("    ✗ No expected snapshot found: %s\n", test->expected_file_path);
       printf("    → Run: bazel run //mrjunejune:create_snapshots\n");
-      Seobeo_Handle_Destroy(client);
+      Seobeo_Client_Response_Destroy(p_resp);
+      Seobeo_Client_Request_Destroy(p_req);
       return -1;
     }
 
-    if (strcmp(response, test->expected_content) != 0)
+    if (strcmp(p_resp->body, test->expected_content) != 0)
     {
       printf("    ✗ Response does not match expected snapshot\n");
       printf("    Expected file: %s\n", test->expected_file_path);
-      Seobeo_Handle_Destroy(client);
+      Seobeo_Client_Response_Destroy(p_resp);
+      Seobeo_Client_Request_Destroy(p_req);
       return -1;
     }
 
-    printf("    ✓ Response matches snapshot (%zu bytes)\n", response_len);
+    printf("    ✓ Response matches snapshot (%zu bytes)\n", p_resp->body_length);
   }
 
-  Seobeo_Handle_Destroy(client);
+  Seobeo_Client_Response_Destroy(p_resp);
+  Seobeo_Client_Request_Destroy(p_req);
   return 0;
 }
 
-// Helper: Execute custom request test
-int execute_custom_request_test(const char *name, const char *custom_request,
-                                 int expected_status, pid_t server_pid)
-{
-  printf("  Testing: %s (expecting %d)\n", name, expected_status);
-
-  Seobeo_Handle *client = create_test_client();
-  if (!client)
-  {
-    printf("    ✗ Failed to create client connection\n");
-    return -1;
-  }
-
-  if (send_http_request(client, NULL, custom_request) < 0)
-  {
-    printf("    ✗ Failed to send request\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  char *response = NULL;
-  size_t response_len = 0;
-  if (read_http_response(client, &response, &response_len) < 0)
-  {
-    printf("    ✗ Failed to read response\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  int actual_status = parse_http_status(response);
-  if (actual_status != expected_status)
-  {
-    printf("    ✗ Status mismatch: expected %d, got %d\n",
-           expected_status, actual_status);
-    free(response);
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  printf("    ✓ Status code: %d\n", actual_status);
-  printf("    ✓ Response received (%zu bytes)\n", response_len);
-
-  free(response);
-  Seobeo_Handle_Destroy(client);
-  return 0;
-}
-
-// Helper: Send POST request with file data
-int send_post_file(Seobeo_Handle *client, const char *path, const char *file_data, size_t file_size)
+int send_post_file(Seobeo_Handle *p_req, const char *path, const char *file_data, size_t file_size)
 {
   char request_buffer[8192];
   int header_len = snprintf(
@@ -405,7 +183,7 @@
   }
 
   // Send headers
-  Seobeo_Handle_Queue(client, (uint8*)request_buffer, (uint32)header_len);
+  Seobeo_Handle_Queue(p_req, (uint8*)request_buffer, (uint32)header_len);
 
   // Send file data in chunks if needed
   size_t remaining = file_size;
@@ -413,15 +191,14 @@
   while (remaining > 0)
   {
     size_t chunk_size = remaining > 4096 ? 4096 : remaining;
-    Seobeo_Handle_Queue(client, (uint8*)ptr, (uint32)chunk_size);
+    Seobeo_Handle_Queue(p_req, (uint8*)ptr, (uint32)chunk_size);
     ptr += chunk_size;
     remaining -= chunk_size;
   }
 
-  return Seobeo_Handle_Flush(client);
+  return Seobeo_Handle_Flush(p_req);
 }
 
-// Helper: Extract JSON field value from response body
 char* extract_json_field(const char *json, const char *field, char *buffer, size_t buffer_size)
 {
   char search_pattern[256];
@@ -452,7 +229,6 @@
   return buffer;
 }
 
-// Helper: Test POST file conversion
 int test_file_conversion(const char *endpoint, const char *test_file_path,
                          const char *expected_format, pid_t server_pid)
 {
@@ -461,152 +237,66 @@
   // Read test file
   size_t file_size;
   char *file_data = read_file(test_file_path, &file_size);
-  if (!file_data)
-  {
+  if (!file_data) {
     printf("    ✗ Failed to read test file: %s\n", test_file_path);
     return -1;
   }
 
-  printf("    → Loaded test file (%zu bytes)\n", file_size);
+  char url[1024];
+  snprintf(url, sizeof(url), "%s%s", TEST_URL, endpoint);
 
-  // Create client and send request
-  Seobeo_Handle *client = create_test_client();
-  if (!client)
-  {
-    printf("    ✗ Failed to create client connection\n");
-    free(file_data);
-    return -1;
-  }
+  Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url);
+  Seobeo_Client_Request_Set_Method(p_req, "POST");
+  Seobeo_Client_Request_Set_Body(p_req, (uint8*)file_data, (uint32)file_size);
+  Seobeo_Client_Request_Add_Header_Array(p_req, "Content-Type: application/octet-stream");
 
-  if (send_post_file(client, endpoint, file_data, file_size) < 0)
-  {
-    printf("    ✗ Failed to send POST request\n");
-    free(file_data);
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
+  Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req);
   free(file_data);
 
-  // Read response
-  char *response = NULL;
-  size_t response_len = 0;
-  if (read_http_response(client, &response, &response_len) < 0)
-  {
-    printf("    ✗ Failed to read response\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  Seobeo_Handle_Destroy(client);
+  if (!p_resp || p_resp->status_code != 200) {
+    printf("    ✗ Conversion failed with status: %d\n", p_resp ? p_resp->status_code : 0);
+    if (p_resp) Seobeo_Client_Response_Destroy(p_resp);
 
-  // Parse status
-  int status = parse_http_status(response);
-  if (status != 200)
-  {
-    printf("    ✗ Conversion failed with status: %d\n", status);
-    printf("    Response: %s\n", response);
-    free(response);
-    return -1;
-  }
-
-  printf("    ✓ Status code: 200\n");
-
-  // Extract download URL from JSON response
-  const char *body = strstr(response, "\r\n\r\n");
-  if (!body)
-  {
-    printf("    ✗ No response body found\n");
-    free(response);
-    return -1;
-  }
-  body += 4;
-
-  char download_url[512];
-  if (!extract_json_field(body, "download_url", download_url, sizeof(download_url)))
-  {
-    printf("    ✗ Failed to extract download_url from response\n");
-    printf("    Response body: %s\n", body);
-    free(response);
+    Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  printf("    ✓ Conversion succeeded\n");
-  printf("    ✓ Download URL: %s\n", download_url);
-  free(response);
-
-  // Test downloading the converted file
-  printf("    → Testing download: GET %s\n", download_url);
-
-  client = create_test_client();
-  if (!client)
-  {
-    printf("    ✗ Failed to create client for download\n");
-    return -1;
-  }
-
-  if (send_http_request(client, download_url, NULL) < 0)
-  {
-    printf("    ✗ Failed to send download request\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  response = NULL;
-  response_len = 0;
-  if (read_http_response(client, &response, &response_len) < 0)
-  {
-    printf("    ✗ Failed to read download response\n");
-    Seobeo_Handle_Destroy(client);
+  // Extract download URL from JSON body
+  char download_url_path[512];
+  if (!extract_json_field((char*)p_resp->body, "download_url", download_url_path, sizeof(download_url_path))) {
+    printf("    ✗ Failed to extract download_url\n");
+    Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  Seobeo_Handle_Destroy(client);
+  printf("    ✓ Conversion succeeded. Download URL: %s\n", download_url_path);
+  Seobeo_Client_Response_Destroy(p_resp);
+  Seobeo_Client_Request_Destroy(p_req);
 
-  status = parse_http_status(response);
-  if (status != 200)
+  printf("    → Testing download: GET %s\n", download_url_path);
+  char full_download_url[1024];
+  snprintf(full_download_url, sizeof(full_download_url), "%s%s", TEST_URL, download_url_path);
+  
+  p_req = Seobeo_Client_Request_Create(full_download_url);
+  p_resp = Seobeo_Client_Request_Execute(p_req);
+
+  if (!p_resp || p_resp->status_code != 200)
   {
-    printf("    ✗ Download failed with status: %d\n", status);
-    free(response);
+    printf("    ✗ Download failed\n");
+    if (p_resp) Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  // Find body in download response
-  body = strstr(response, "\r\n\r\n");
-  if (!body)
-  {
-    printf("    ✗ No file data in download response\n");
-    free(response);
-    return -1;
-  }
-  body += 4;
-
-  size_t downloaded_size = response_len - (body - response);
+  printf("    ✓ Downloaded converted file (%u bytes)\n", p_resp->body_length);
 
-  // Verify content type in response headers
-  const char *content_type = strstr(response, "Content-Type: ");
-  if (!content_type)
-  {
-    printf("    ✗ No Content-Type header in download\n");
-    free(response);
-    return -1;
-  }
-
-  if (strstr(content_type, expected_format) == NULL)
-  {
-    printf("    ✗ Wrong content type (expected %s)\n", expected_format);
-    free(response);
-    return -1;
-  }
-
-  printf("    ✓ Downloaded converted file (%zu bytes)\n", downloaded_size);
-  printf("    ✓ Content-Type: %s\n", expected_format);
-
-  free(response);
+  Seobeo_Client_Response_Destroy(p_resp);
+  Seobeo_Client_Request_Destroy(p_req);
   return 0;
 }
 
-// Helper: Start test server
 pid_t start_test_server(const char *server_binary)
 {
   pid_t server_pid = fork();
@@ -694,14 +384,12 @@
 // Main integration test
 int test_server_client_integration(const char *server_binary)
 {
-  printf("=== Server-Client Integration Test ===\n");
+  printf("=== Server-p_req Integration Test ===\n");
   printf("MODE: Verifying Against Snapshots\n\n");
 
   char cwd[1024];
   if (getcwd(cwd, sizeof(cwd)) != NULL)
-  {
     printf("Working directory: %s\n", cwd);
-  }
 
   if (access(server_binary, X_OK) != 0)
   {
@@ -714,14 +402,11 @@
 
   pid_t server_pid = start_test_server(server_binary);
   if (server_pid < 0)
-  {
     return -1;
-  }
 
   int failed_tests = 0;
   int passed_tests = 0;
 
-  // Define test cases - paths that should succeed (200 OK)
   TestCase success_tests[] = {
     {"/", 200, NULL, NULL, NULL, 0},
     {"/resume", 200, NULL, NULL, NULL, 0},
@@ -731,7 +416,6 @@
   };
   int num_success_tests = sizeof(success_tests) / sizeof(success_tests[0]);
 
-  // Define test cases - paths that should redirect (301)
   TestCase redirect_tests[] = {
     {"/index.html", 301, NULL, NULL, NULL, 0},
     {"/resume/index.html", 301, NULL, NULL, NULL, 0},
@@ -741,7 +425,6 @@
   };
   int num_redirect_tests = sizeof(redirect_tests) / sizeof(redirect_tests[0]);
 
-  // Define test cases - paths that should fail (404)
   TestCase failure_tests[] = {
     {"/nonexistent", 404, NULL, NULL, NULL, 0},
     {"/does/not/exist", 404, NULL, NULL, NULL, 0},
@@ -749,134 +432,73 @@
   };
   int num_failure_tests = sizeof(failure_tests) / sizeof(failure_tests[0]);
 
-  // Initialize all test cases
   for (int i = 0; i < num_success_tests; i++)
-  {
     init_test_case(&success_tests[i]);
-  }
+
   for (int i = 0; i < num_redirect_tests; i++)
-  {
     init_test_case(&redirect_tests[i]);
-  }
+
   for (int i = 0; i < num_failure_tests; i++)
-  {
     init_test_case(&failure_tests[i]);
-  }
 
-  // Run success tests
   printf("Running tests for paths that should succeed:\n");
   for (int i = 0; i < num_success_tests; i++)
   {
     if (execute_test_case(&success_tests[i], server_pid) == 0)
-    {
       passed_tests++;
-    }
     else
-    {
       failed_tests++;
-    }
   }
 
   printf("\n");
 
-  // Run redirect tests
   printf("Running tests for paths that should redirect:\n");
   for (int i = 0; i < num_redirect_tests; i++)
   {
     if (execute_test_case(&redirect_tests[i], server_pid) == 0)
-    {
       passed_tests++;
-    }
     else
-    {
       failed_tests++;
-    }
-  }
-
-  printf("\n");
-
-  // Run failure tests
-  printf("Running tests for paths that should fail:\n");
-  for (int i = 0; i < num_failure_tests; i++)
-  {
-    if (execute_test_case(&failure_tests[i], server_pid) == 0)
-    {
-      passed_tests++;
-    }
-    else
-    {
-      failed_tests++;
-    }
   }
 
   printf("\n");
 
-  // Test with custom request
-  printf("Running tests with custom requests:\n");
-  char custom_request[4096];
-  snprintf(custom_request, sizeof(custom_request),
-           "GET / HTTP/1.1\r\n"
-           "Host: %s\r\n"
-           "Connection: close\r\n"
-           "X-Custom-Header: TestValue\r\n"
-           "\r\n",
-           TEST_HOST);
-
-  if (execute_custom_request_test("Custom headers GET /", custom_request, 200, server_pid) == 0)
+  printf("Running tests for paths that should fail:\n");
+  for (int i = 0; i < num_failure_tests; i++)
   {
-    passed_tests++;
-  }
-  else
-  {
-    failed_tests++;
+    if (execute_test_case(&failure_tests[i], server_pid) == 0)
+      passed_tests++;
+    else
+      failed_tests++;
   }
 
   printf("\n");
 
-  // Test POST endpoints
   printf("Running tests for POST conversion endpoints:\n");
-
-  // Test image-to-webp conversion
   if (test_file_conversion("/api/convert/image-to-webp",
                            "mrjunejune/test/shiba.webp",
                            "image/webp",
                            server_pid) == 0)
-  {
     passed_tests++;
-  }
   else
-  {
     failed_tests++;
-  }
 
   printf("\n");
 
-  // Test video-to-mp4 conversion
   if (test_file_conversion("/api/convert/video-to-mp4",
                            "mrjunejune/test/test_avi.avi",
                            "video/mp4",
                            server_pid) == 0)
-  {
     passed_tests++;
-  }
   else
-  {
     failed_tests++;
-  }
 
-  // Cleanup test cases
   for (int i = 0; i < num_success_tests; i++)
-  {
     cleanup_test_case(&success_tests[i]);
-  }
   for (int i = 0; i < num_redirect_tests; i++)
-  {
     cleanup_test_case(&redirect_tests[i]);
-  }
   for (int i = 0; i < num_failure_tests; i++)
-  {
     cleanup_test_case(&failure_tests[i]);
-  }
 
   stop_test_server(server_pid);
 
@@ -893,20 +515,14 @@
 
   const char *server_binary = "./mrjunejune_server";
   if (argc > 1)
-  {
     server_binary = argv[1];
-  }
 
   int result = test_server_client_integration(server_binary);
 
   if (result == 0)
-  {
     printf("\n✓ All tests passed!\n");
-  }
   else
-  {
     printf("\n✗ Some tests failed\n");
-  }
 
   return result;
 }
--- a/mrjunejune/test/snapshots/resume.snapshot	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/snapshots/resume.snapshot	Thu Jan 08 18:03:34 2026 -0800
@@ -1,6 +1,6 @@
 HTTP/1.1 200 OK
 Content-Type: text/html
-Content-Length: 14484
+Content-Length: 14511
 Connection: close
 
 <!doctype html>
@@ -13,10 +13,10 @@
 <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font"  crossorigin>
 <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin>
 
-<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin>
-<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin> -->
+<!-- <link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> -->
 
-<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> -->
 <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin>
 <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
 
--- a/mrjunejune/test/snapshots/resume_index.html.snapshot	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/snapshots/resume_index.html.snapshot	Thu Jan 08 18:03:34 2026 -0800
@@ -3,5 +3,4 @@
 Content-Length: 0
 Connection: close
 Body: 
-Location: /resume
 
--- a/mrjunejune/test/snapshots/root.snapshot	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/snapshots/root.snapshot	Thu Jan 08 18:03:34 2026 -0800
@@ -1,6 +1,6 @@
 HTTP/1.1 200 OK
 Content-Type: text/html
-Content-Length: 5723
+Content-Length: 5750
 Connection: close
 
 <!doctype html>
@@ -14,10 +14,10 @@
 <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font"  crossorigin>
 <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin>
 
-<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin>
-<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin> -->
+<!-- <link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> -->
 
-<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> -->
 <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin>
 <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
 
--- a/mrjunejune/test/snapshots/tools.snapshot	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/snapshots/tools.snapshot	Thu Jan 08 18:03:34 2026 -0800
@@ -1,6 +1,6 @@
 HTTP/1.1 200 OK
 Content-Type: text/html
-Content-Length: 4246
+Content-Length: 4273
 Connection: close
 
 <!doctype html>
@@ -13,10 +13,10 @@
 <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font"  crossorigin>
 <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin>
 
-<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin>
-<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin> -->
+<!-- <link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> -->
 
-<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> -->
 <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin>
 <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
 
--- a/mrjunejune/test/snapshots/tools_file_converter.snapshot	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_file_converter.snapshot	Thu Jan 08 18:03:34 2026 -0800
@@ -1,6 +1,6 @@
 HTTP/1.1 200 OK
 Content-Type: text/html
-Content-Length: 8526
+Content-Length: 8553
 Connection: close
 
 <!DOCTYPE html>
@@ -16,10 +16,10 @@
 <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font"  crossorigin>
 <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin>
 
-<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin>
-<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin> -->
+<!-- <link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> -->
 
-<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> -->
 <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin>
 <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
 
--- a/mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot	Thu Jan 08 18:03:34 2026 -0800
@@ -4,444 +4,3 @@
 Connection: close
 Body: 
 
-"font/woff" crossorigin>
-<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin>
-
-<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin>
-<link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin>
-<link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
-
-<link rel="preload" href="/base.css" as="style" />
-<link rel="stylesheet" href="/base.css" />
-
-
-    <link rel="stylesheet" href="markdown_to_html/index.css" />
-</head>
-<body>
-    <style>
-  :root {
-    --header-background: var(--white);
-    --header-color: rgb(var(--black));
-    --link-hover-accent: var(--awesome);
-  }
-
-  /* Fixed icon in top left corner */
-  #themeToggle {
-    position: fixed;
-    top: 20px;
-    left: 20px;
-    background: var(--header-background);
-    display: flex;
-    align-items: center;
-    border-radius: 50%;
-    cursor: pointer;
-    z-index: 1000;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-    transition: transform 0.2s ease;
-  }
-
-  #themeToggle:hover {
-    transform: scale(1.05);
-  }
-
-  /* Professional header */
-  header {
-    margin: auto;
-    padding: 1.5em 1em;
-    font-family: "More", sans-serif;
-    box-shadow: 0 2px 8px rgba(var(--black), 5%);
-    width: 720px;
-    max-width: calc(100% - 2em);
-    text-align: center;
-  }
-
-  header h1 {
-    margin: 0;
-    font-size: 1.8em;
-    font-weight: 700;
-    letter-spacing: -0.5px;
-  }
-
-  header h1 a {
-    text-decoration: none;
-    color: var(--header-color);
-  }
-
-  header h1 a::before {
-    display: none;
-  }
-
-  /* Mobile responsiveness */
-  @media (max-width: 720px) {
-    #themeToggle {
-      top: 15px;
-      left: 15px;
-    }
-
-    header {
-      padding: 1em;
-    }
-
-    header h1 {
-      font-size: 1.5em;
-    }
-  }
-
-  @media (max-width: 480px) {
-    #themeToggle {
-      top: 10px;
-      left: 10px;
-    }
-
-    #themeToggle img {
-      height: 40px;
-      width: 40px;
-    }
-
-    header h1 {
-      font-size: 1.3em;
-    }
-  }
-
-  #logo {
-    width: 300px;
-  }
-
-  /* 1. DEFINE THE DEFAULTS (Light Mode) */
-  :root {
-    --logo-invert: invert(0);
-    --epi-grayscale: grayscale(0) brightness(1);
-  }
-  
-  /* 2. MANUAL DARK OVERRIDE */
-  html.dark {
-    --logo-invert: invert(1);
-    --epi-grayscale: grayscale(1);
-  }
-  
-  /* 3. MANUAL LIGHT OVERRIDE */
-  html.light-mode {
-    --logo-invert: invert(0);
-    --epi-grayscale: brightness(2.9) grayscale(1);
-  }
-  
-  /* 4. SYSTEM PREFERENCE */
-  @media (prefers-color-scheme: dark) {
-    :root:not(.light-mode) {
-      --logo-invert: invert(1);
-    }
-  }
-  
-  /* 5. APPLY TO ELEMENTS */
-  #logo {
-    -webkit-filter: var(--logo-invert);
-    filter: var(--logo-invert);
-    transition: filter 0.3s ease;
-  }
-  
-  .epi-logo {
-    -webkit-filter: var(--epi-grayscale);
-    filter: var(--epi-grayscale);
-    transition: filter 0.3s ease;
-  }
-</style>
-
-<div id="themeToggle">
-  <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50">
-</div>
-
-<header>
-  <h1><a href="/">MrJuneJune</a></h1>
-</header>
-<script src="/index.js"></script>
-
-
-    <div class="header">
-        <h1>Markdown to HTML Converter</h1>
-    </div>
-    
-    <div class="container">
-        <div class="panel">
-            <div class="label">Markdown Input</div>
-            <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter
-
-## Features
-
-This converter supports:
-
-- **Bold text** and *italic text*
-- [Links](https://example.com)
-- `inline code`
-- ~~strikethrough~~
-
-### Lists
-
-1. Ordered lists
-2. Like this one
-3. With numbers
-
-- Unordered lists
-- Use dashes
-- Or asterisks
-
-### Code Blocks
-
-```
-function example() {
-    return "Hello World";
-}
-```
-
-### Blockquotes
-
-> This is a blockquote
-> It can span multiple lines
-
----
-
-### Images
-
-![Alt text](https://via.placeholder.com/150)
-
-**Try editing this text!**</textarea>
-        </div>
-        
-        <div class="panel">
-            <div class="title">
-              <div class="label">HTML Output</div>
-              <button id="copy"> Copy </button>
-            </div>
-            <div id="output"></div>
-        </div>
-    </div>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script src="/markdown_to_html.js"></script>
-    <script>
-        function convert() {
-          output.innerHTML = '';
-          const markdown = input.value;
-          renderMarkdown(output, markdown);
-        }
-        input.addEventListener('input', convert);
-        
-        convert();
-
-        copy.addEventListener('click', () => {
-          const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'});
-          const textBlob = new Blob([output.innerText], { type: 'text/plain'});
-          const data = [new ClipboardItem({
-            'text/html': htmlBlob,
-            'text/plain': textBlob
-          })];
-          navigator.clipboard.write(data).then(() => {
-            copy.textContent = "Copied!";
-            setTimeout(() => {
-              copy.textContent = "Copy";
-              copy.classList.remove('success');
-            }, 1000);
-          }).catch(err => {
-            console.error('Failed to copy: ', err);
-          });
-        });
-    </script>
-</body>
-</htmlLocation: /tools
-
-
-          <span class="entry-title-style">Tools & Platforms:</span>
-          <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Web Frameworks: </span>
-          <span class="skill-type-style"> Django, Rails, React, Flask</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> DevOp:</span>
-          <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Language:</span>
-          <span class="skill-type-style"> English, Korean, Japanese </span>
-        </p>
-      </div>
-
-      <!-- Experiences -->
-     <div class="sub-header">
-        <h2 class="section-style"> Experience </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.meta.com/">Meta</a>
-        </p>
-        <p class="entry-location-style">San Francisco, CA, USA</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Oct, 2024 - Present</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI.
-        </li>
-        <li>
-          Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems.
-        </li>
-        <li>
-          Partnered with data science to design A/B tests and analyze revenue impact of ads destination.
-        </li>
-        <li>
-          Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity.
-        </li>
-      </ul>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.wmg.com/">Warner Music Group</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p>
-        <p class="entry-date-style">July, 2023 - Sept, 2024</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and
-        </li>
-        <li>
-          TypeScript testing and code structures.
-        </li>
-        <li>
-          Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values.
-        </li>
-        <li>
-          Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes.
-        </li>
-        <li>
-          Developed CI/CD pipelines for backend structures.
-        </li>
-        <li>
-          Designed infrastructure for pub/sub, caching, and media processing logic.
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.google.com/">Google</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Feb, 2022 - July 2023</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li>
-        <li>
-          Improved a response time and render time of App Script hover card components.</li>
-        <li>
-          Collaborated with a team of developers to ensure timely and accurate delivery of features.</li>
-        <li>
-          Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.everlywell.com/">Everlywell</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">December, 2020 - Jan, 2022</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li>
-        <li>
-          Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li>
-        <li>
-          Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li>
-        <li>
-          Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.spiria.com/">Spiria</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">October, 2018 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger.
-        </li>
-        <li>
-          Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs.
-        </li>
-        <li>
-          Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers
-        </li>
-        <li>
-          Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.apexscore.ai/">Apex Score</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">September, 2019 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy.
-        </li>
-        <li>
-          Created custom image uploader to Amazon s3 bucket using boto3 library.
-        </li>
-        <li>
-          Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base.
-        </li>
-        <li>
-          Created an interactive graph using D3.js in Vue.js with data from Flask backend API. 
-        </li>
-      </ul>
-
-     <div class="sub-header">
-        <h2 class="section-style"> Education </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          University of British Columbia
-        </p>
-        <p class="entry-location-style">Kelowna, British Columbia</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p>
-        <p class="entry-date-style">2014 - 2018</p>
-      </div>
-      <div id="footer"></div>
-    </main>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script href="index.js"></script>
-  </body>
-</htmlLocation: /tools/markdown_to_html
-Location: /tools/file_converter
-
--- a/mrjunejune/test/snapshots/tools_index.html.snapshot	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_index.html.snapshot	Thu Jan 08 18:03:34 2026 -0800
@@ -4,442 +4,3 @@
 Connection: close
 Body: 
 
-"font/woff" crossorigin>
-<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin>
-
-<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin>
-<link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin>
-<link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
-
-<link rel="preload" href="/base.css" as="style" />
-<link rel="stylesheet" href="/base.css" />
-
-
-    <link rel="stylesheet" href="markdown_to_html/index.css" />
-</head>
-<body>
-    <style>
-  :root {
-    --header-background: var(--white);
-    --header-color: rgb(var(--black));
-    --link-hover-accent: var(--awesome);
-  }
-
-  /* Fixed icon in top left corner */
-  #themeToggle {
-    position: fixed;
-    top: 20px;
-    left: 20px;
-    background: var(--header-background);
-    display: flex;
-    align-items: center;
-    border-radius: 50%;
-    cursor: pointer;
-    z-index: 1000;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-    transition: transform 0.2s ease;
-  }
-
-  #themeToggle:hover {
-    transform: scale(1.05);
-  }
-
-  /* Professional header */
-  header {
-    margin: auto;
-    padding: 1.5em 1em;
-    font-family: "More", sans-serif;
-    box-shadow: 0 2px 8px rgba(var(--black), 5%);
-    width: 720px;
-    max-width: calc(100% - 2em);
-    text-align: center;
-  }
-
-  header h1 {
-    margin: 0;
-    font-size: 1.8em;
-    font-weight: 700;
-    letter-spacing: -0.5px;
-  }
-
-  header h1 a {
-    text-decoration: none;
-    color: var(--header-color);
-  }
-
-  header h1 a::before {
-    display: none;
-  }
-
-  /* Mobile responsiveness */
-  @media (max-width: 720px) {
-    #themeToggle {
-      top: 15px;
-      left: 15px;
-    }
-
-    header {
-      padding: 1em;
-    }
-
-    header h1 {
-      font-size: 1.5em;
-    }
-  }
-
-  @media (max-width: 480px) {
-    #themeToggle {
-      top: 10px;
-      left: 10px;
-    }
-
-    #themeToggle img {
-      height: 40px;
-      width: 40px;
-    }
-
-    header h1 {
-      font-size: 1.3em;
-    }
-  }
-
-  #logo {
-    width: 300px;
-  }
-
-  /* 1. DEFINE THE DEFAULTS (Light Mode) */
-  :root {
-    --logo-invert: invert(0);
-    --epi-grayscale: grayscale(0) brightness(1);
-  }
-  
-  /* 2. MANUAL DARK OVERRIDE */
-  html.dark {
-    --logo-invert: invert(1);
-    --epi-grayscale: grayscale(1);
-  }
-  
-  /* 3. MANUAL LIGHT OVERRIDE */
-  html.light-mode {
-    --logo-invert: invert(0);
-    --epi-grayscale: brightness(2.9) grayscale(1);
-  }
-  
-  /* 4. SYSTEM PREFERENCE */
-  @media (prefers-color-scheme: dark) {
-    :root:not(.light-mode) {
-      --logo-invert: invert(1);
-    }
-  }
-  
-  /* 5. APPLY TO ELEMENTS */
-  #logo {
-    -webkit-filter: var(--logo-invert);
-    filter: var(--logo-invert);
-    transition: filter 0.3s ease;
-  }
-  
-  .epi-logo {
-    -webkit-filter: var(--epi-grayscale);
-    filter: var(--epi-grayscale);
-    transition: filter 0.3s ease;
-  }
-</style>
-
-<div id="themeToggle">
-  <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50">
-</div>
-
-<header>
-  <h1><a href="/">MrJuneJune</a></h1>
-</header>
-<script src="/index.js"></script>
-
-
-    <div class="header">
-        <h1>Markdown to HTML Converter</h1>
-    </div>
-    
-    <div class="container">
-        <div class="panel">
-            <div class="label">Markdown Input</div>
-            <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter
-
-## Features
-
-This converter supports:
-
-- **Bold text** and *italic text*
-- [Links](https://example.com)
-- `inline code`
-- ~~strikethrough~~
-
-### Lists
-
-1. Ordered lists
-2. Like this one
-3. With numbers
-
-- Unordered lists
-- Use dashes
-- Or asterisks
-
-### Code Blocks
-
-```
-function example() {
-    return "Hello World";
-}
-```
-
-### Blockquotes
-
-> This is a blockquote
-> It can span multiple lines
-
----
-
-### Images
-
-![Alt text](https://via.placeholder.com/150)
-
-**Try editing this text!**</textarea>
-        </div>
-        
-        <div class="panel">
-            <div class="title">
-              <div class="label">HTML Output</div>
-              <button id="copy"> Copy </button>
-            </div>
-            <div id="output"></div>
-        </div>
-    </div>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script src="/markdown_to_html.js"></script>
-    <script>
-        function convert() {
-          output.innerHTML = '';
-          const markdown = input.value;
-          renderMarkdown(output, markdown);
-        }
-        input.addEventListener('input', convert);
-        
-        convert();
-
-        copy.addEventListener('click', () => {
-          const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'});
-          const textBlob = new Blob([output.innerText], { type: 'text/plain'});
-          const data = [new ClipboardItem({
-            'text/html': htmlBlob,
-            'text/plain': textBlob
-          })];
-          navigator.clipboard.write(data).then(() => {
-            copy.textContent = "Copied!";
-            setTimeout(() => {
-              copy.textContent = "Copy";
-              copy.classList.remove('success');
-            }, 1000);
-          }).catch(err => {
-            console.error('Failed to copy: ', err);
-          });
-        });
-    </script>
-</body>
-</htmlLocation: /tools
-
-
-          <span class="entry-title-style">Tools & Platforms:</span>
-          <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Web Frameworks: </span>
-          <span class="skill-type-style"> Django, Rails, React, Flask</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> DevOp:</span>
-          <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Language:</span>
-          <span class="skill-type-style"> English, Korean, Japanese </span>
-        </p>
-      </div>
-
-      <!-- Experiences -->
-     <div class="sub-header">
-        <h2 class="section-style"> Experience </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.meta.com/">Meta</a>
-        </p>
-        <p class="entry-location-style">San Francisco, CA, USA</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Oct, 2024 - Present</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI.
-        </li>
-        <li>
-          Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems.
-        </li>
-        <li>
-          Partnered with data science to design A/B tests and analyze revenue impact of ads destination.
-        </li>
-        <li>
-          Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity.
-        </li>
-      </ul>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.wmg.com/">Warner Music Group</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p>
-        <p class="entry-date-style">July, 2023 - Sept, 2024</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and
-        </li>
-        <li>
-          TypeScript testing and code structures.
-        </li>
-        <li>
-          Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values.
-        </li>
-        <li>
-          Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes.
-        </li>
-        <li>
-          Developed CI/CD pipelines for backend structures.
-        </li>
-        <li>
-          Designed infrastructure for pub/sub, caching, and media processing logic.
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.google.com/">Google</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Feb, 2022 - July 2023</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li>
-        <li>
-          Improved a response time and render time of App Script hover card components.</li>
-        <li>
-          Collaborated with a team of developers to ensure timely and accurate delivery of features.</li>
-        <li>
-          Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.everlywell.com/">Everlywell</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">December, 2020 - Jan, 2022</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li>
-        <li>
-          Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li>
-        <li>
-          Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li>
-        <li>
-          Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.spiria.com/">Spiria</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">October, 2018 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger.
-        </li>
-        <li>
-          Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs.
-        </li>
-        <li>
-          Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers
-        </li>
-        <li>
-          Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.apexscore.ai/">Apex Score</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">September, 2019 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy.
-        </li>
-        <li>
-          Created custom image uploader to Amazon s3 bucket using boto3 library.
-        </li>
-        <li>
-          Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base.
-        </li>
-        <li>
-          Created an interactive graph using D3.js in Vue.js with data from Flask backend API. 
-        </li>
-      </ul>
-
-     <div class="sub-header">
-        <h2 class="section-style"> Education </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          University of British Columbia
-        </p>
-        <p class="entry-location-style">Kelowna, British Columbia</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p>
-        <p class="entry-date-style">2014 - 2018</p>
-      </div>
-      <div id="footer"></div>
-    </main>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script href="index.js"></script>
-  </body>
-</html>
--- a/mrjunejune/test/snapshots/tools_markdown_to_html.snapshot	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_markdown_to_html.snapshot	Thu Jan 08 18:03:34 2026 -0800
@@ -1,6 +1,6 @@
 HTTP/1.1 200 OK
 Content-Type: text/html
-Content-Length: 5985
+Content-Length: 6012
 Connection: close
 
 <!DOCTYPE html>
@@ -16,10 +16,10 @@
 <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font"  crossorigin>
 <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin>
 
-<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin>
-<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin> -->
+<!-- <link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> -->
 
-<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> -->
 <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin>
 <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
 
--- a/mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot	Thu Jan 08 08:46:49 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot	Thu Jan 08 18:03:34 2026 -0800
@@ -4,443 +4,3 @@
 Connection: close
 Body: 
 
-"font/woff" crossorigin>
-<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin>
-
-<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin>
-<link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin>
-<link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
-
-<link rel="preload" href="/base.css" as="style" />
-<link rel="stylesheet" href="/base.css" />
-
-
-    <link rel="stylesheet" href="markdown_to_html/index.css" />
-</head>
-<body>
-    <style>
-  :root {
-    --header-background: var(--white);
-    --header-color: rgb(var(--black));
-    --link-hover-accent: var(--awesome);
-  }
-
-  /* Fixed icon in top left corner */
-  #themeToggle {
-    position: fixed;
-    top: 20px;
-    left: 20px;
-    background: var(--header-background);
-    display: flex;
-    align-items: center;
-    border-radius: 50%;
-    cursor: pointer;
-    z-index: 1000;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-    transition: transform 0.2s ease;
-  }
-
-  #themeToggle:hover {
-    transform: scale(1.05);
-  }
-
-  /* Professional header */
-  header {
-    margin: auto;
-    padding: 1.5em 1em;
-    font-family: "More", sans-serif;
-    box-shadow: 0 2px 8px rgba(var(--black), 5%);
-    width: 720px;
-    max-width: calc(100% - 2em);
-    text-align: center;
-  }
-
-  header h1 {
-    margin: 0;
-    font-size: 1.8em;
-    font-weight: 700;
-    letter-spacing: -0.5px;
-  }
-
-  header h1 a {
-    text-decoration: none;
-    color: var(--header-color);
-  }
-
-  header h1 a::before {
-    display: none;
-  }
-
-  /* Mobile responsiveness */
-  @media (max-width: 720px) {
-    #themeToggle {
-      top: 15px;
-      left: 15px;
-    }
-
-    header {
-      padding: 1em;
-    }
-
-    header h1 {
-      font-size: 1.5em;
-    }
-  }
-
-  @media (max-width: 480px) {
-    #themeToggle {
-      top: 10px;
-      left: 10px;
-    }
-
-    #themeToggle img {
-      height: 40px;
-      width: 40px;
-    }
-
-    header h1 {
-      font-size: 1.3em;
-    }
-  }
-
-  #logo {
-    width: 300px;
-  }
-
-  /* 1. DEFINE THE DEFAULTS (Light Mode) */
-  :root {
-    --logo-invert: invert(0);
-    --epi-grayscale: grayscale(0) brightness(1);
-  }
-  
-  /* 2. MANUAL DARK OVERRIDE */
-  html.dark {
-    --logo-invert: invert(1);
-    --epi-grayscale: grayscale(1);
-  }
-  
-  /* 3. MANUAL LIGHT OVERRIDE */
-  html.light-mode {
-    --logo-invert: invert(0);
-    --epi-grayscale: brightness(2.9) grayscale(1);
-  }
-  
-  /* 4. SYSTEM PREFERENCE */
-  @media (prefers-color-scheme: dark) {
-    :root:not(.light-mode) {
-      --logo-invert: invert(1);
-    }
-  }
-  
-  /* 5. APPLY TO ELEMENTS */
-  #logo {
-    -webkit-filter: var(--logo-invert);
-    filter: var(--logo-invert);
-    transition: filter 0.3s ease;
-  }
-  
-  .epi-logo {
-    -webkit-filter: var(--epi-grayscale);
-    filter: var(--epi-grayscale);
-    transition: filter 0.3s ease;
-  }
-</style>
-
-<div id="themeToggle">
-  <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50">
-</div>
-
-<header>
-  <h1><a href="/">MrJuneJune</a></h1>
-</header>
-<script src="/index.js"></script>
-
-
-    <div class="header">
-        <h1>Markdown to HTML Converter</h1>
-    </div>
-    
-    <div class="container">
-        <div class="panel">
-            <div class="label">Markdown Input</div>
-            <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter
-
-## Features
-
-This converter supports:
-
-- **Bold text** and *italic text*
-- [Links](https://example.com)
-- `inline code`
-- ~~strikethrough~~
-
-### Lists
-
-1. Ordered lists
-2. Like this one
-3. With numbers
-
-- Unordered lists
-- Use dashes
-- Or asterisks
-
-### Code Blocks
-
-```
-function example() {
-    return "Hello World";
-}
-```
-
-### Blockquotes
-
-> This is a blockquote
-> It can span multiple lines
-
----
-
-### Images
-
-![Alt text](https://via.placeholder.com/150)
-
-**Try editing this text!**</textarea>
-        </div>
-        
-        <div class="panel">
-            <div class="title">
-              <div class="label">HTML Output</div>
-              <button id="copy"> Copy </button>
-            </div>
-            <div id="output"></div>
-        </div>
-    </div>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script src="/markdown_to_html.js"></script>
-    <script>
-        function convert() {
-          output.innerHTML = '';
-          const markdown = input.value;
-          renderMarkdown(output, markdown);
-        }
-        input.addEventListener('input', convert);
-        
-        convert();
-
-        copy.addEventListener('click', () => {
-          const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'});
-          const textBlob = new Blob([output.innerText], { type: 'text/plain'});
-          const data = [new ClipboardItem({
-            'text/html': htmlBlob,
-            'text/plain': textBlob
-          })];
-          navigator.clipboard.write(data).then(() => {
-            copy.textContent = "Copied!";
-            setTimeout(() => {
-              copy.textContent = "Copy";
-              copy.classList.remove('success');
-            }, 1000);
-          }).catch(err => {
-            console.error('Failed to copy: ', err);
-          });
-        });
-    </script>
-</body>
-</htmlLocation: /tools
-
-
-          <span class="entry-title-style">Tools & Platforms:</span>
-          <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Web Frameworks: </span>
-          <span class="skill-type-style"> Django, Rails, React, Flask</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> DevOp:</span>
-          <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Language:</span>
-          <span class="skill-type-style"> English, Korean, Japanese </span>
-        </p>
-      </div>
-
-      <!-- Experiences -->
-     <div class="sub-header">
-        <h2 class="section-style"> Experience </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.meta.com/">Meta</a>
-        </p>
-        <p class="entry-location-style">San Francisco, CA, USA</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Oct, 2024 - Present</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI.
-        </li>
-        <li>
-          Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems.
-        </li>
-        <li>
-          Partnered with data science to design A/B tests and analyze revenue impact of ads destination.
-        </li>
-        <li>
-          Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity.
-        </li>
-      </ul>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.wmg.com/">Warner Music Group</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p>
-        <p class="entry-date-style">July, 2023 - Sept, 2024</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and
-        </li>
-        <li>
-          TypeScript testing and code structures.
-        </li>
-        <li>
-          Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values.
-        </li>
-        <li>
-          Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes.
-        </li>
-        <li>
-          Developed CI/CD pipelines for backend structures.
-        </li>
-        <li>
-          Designed infrastructure for pub/sub, caching, and media processing logic.
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.google.com/">Google</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Feb, 2022 - July 2023</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li>
-        <li>
-          Improved a response time and render time of App Script hover card components.</li>
-        <li>
-          Collaborated with a team of developers to ensure timely and accurate delivery of features.</li>
-        <li>
-          Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.everlywell.com/">Everlywell</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">December, 2020 - Jan, 2022</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li>
-        <li>
-          Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li>
-        <li>
-          Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li>
-        <li>
-          Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.spiria.com/">Spiria</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">October, 2018 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger.
-        </li>
-        <li>
-          Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs.
-        </li>
-        <li>
-          Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers
-        </li>
-        <li>
-          Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.apexscore.ai/">Apex Score</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">September, 2019 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy.
-        </li>
-        <li>
-          Created custom image uploader to Amazon s3 bucket using boto3 library.
-        </li>
-        <li>
-          Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base.
-        </li>
-        <li>
-          Created an interactive graph using D3.js in Vue.js with data from Flask backend API. 
-        </li>
-      </ul>
-
-     <div class="sub-header">
-        <h2 class="section-style"> Education </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          University of British Columbia
-        </p>
-        <p class="entry-location-style">Kelowna, British Columbia</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p>
-        <p class="entry-date-style">2014 - 2018</p>
-      </div>
-      <div id="footer"></div>
-    </main>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script href="index.js"></script>
-  </body>
-</htmlLocation: /tools/markdown_to_html
-
--- a/playground/.gitignore	Thu Jan 08 08:46:49 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-# dependencies (bun install)
-node_modules
-
-# output
-out
-dist
-*.tgz
-
-# code coverage
-coverage
-*.lcov
-
-# logs
-logs
-_.log
-report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
-
-# dotenv environment variable files
-.env
-.env.development.local
-.env.test.local
-.env.production.local
-.env.local
-
-# caches
-.eslintcache
-.cache
-*.tsbuildinfo
-
-# IntelliJ based IDEs
-.idea
-
-# Finder (MacOS) folder config
-.DS_Store
--- a/playground/BUILD	Thu Jan 08 08:46:49 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
-load("//gui_ze:gui_ze.bzl", "bun_build")
-
-cc_binary(
-  name = "main",
-  srcs = ["main.c"],
-)
-
-filegroup(
-  name = "all_ts_files",
-  srcs = glob([
-      "**/*.ts",
-      "**/*.tsx",
-      "**/*.js",
-      "**/*.jsx",
-  ], allow_empty=True)
-)
-
-bun_build(
-  name = "hello",
-  src = "main.ts",
-  src_folder = "playground",
-  data = [
-    "//third_party/bun:bun_files",
-    ":all_ts_files",
-  ],
-  visibility = ["//visibility:public"],
-)
--- a/playground/README.md	Thu Jan 08 08:46:49 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-# playground
-
-To install dependencies:
-
-```bash
-bun install
-```
-
-To run:
-
-```bash
-bun run 
-```
-
-This project was created using `bun init` in bun v1.2.23. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
--- a/playground/foo/foo.ts	Thu Jan 08 08:46:49 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-export const foo = () => 10;
--- a/playground/hello.ts	Thu Jan 08 08:46:49 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-const JUNE = "JUNE";
-
-export {
-  JUNE,
-}
--- a/playground/june.tsx	Thu Jan 08 08:46:49 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-import react from "React"
-
-const Foo = () => {
-  return (
-    <> hehexd </>
-  );
-}
-
-export {
-  Foo,
-}
Binary file playground/main has changed
--- a/playground/main.c	Thu Jan 08 08:46:49 2026 -0800
+++ b/playground/main.c	Thu Jan 08 18:03:34 2026 -0800
@@ -1,42 +1,65 @@
+#include <stdlib.h>
 #include <stdio.h>
-#include <stdlib.h>
-
-#define REPO_ROOT "/Users/mrjunejune/zenbu"
+#include <string.h>
 
-int main()
-{
-  char command[512];
-  snprintf(command, sizeof(command), "hg -R %s serve --stdio", REPO_ROOT);
-  printf("command: %s\n", command);
-  
-  FILE *hg_pipe = popen(command, "r+");
-  if (!hg_pipe)
-  {
-    printf("Failed to open pipe\n");
-    return -1;
-  }
+int main() {
+    char *input[5] = {"Hello ", "Foo <<E", "N", "DD>", "Park <END> "};
+    char *key = "<END>";
+    int key_len = strlen(key);
 
-  fprintf(hg_pipe, "capabilities\n");
-  fflush(hg_pipe);
+    char **buffers = malloc(sizeof(char *) * 100);
+    int buffer_index = 0;
+    int key_ptr = 0;
+    for (int i = 0; i < 5; i++) {
+        char *packet = input[i];
+        int p_len = strlen(packet);
 
-  char *output = malloc(sizeof(char) * 2048);
-  char *curr = output;
-  int c;
-  int number_of_breakline = 0;
-  while ((c = fgetc(hg_pipe))  != NULL)
-  {
-    *curr++ = c;
-    printf("output: %s\n", output);
-    if (c == '\n')
-      number_of_breakline++;
-    if (number_of_breakline == 2)
-      break;
-    printf("char: %c\n", c);
-  }
-  pclose(hg_pipe);
+        for (int j = 0; j < p_len; j++) {
+            if (packet[j] == key[key_ptr]) {
+                key_ptr++;
+                
+                // If the WHOLE keyword is found
+                if (key_ptr == key_len) {
+                    // 1. Print all previous "safe" buffers
+                    // (The ones before the one where the keyword started)
+                    for (int b = 0; b < buffer_index; b++)
+                        printf("%s", buffers[b]);
+                    
+                    // 2. Handle the "Current" packet truncation
+                    // Calculate where the match started in THIS packet
+                    // If key_ptr was satisfied across multiple packets, 
+                    // 'j' is the end of the match in the current packet.
+                    int match_end_in_packet = j + 1;
+                    int match_len_in_this_packet = (key_ptr <= match_end_in_packet) ? key_ptr : match_end_in_packet;
+                    
+                    printf("%.*s\n", (match_end_in_packet - match_len_in_this_packet), packet);
+                    
+                    free(buffers);
+                    return 0;
+                }
+            } else {
+                // If a match fails, we must "flush" the buffers we were holding
+                if (key_ptr > 0) {
+                    for (int b = 0; b < buffer_index; b++) printf("%s", buffers[b]);
+                    buffer_index = 0;
+                    
+                    if (packet[j] == key[0]) key_ptr = 1;
+                    else {
+                        printf("%c", packet[j]);
+                        key_ptr = 0;
+                    }
+                } else {
+                    printf("%c", packet[j]);
+                }
+            }
+        }
 
+        // If we finish a packet and we are in the middle of a match, buffer it
+        if (key_ptr > 0) {
+            buffers[buffer_index++] = packet;
+        }
+    }
 
-  return 0;
+    free(buffers);
+    return 0;
 }
-
-
--- a/playground/main.ts	Thu Jan 08 08:46:49 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-import { Foo } from "./june";
-import ReactDOM from 'react-dom/client';
-
-ReactDOM.createRoot(document.getElementById('root')!).render(Foo());
-
--- a/playground/new.txt	Thu Jan 08 08:46:49 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-hello good sir
--- a/seobeo/BUILD	Thu Jan 08 08:46:49 2026 -0800
+++ b/seobeo/BUILD	Thu Jan 08 18:03:34 2026 -0800
@@ -1,12 +1,13 @@
 load("@rules_cc//cc:cc_library.bzl", "cc_library")
 
-# File group 
+# File group
 filegroup(
   name = "seobeo_hdrs",
   srcs = [
     "seobeo.h",
     "seobeo_internal.h",
     "snapshot_creator.h",
+    "s_test_generator.h",
   ],
   visibility = ["//visibility:public"],
 )
@@ -353,3 +354,63 @@
   actual = ":seobeo",
   visibility = ["//visibility:public"],
 )
+# ============================================================================
+# Testing Utilities
+# ============================================================================
+
+# Testing library with snapshot and test generation tools
+alias(
+  name = "seobeo_test",
+  actual = select({
+    "//config:macos":  ":seobeo_test_macos",
+    "//config:linux":  ":seobeo_test_linux",
+    "//conditions:default": ":seobeo_test_linux",
+  }),
+  visibility = ["//visibility:public"],
+)
+
+cc_library(
+  name = "seobeo_test_macos",
+  srcs = [
+    "snapshot_creator.c",
+    "s_test_generator.c",
+  ],
+  hdrs = [
+    "snapshot_creator.h",
+    "s_test_generator.h",
+  ],
+  deps = [
+    ":seobeo_tcp_client_macos",
+  ],
+  target_compatible_with = [
+    "@platforms//os:osx",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+cc_library(
+  name = "seobeo_test_linux",
+  srcs = [
+    "snapshot_creator.c",
+    "s_test_generator.c",
+  ],
+  hdrs = [
+    "snapshot_creator.h",
+    "s_test_generator.h",
+  ],
+  deps = [
+    ":seobeo_tcp_client_linux",
+  ],
+  target_compatible_with = [
+    "@platforms//os:linux",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+# Test generator tool - generates test files from route definitions
+cc_binary(
+  name = "test_generator",
+  srcs = ["tools/test_generator_main.c"],
+  deps = [":seobeo_test"],
+  visibility = ["//visibility:public"],
+)
--- a/seobeo/snapshot_creator.c	Thu Jan 08 08:46:49 2026 -0800
+++ b/seobeo/snapshot_creator.c	Thu Jan 08 18:03:34 2026 -0800
@@ -1,13 +1,11 @@
 #include "seobeo/snapshot_creator.h"
+#include "seobeo/seobeo_internal.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
-#define MAX_RESPONSE_SIZE (1024 * 1024)
-
-// Helper: Convert URL path to filename
 static void path_to_filename(const char *path, char *filename, size_t max_len)
 {
   if (strcmp(path, "/") == 0)
@@ -18,9 +16,7 @@
 
   const char *p = path;
   if (*p == '/')
-  {
     p++;
-  }
 
   char *out = filename;
   size_t remaining = max_len - 1;
@@ -69,68 +65,61 @@
   return mkdir(tmp, 0755);
 }
 
-// Helper: Generate HTTP GET request
-static int generate_http_get(char *buffer, size_t size, const char *path, const char *host)
+// Helper: Build full HTTP response from Seobeo_Client_Response
+static char* build_full_response(Seobeo_Client_Response *p_resp, size_t *len_out)
 {
-  return snprintf(
-    buffer, size,
-    "GET %s HTTP/1.1\r\n"
-    "Host: %s\r\n"
-    "Connection: close\r\n"
-    "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
-    "User-Agent: SeobeoSnapshotCreator/1.0\r\n"
-    "\r\n",
-    path, host
-  );
-}
-
-// Helper: Read full HTTP response
-static char* read_full_response(Seobeo_Handle *client, size_t *len_out)
-{
-  char *response = malloc(MAX_RESPONSE_SIZE);
-  if (!response)
-  {
+  if (!p_resp)
     return NULL;
-  }
-
-  size_t total = 0;
-  int attempts = 0;
-  const int max_attempts = 100;
 
-  while (attempts++ < max_attempts && total < MAX_RESPONSE_SIZE - 1)
-  {
-    int n = Seobeo_Handle_Read(client);
-
-    if (n > 0)
-    {
-      size_t to_copy = client->read_buffer_len;
-      if (total + to_copy > MAX_RESPONSE_SIZE - 1)
-      {
-        to_copy = MAX_RESPONSE_SIZE - 1 - total;
-      }
+  // Calculate total response size
+  size_t status_line_len = 100; // "HTTP/1.1 200 OK\r\n"
+  size_t headers_len = 0;
+  size_t body_len = p_resp->body_length;
 
-      memcpy(response + total, client->read_buffer, to_copy);
-      total += to_copy;
-      Seobeo_Handle_Consume(client, (uint32)to_copy);
-    }
-    else if (n == -2)
+  // Estimate header size
+  if (p_resp->headers)
+  {
+    size_t header_count = Dowa_Array_Length(p_resp->headers);
+    for (size_t i = 0; i < header_count; i++)
     {
-      break;
-    }
-    else if (n == 0)
-    {
-      usleep(10000);
-      continue;
-    }
-    else
-    {
-      free(response);
-      return NULL;
+      headers_len += strlen(p_resp->headers[i].key) + strlen(p_resp->headers[i].value) + 4; // ": " + "\r\n"
     }
   }
 
-  response[total] = '\0';
-  *len_out = total;
+  size_t total_size = status_line_len + headers_len + 2 + body_len + 1; // +2 for "\r\n", +1 for null terminator
+  char *response = malloc(total_size);
+  if (!response)
+    return NULL;
+
+  // Build status line
+  int offset = snprintf(response, total_size, "HTTP/1.1 %d %s\r\n",
+                        p_resp->status_code,
+                        p_resp->status_text ? p_resp->status_text : "OK");
+
+  // Add headers
+  if (p_resp->headers)
+  {
+    size_t header_count = Dowa_Array_Length(p_resp->headers);
+    for (size_t i = 0; i < header_count; i++)
+    {
+      offset += snprintf(response + offset, total_size - offset, "%s: %s\r\n",
+                        p_resp->headers[i].key,
+                        p_resp->headers[i].value);
+    }
+  }
+
+  // End of headers
+  offset += snprintf(response + offset, total_size - offset, "\r\n");
+
+  // Add body
+  if (p_resp->body && body_len > 0)
+  {
+    memcpy(response + offset, p_resp->body, body_len);
+    offset += body_len;
+  }
+
+  response[offset] = '\0';
+  *len_out = offset;
   return response;
 }
 
@@ -171,67 +160,54 @@
 
   printf("Creating snapshot: %s (expecting %d)\n", config->path, config->expected_status);
 
-  // Connect to server
-  Seobeo_Handle *client = Seobeo_Stream_Handle_Client_Create(config->host, config->port, FALSE);
-  if (!client || client->socket < 0)
+  // Build URL
+  char url[2048];
+  snprintf(url, sizeof(url), "http://%s:%s%s", config->host, config->port, config->path);
+
+  // Create HTTP request
+  Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url);
+  if (!p_req)
   {
-    fprintf(stderr, "  ✗ Failed to connect to %s:%s\n", config->host, config->port);
-    if (client)
-    {
-      Seobeo_Handle_Destroy(client);
-    }
+    fprintf(stderr, "  ✗ Failed to create request for %s\n", url);
     return -1;
   }
 
-  // Send GET request
-  char request[4096];
-  int req_len = generate_http_get(request, sizeof(request), config->path, config->host);
-  Seobeo_Handle_Queue(client, (uint8*)request, (uint32)req_len);
-
-  if (Seobeo_Handle_Flush(client) < 0)
+  // Execute request
+  Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req);
+  if (!p_resp)
   {
-    fprintf(stderr, "  ✗ Failed to send request\n");
-    Seobeo_Handle_Destroy(client);
+    fprintf(stderr, "  ✗ Failed to get response\n");
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  // Read response
-  size_t response_len = 0;
-  char *response = read_full_response(client, &response_len);
-  Seobeo_Handle_Destroy(client);
-
-  if (!response || response_len == 0)
+  // Check status code
+  if (p_resp->status_code != config->expected_status)
   {
-    fprintf(stderr, "  ✗ Failed to read response\n");
-    if (response)
-    {
-      free(response);
-    }
+    fprintf(stderr, "  ✗ Status mismatch: expected %d, got %d\n",
+            config->expected_status, p_resp->status_code);
+    Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  // Parse status code
-  int status = -1;
-  const char *status_line = strstr(response, "HTTP/1.1 ");
-  if (!status_line)
-  {
-    status_line = strstr(response, "HTTP/1.0 ");
-  }
-  if (status_line)
+  printf("  ✓ Status: %d\n", p_resp->status_code);
+
+  // Build full HTTP response
+  size_t response_len = 0;
+  char *response = build_full_response(p_resp, &response_len);
+
+  Seobeo_Client_Response_Destroy(p_resp);
+  Seobeo_Client_Request_Destroy(p_req);
+
+  if (!response || response_len == 0)
   {
-    sscanf(status_line + 9, "%d", &status);
-  }
-
-  if (status != config->expected_status)
-  {
-    fprintf(stderr, "  ✗ Status mismatch: expected %d, got %d\n",
-            config->expected_status, status);
-    free(response);
+    fprintf(stderr, "  ✗ Failed to build response\n");
+    if (response)
+      free(response);
     return -1;
   }
 
-  printf("  ✓ Status: %d\n", status);
-
   // Generate snapshot filename
   char filename[256];
   path_to_filename(config->path, filename, sizeof(filename));