view s3/tests/s3_uploader_test.c @ 279:b3b547563ec7

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

#include "s3/s3_uploader.h"

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

// Simple .env parser
static char *parse_env_value(const char *env_content, const char *key)
{
  const char *pos = strstr(env_content, key);
  if (!pos) return NULL;

  pos += strlen(key);
  if (*pos != '=') return NULL;
  pos++;

  const char *end = pos;
  while (*end && *end != '\n' && *end != '\r') end++;

  size_t len = end - pos;
  char *value = malloc(len + 1);
  memcpy(value, pos, len);
  value[len] = '\0';
  return value;
}

static char *load_file(const char *path)
{
  FILE *f = fopen(path, "r");
  if (!f) return NULL;

  fseek(f, 0, SEEK_END);
  long size = ftell(f);
  fseek(f, 0, SEEK_SET);

  char *content = malloc(size + 1);
  fread(content, 1, size, f);
  content[size] = '\0';
  fclose(f);
  return content;
}

int main(int argc, char **argv)
{
  // Try to load .env file - check multiple locations for bazel runfiles
  char *env_content = load_file(".env");
  if (!env_content)
  {
    env_content = load_file("zenbu/.env");  // bazel runfiles path
  }
  if (!env_content)
  {
    env_content = load_file("../.env");
  }
  if (!env_content)
  {
    env_content = load_file("../../.env");
  }

  char *access_key = NULL;
  char *secret_key = NULL;

  if (env_content)
  {
    access_key = parse_env_value(env_content, "AWS_MRJUNEJUNE_ACCESS_KEY");
    secret_key = parse_env_value(env_content, "AWS_MRJUNEJUNE_SECRET_ACCESS_KEY");
    free(env_content);
  }

  // Fall back to environment variables
  if (!access_key) access_key = getenv("AWS_ACCESS_KEY_ID") ? strdup(getenv("AWS_ACCESS_KEY_ID")) : NULL;
  if (!secret_key) secret_key = getenv("AWS_SECRET_ACCESS_KEY") ? strdup(getenv("AWS_SECRET_ACCESS_KEY")) : NULL;

  if (!access_key || !secret_key)
  {
    printf("Missing credentials. Set in .env file or environment:\n");
    printf("  AWS_MRJUNEJUNE_ACCESS_KEY / AWS_ACCESS_KEY_ID\n");
    printf("  AWS_MRJUNEJUNE_SECRET_ACCESS_KEY / AWS_SECRET_ACCESS_KEY\n");
    return 1;
  }

  S3_Config config = {
    .access_key_id     = access_key,
    .secret_access_key = secret_key,
    .region            = "us-west-2",
    .bucket            = "mrjunejune",
    .endpoint          = NULL,
    .use_path_style    = FALSE
  };

  printf("S3 Uploader Test\n");
  printf("================\n");
  printf("Region: %s\n", config.region);
  printf("Bucket: %s\n", config.bucket);
  printf("\n");

  // Test 1: Upload string data
  printf("Test 1: Uploading string data...\n");
  const char *test_data = "Hello from S3 uploader test!";
  S3_Result result = S3_Upload_Data(&config,
                                     (const uint8 *)test_data,
                                     strlen(test_data),
                                     "test/hello.txt",
                                     "text/plain");
  if (result.success)
  {
    printf("  SUCCESS! ETag: %s\n", result.etag ? result.etag : "(none)");
  }
  else
  {
    printf("  FAILED! Status: %d, Error: %s\n",
           result.status_code,
           result.error_message ? result.error_message : "(unknown)");
  }
  S3_Result_Destroy(&result);

  // Test 2: Content type detection
  printf("\nTest 2: Content type detection...\n");
  printf("  .html -> %s\n", S3_Guess_Content_Type("page.html"));
  printf("  .png  -> %s\n", S3_Guess_Content_Type("image.png"));
  printf("  .json -> %s\n", S3_Guess_Content_Type("data.json"));
  printf("  .xyz  -> %s\n", S3_Guess_Content_Type("unknown.xyz"));

  // Test 3: Generate presigned PUT URL
  printf("\nTest 3: Generating presigned PUT URL...\n");
  S3_Presigned_URL put_url = S3_Presign_Put(&config,
                                             "uploads/client-upload.png",
                                             "image/png",
                                             3600);  // 1 hour
  if (put_url.success)
  {
    printf("  SUCCESS!\n");
    printf("  URL: %s\n", put_url.url);
    printf("\n  Client can upload with:\n");
    printf("    curl -X PUT -H \"Content-Type: image/png\" --data-binary @file.png \"$URL\"\n");
  }
  else
  {
    printf("  FAILED! Error: %s\n", put_url.error_message ? put_url.error_message : "(unknown)");
  }
  S3_Presigned_URL_Destroy(&put_url);

  // Test 4: Generate presigned GET URL
  printf("\nTest 4: Generating presigned GET URL...\n");
  S3_Presigned_URL get_url = S3_Presign_Get(&config,
                                             "uploads/client-upload.png",
                                             3600);  // 1 hour
  if (get_url.success)
  {
    printf("  SUCCESS!\n");
    printf("  URL: %s\n", get_url.url);
    printf("\n  Client can download with:\n");
    printf("    curl \"$URL\" -o file.png\n");
  }
  else
  {
    printf("  FAILED! Error: %s\n", get_url.error_message ? get_url.error_message : "(unknown)");
  }
  S3_Presigned_URL_Destroy(&get_url);

  // Test 5: Upload file (if provided as argument)
  if (argc >= 3)
  {
    printf("\nTest 5: Uploading file...\n");
    printf("  Local: %s\n", argv[1]);
    printf("  S3 Key: %s\n", argv[2]);

    result = S3_Upload_File(&config, argv[1], argv[2]);
    if (result.success)
    {
      printf("  SUCCESS! ETag: %s\n", result.etag ? result.etag : "(none)");
    }
    else
    {
      printf("  FAILED! Status: %d, Error: %s\n",
             result.status_code,
             result.error_message ? result.error_message : "(unknown)");
    }
    S3_Result_Destroy(&result);
  }
  else
  {
    printf("\nTest 5: Skipped (no file path provided)\n");
    printf("  Usage: %s <local_file> <s3_key>\n", argv[0]);
  }

  printf("\nDone!\n");

  free(access_key);
  free(secret_key);
  return 0;
}