summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/api.c246
-rw-r--r--test/proto.c71
2 files changed, 300 insertions, 17 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();
+}
diff --git a/test/proto.c b/test/proto.c
index 29563a3..a566971 100644
--- a/test/proto.c
+++ b/test/proto.c
@@ -745,19 +745,21 @@ void test_lookup_response() {
// The node responds. This should add it to our frontier since it's
// empty. It should also fan out the search into what it returns since
// we still have empty spots after adding this one.
- char buff[] = "d1:y1:r1:t1:11:rd2:id20:CBBBBBBBBBBBBBBBBBBB5:nodes52:aBBBBBBBBBBBBBBBBBBB\xFF\xFF\xFF\xFF\x00\x01""aaaaBBBBBBBBBBBBBBBB\xFF\xFF\xFF\xFF\x00\x01""ee";
+ char buff[] = "d1:y1:r1:t1:11:rd2:id20:CBBBBBBBBBBBBBBBBBBB5:nodes78:aBBBBBBBBBBBBBBBBBBB\xFF\xFF\xFF\xFF\x00\x01""aaaaBBBBBBBBBBBBBBBB\xFF\xFF\xFF\xFF\x00\x01""aaaBBBBBBBBBBBBBBBBB\xFF\xFF\xFF\xFF\x00\x01""ee";
struct message* message_cursor = outbuff;
- int rc = proto_run(&dht, buff, sizeof(buff), (struct sockaddr_in*)&remote, sizeof(remote), now, &message_cursor, outbuff+2);
+ int rc = proto_run(&dht, buff, sizeof(buff), (struct sockaddr_in*)&remote, sizeof(remote), now, &message_cursor, outbuff+10);
TEST_ASSERT_EQUAL(rc, 0);
TEST_ASSERT_EQUAL_CHAR_ARRAY("CBBBBBBBBBBBBBBBBBBB", &dht.lookup.closest[0], 20);
- TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+2);
+ TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+3);
TEST_ASSERT_EQUAL(91, outbuff[0].payload_len);
TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:ad2:id20:BBBBBBBBBBBBBBBBBBBB6:target20:aaaaaaaaaaaaaaaaaaaae1:q9:find_node1:t1:21:y1:qe", outbuff[0].payload, 91);
TEST_ASSERT_EQUAL(91, outbuff[1].payload_len);
TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:ad2:id20:BBBBBBBBBBBBBBBBBBBB6:target20:aaaaaaaaaaaaaaaaaaaae1:q9:find_node1:t1:31:y1:qe", outbuff[1].payload, 91);
- TEST_ASSERT_EQUAL(2, dht.lookup.outstanding); // Resolve 1, add 2
+ TEST_ASSERT_EQUAL(91, outbuff[2].payload_len);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:ad2:id20:BBBBBBBBBBBBBBBBBBBB6:target20:aaaaaaaaaaaaaaaaaaaae1:q9:find_node1:t1:41:y1:qe", outbuff[2].payload, 91);
+ TEST_ASSERT_EQUAL(3, dht.lookup.outstanding); // Resolve 1, add 3
memcpy(&remote, &outbuff[0].dest, sizeof(remote));
}
@@ -770,20 +772,51 @@ void test_lookup_response() {
// we just asserted above. Those nodes happened to be closer to the final
// target than the outstanding request we have going on.
{
- for(size_t i = 0; i < 8; i++) {
- dht.lookup.closest[i] = (struct nodeid){.inner={0x42424242, 0x42424242, 0x42424242, 0x42424242, 0x42424242}};
- // The first two bytes match
- dht.lookup.closest[i].inner_b[0] = 'a';
- dht.lookup.closest[i].inner_b[1] = 'a';
- dht.lookup.closest[i].inner_b[2] = 'a' + i;
-
- dht.lookup.closest_addr[i].ip = 0;
- dht.lookup.closest_addr[i].port = 1;
- }
+ dht.lookup.closest[0] = (struct nodeid){.inner_b={"aaaBBBBBBBBBBBBBBBBB"}};
+ dht.lookup.closest_addr[0] = (struct addr){.ip = 0, .port = 1};
+ dht.lookup.closest[1] = (struct nodeid){.inner_b={"aabBBBBBBBBBBBBBBBBB"}};
+ dht.lookup.closest_addr[1] = (struct addr){.ip = 0, .port = 1};
+ dht.lookup.closest[2] = (struct nodeid){.inner_b={"aacBBBBBBBBBBBBBBBBB"}};
+ dht.lookup.closest_addr[2] = (struct addr){.ip = 0, .port = 1};
+ dht.lookup.closest[3] = (struct nodeid){.inner_b={"aadBBBBBBBBBBBBBBBBB"}};
+ dht.lookup.closest_addr[3] = (struct addr){.ip = 0, .port = 1};
+ dht.lookup.closest[4] = (struct nodeid){.inner_b={"aaeBBBBBBBBBBBBBBBBB"}};
+ dht.lookup.closest_addr[4] = (struct addr){.ip = 0, .port = 1};
+ dht.lookup.closest[5] = (struct nodeid){.inner_b={"aafBBBBBBBBBBBBBBBBB"}};
+ dht.lookup.closest_addr[5] = (struct addr){.ip = 0, .port = 1};
+ dht.lookup.closest[6] = (struct nodeid){.inner_b={"aagBBBBBBBBBBBBBBBBB"}};
+ dht.lookup.closest_addr[6] = (struct addr){.ip = 0, .port = 1};
+ dht.lookup.closest[7] = (struct nodeid){.inner_b={"aahBBBBBBBBBBBBBBBBB"}};
+ dht.lookup.closest_addr[7] = (struct addr){.ip = 0, .port = 1};
}
now += 1;
{
+ // The whole omitted interactive above included a response to the 3rd
+ // message from the last send, but before that, we fanned out from
+ // another node that knew about it too. This means we still have
+ // a leftover pending request to that node waiting for us. When that
+ // comes in, we should notice that, although it's better than some of
+ // our other candidates, it's also already there. It should therefore
+ // not be included
+ char buff[] = "d1:y1:r1:t1:41:rd2:id20:aaaBBBBBBBBBBBBBBBBB5:nodes0:ee";
+ struct message* message_cursor = outbuff;
+ int rc = proto_run(&dht, buff, sizeof(buff), (struct sockaddr_in*)&remote, sizeof(remote), now, &message_cursor, outbuff+2);
+
+ TEST_ASSERT_EQUAL(rc, 0);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[0], "aaaBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[1], "aabBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[2], "aacBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[3], "aadBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[4], "aaeBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[5], "aafBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[6], "aagBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[7], "aahBBBBBBBBBBBBBBBBB", 20);
+
+ TEST_ASSERT_EQUAL(2, dht.lookup.outstanding); // Resolve 1
+ }
+
+ {
// The node now finally responds, but woops only the first byte of its
// ID matches. That's worse than the frontier and shouldn't cause any
// addtional adjustment to the frontier.
@@ -794,10 +827,14 @@ void test_lookup_response() {
int rc = proto_run(&dht, buff, sizeof(buff), (struct sockaddr_in*)&remote, sizeof(remote), now, &message_cursor, outbuff+2);
TEST_ASSERT_EQUAL(rc, 0);
- // We only need to check this once since we always pick the first slot
- // with a given score. It's a little implementation dependant, but it
- // beats having 8 asserts.
TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[0], "aaaBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[1], "aabBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[2], "aacBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[3], "aadBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[4], "aaeBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[5], "aafBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[6], "aagBBBBBBBBBBBBBBBBB", 20);
+ TEST_ASSERT_EQUAL_CHAR_ARRAY(&dht.lookup.closest[7], "aahBBBBBBBBBBBBBBBBB", 20);
// And we didn't fan out to the new node since it's already part of the current frontier
TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff);