Mercurial
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 - - - -**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>© 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>© 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 - - - -**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>© 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>© 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 - - - -**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>© 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>© 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, -}
--- 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));