summaryrefslogtreecommitdiff
path: root/test/api.c
diff options
context:
space:
mode:
authorJesper Jensen <jesper@jnsn.dev>2025-07-18 09:32:16 +0200
committerJesper Jensen <jesper@jnsn.dev>2025-07-18 09:32:33 +0200
commit69528489cddd27b4d4b9411d00b60cccc175b0e8 (patch)
treeeeed9d13f99858b3fd5660bc8d57e86bf81088a3 /test/api.c
parenta46e9a5cbb8fc233a0ccb47708e7686c2b561ac0 (diff)
Add a simple API for lookups
Diffstat (limited to 'test/api.c')
-rw-r--r--test/api.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/test/api.c b/test/api.c
new file mode 100644
index 0000000..c88ed46
--- /dev/null
+++ b/test/api.c
@@ -0,0 +1,246 @@
+#include "log.h"
+#include "unity.h"
+
+#include "api.h"
+#include "base64.h"
+
+#include <assert.h>
+#include <curl/curl.h>
+#include <stdlib.h>
+#include <string.h>
+
+// @PASTE Stolen from libcurl documentation
+struct memory {
+ char *body;
+ size_t size;
+};
+
+static size_t write_to_memory(char *data, size_t size, size_t nmemb, void *clientp) {
+ size_t realsize = size * nmemb;
+ struct memory *mem = (struct memory *)clientp;
+
+ char *ptr = realloc(mem->body, mem->size + realsize + 1);
+ if(!ptr) return 0; /* out of memory */
+
+ mem->body = ptr;
+ memcpy(&(mem->body[mem->size]), data, realsize);
+ mem->size += realsize;
+ mem->body[mem->size] = 0;
+
+ return realsize;
+}
+
+static size_t read_from_memory(char *data, size_t size, size_t nmemb, void *clientp) {
+ size_t realsize = size * nmemb;
+ struct memory *mem = (struct memory *)clientp;
+
+ realsize = realsize > mem->size ? mem->size : realsize;
+
+ memcpy(data, mem->body, realsize);
+ mem->size -= realsize;
+ mem->body += realsize;
+
+ return realsize;
+}
+
+void test_base64_decode() {
+ size_t res = 0;
+ uint8_t buf[256];
+
+ res = base64_decode((unsigned char*)"AAAA", 4, buf, 256);
+ TEST_ASSERT_EQUAL(3, res);
+ TEST_ASSERT_EQUAL_MEMORY("\0\0\0", buf, 3);
+
+ res = base64_decode((unsigned char*)"AA==", 4, buf, 256);
+ TEST_ASSERT_EQUAL(1, res);
+ TEST_ASSERT_EQUAL_MEMORY("\0", buf, 1);
+
+ res = base64_decode((unsigned char*)"MQ==", 4, buf, 256);
+ TEST_ASSERT_EQUAL(1, res);
+ TEST_ASSERT_EQUAL_MEMORY("1", buf, 1);
+
+ res = base64_decode((unsigned char*)"AAA=", 4, buf, 256);
+ TEST_ASSERT_EQUAL(2, res);
+ TEST_ASSERT_EQUAL_MEMORY("\0\0", buf, 2);
+
+ // Too short of an input string
+ res = base64_decode((unsigned char*)"AA=", 3, buf, 256);
+ TEST_ASSERT_EQUAL(-1, res);
+
+ // Too little space in the output buffer
+ res = base64_decode((unsigned char*)"AAA=", 4, buf, 1);
+ TEST_ASSERT_EQUAL(-1, res);
+
+ // Too little space in the output buffer
+ res = base64_decode((unsigned char*)"dGVzdA==", 8, buf, 256);
+ TEST_ASSERT_EQUAL(4, res);
+ TEST_ASSERT_EQUAL_MEMORY("test", buf, 4);
+
+ // A null byte in the middle of the input
+ res = base64_decode((unsigned char*)"AA\0=", 4, buf, 256);
+ TEST_ASSERT_EQUAL(-1, res);
+
+ // It should stop at the first equals
+ res = base64_decode_incr((unsigned char*)"AA==W", 5, buf, 256);
+ TEST_ASSERT_EQUAL(1, res);
+ TEST_ASSERT_EQUAL_MEMORY("\0", buf, 1);
+
+ res = base64_decode_incr((unsigned char*)"AAAA\"", 5, buf, 256);
+ TEST_ASSERT_EQUAL(3, res);
+ TEST_ASSERT_EQUAL_MEMORY("\0\0\0", buf, 3);
+
+ res = base64_decode_incr((unsigned char*)"AAAA}", 5, buf, 256);
+ TEST_ASSERT_EQUAL(3, res);
+ TEST_ASSERT_EQUAL_MEMORY("\0\0\0", buf, 3);
+
+ // We are a little overpermissive when it comes to the padding equals. This
+ // isn't technically valid, but due to some implementation details we still
+ // accept it. I think that's fine
+ res = base64_decode_incr((unsigned char*)"AA=W", 4, buf, 256);
+ TEST_ASSERT_EQUAL(1, res);
+ TEST_ASSERT_EQUAL_MEMORY("\0", buf, 1);
+}
+
+void test_root_get() {
+ CURLcode curlRes;
+ CURL *curl = curl_easy_init();
+ TEST_ASSERT_NOT_NULL(curl);
+
+ struct dht dht = {0};
+ dht.self = (struct nodeid){.inner={0x42424242, 0x42424242, 0x42424242, 0x42424242, 0x42424242}};
+ api_init(&dht);
+
+ curlRes = curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:6982/");
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_memory);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ struct memory body = {0};
+ curlRes = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &body);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_perform(curl);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ char *ct;
+ curlRes = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ TEST_ASSERT_EQUAL_STRING("application/json", ct);
+
+ TEST_ASSERT_EQUAL_STRING("{ \"id\": \"QkJCQkJCQkJCQkJCQkJCQkJCQkI=\" }", body.body);
+ body = (struct memory){0};
+
+ free(body.body);
+ curl_easy_cleanup(curl);
+ api_end();
+}
+
+void test_lookup_get() {
+ curl_global_init(CURL_GLOBAL_ALL);
+ CURLcode curlRes;
+ CURL *curl = curl_easy_init();
+ TEST_ASSERT_NOT_NULL(curl);
+
+ struct dht dht = {0};
+ dht.self = (struct nodeid){.inner={0x42424242, 0x42424242, 0x42424242, 0x42424242, 0x42424242}};
+ api_init(&dht);
+
+ curlRes = curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:6982/lookup");
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_memory);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ struct memory body = {0};
+ curlRes = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &body);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_perform(curl);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ char *ct;
+ curlRes = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ TEST_ASSERT_EQUAL_STRING("application/json", ct);
+
+ TEST_ASSERT_EQUAL_STRING(
+ "{ "
+ "\"state\": \"empty\" "
+ "}",
+ body.body
+ );
+ body = (struct memory){0};
+
+ {
+ curlRes = curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_from_memory);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ struct memory req_body = {
+ .body = "{\"target\": \"BAAAAAAAAAAAAAAAAAAAAAAAAAA=\", \"state\": \"pending\"}",
+ .size = strlen(req_body.body),
+ };
+ curlRes = curl_easy_setopt(curl, CURLOPT_READDATA, &req_body);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_perform(curl);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+ TEST_ASSERT_EQUAL_STRING("application/json", ct);
+
+ TEST_ASSERT_EQUAL_STRING(
+ "{ "
+ "\"state\": \"pending\", "
+ "\"outstanding\": 0, "
+ "\"target\": \"BAAAAAAAAAAAAAAAAAAAAAAAAAA=\" "
+ "}",
+ body.body
+ );
+ body = (struct memory){0};
+ }
+
+ // The protocol does whatever and complete the lookup
+ dht.lookup.state = OP_COMPLETED;
+
+ {
+ curlRes = curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_from_memory);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ struct memory req_body = {
+ .body = "{\"state\": \"empty\"}",
+ .size = strlen(req_body.body),
+ };
+ curlRes = curl_easy_setopt(curl, CURLOPT_READDATA, &req_body);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_perform(curl);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+
+ curlRes = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);
+ TEST_ASSERT_EQUAL(CURLE_OK, curlRes);
+ TEST_ASSERT_EQUAL_STRING("application/json", ct);
+
+ TEST_ASSERT_EQUAL_STRING(
+ "{ "
+ "\"state\": \"empty\" "
+ "}",
+ body.body
+ );
+ body = (struct memory){0};
+ }
+
+ free(body.body);
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+ api_end();
+}