diff --git a/src/client.c b/tests/client.c
similarity index 100%
rename from src/client.c
rename to tests/client.c
diff --git a/tests/index.html b/tests/index.html
new file mode 100644
index 0000000..c7a91ec
--- /dev/null
+++ b/tests/index.html
@@ -0,0 +1,3 @@
+
+Welcome to the
example.re homepage!
+
diff --git a/src/main.c b/tests/main.c
similarity index 100%
rename from src/main.c
rename to tests/main.c
diff --git a/src/server.c b/tests/server.c
similarity index 100%
rename from src/server.c
rename to tests/server.c
diff --git a/tests/test.h b/tests/test.h
new file mode 100644
index 0000000..99d59f6
--- /dev/null
+++ b/tests/test.h
@@ -0,0 +1,18 @@
+#pragma once
+#include
+#include
+#include
+
+#define ASSERT(cond) \
+ do { \
+ if (!(cond)) { \
+ fprintf(stderr, "FAIL %s:%d %s\n", __FILE__, __LINE__, #cond); \
+ exit(1); \
+ } \
+ } while (0)
+
+#define ASSERT_EQ(a, b) ASSERT((a) == (b))
+#define ASSERT_NE(a, b) ASSERT((a) != (b))
+#define ASSERT_STR_EQ(a, b) ASSERT(strcmp((a), (b)) == 0)
+#define ASSERT_NULL(p) ASSERT((p) == NULL)
+#define ASSERT_NOT_NULL(p) ASSERT((p) != NULL)
diff --git a/tests/test_enchufe.c b/tests/test_enchufe.c
new file mode 100644
index 0000000..c4c521b
--- /dev/null
+++ b/tests/test_enchufe.c
@@ -0,0 +1,61 @@
+#include
+#include
+#include
+#include
+#include "enchufe.h"
+#include "test.h"
+
+static void test_atob_len(void) {
+ Buffer b = atob("hello");
+ ASSERT_EQ(b.len, 5);
+}
+
+static void test_atob_contents(void) {
+ Buffer b = atob("hello");
+ ASSERT(memcmp(b.buf, "hello", 5) == 0);
+}
+
+static void test_atob_empty(void) {
+ Buffer b = atob("");
+ ASSERT_EQ(b.len, 0);
+}
+
+static void test_nuevo_valid_fd(void) {
+ FD fd = nuevo();
+ ASSERT(fd >= 0);
+ close(fd);
+}
+
+static void test_receptaculo_fields(void) {
+ IPv4 ip = {.bytes = {127, 0, 0, 1}};
+ Port port = htons(8080);
+ Receptaculo r = receptaculo(ip, port);
+ ASSERT_EQ(r.addr.sin_family, AF_INET);
+ ASSERT_EQ(r.addr.sin_port, port);
+ ASSERT_EQ(r.addr.sin_addr.s_addr, ip.ip);
+ ASSERT_EQ(r.addrlen, (socklen_t)sizeof(struct sockaddr_in));
+}
+
+static void test_aplasta_copies_fields(void) {
+ FD fd = nuevo();
+ IPv4 ip = {.bytes = {127, 0, 0, 1}};
+ Port port = htons(9090);
+ Receptaculo r = receptaculo(ip, port);
+ Enchufe e = aplasta(fd, r);
+ ASSERT_EQ(e.fd, fd);
+ ASSERT_EQ(e.addr.sin_port, r.addr.sin_port);
+ ASSERT_EQ(e.addr.sin_addr.s_addr, r.addr.sin_addr.s_addr);
+ ASSERT_EQ(e.addrlen, r.addrlen);
+ close(fd);
+}
+
+int main(void) {
+ test_atob_len();
+ test_atob_contents();
+ test_atob_empty();
+ test_nuevo_valid_fd();
+ test_receptaculo_fields();
+ test_aplasta_copies_fields();
+ printf("test_enchufe: all tests passed\n");
+ return 0;
+}
diff --git a/tests/test_network.c b/tests/test_network.c
new file mode 100644
index 0000000..96f3ebb
--- /dev/null
+++ b/tests/test_network.c
@@ -0,0 +1,103 @@
+#include
+#include
+#include
+#include
+#include
+#include "enchufe.h"
+#include "test.h"
+
+#define MSG "ping"
+
+/* Bind to port 0 (OS picks a free port), return the assigned port in
+ network byte order via *out_port. */
+static Enchufe bind_ephemeral(IPv4 ip, Port *out_port) {
+ Enchufe s = enchufa(ip, htons(0));
+ amarra(s);
+
+ struct sockaddr_in actual = {0};
+ socklen_t alen = sizeof(actual);
+ getsockname(s.fd, (struct sockaddr *)&actual, &alen);
+ *out_port = actual.sin_port; /* already network byte order */
+ return s;
+}
+
+static void test_send_recv(void) {
+ IPv4 lo = {.bytes = {127, 0, 0, 1}};
+ Port port = 0;
+ Enchufe server = bind_ephemeral(lo, &port);
+ escucha(server, 1);
+
+ pid_t pid = fork();
+ ASSERT(pid >= 0);
+
+ if (pid == 0) {
+ Enchufe client = enchufa(lo, port);
+ conecta(client);
+ zumba(client, atob(MSG));
+ desenchufa(client);
+ exit(0);
+ }
+
+ Enchufe peer = acepta(server);
+
+ Byte raw[32] = {0};
+ Buffer buf = {.buf = raw, .len = sizeof(raw) - 1};
+ size_t n = recibe(peer, buf);
+
+ ASSERT_EQ(n, strlen(MSG));
+ ASSERT(memcmp(raw, MSG, n) == 0);
+
+ desenchufa(peer);
+ desenchufa(server);
+
+ int status = 0;
+ waitpid(pid, &status, 0);
+ ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0);
+}
+
+static void test_multi_send(void) {
+ IPv4 lo = {.bytes = {127, 0, 0, 1}};
+ Port port = 0;
+ Enchufe server = bind_ephemeral(lo, &port);
+ escucha(server, 1);
+
+ pid_t pid = fork();
+ ASSERT(pid >= 0);
+
+ if (pid == 0) {
+ Enchufe client = enchufa(lo, port);
+ conecta(client);
+ zumba(client, atob("foo"));
+ zumba(client, atob("bar"));
+ desenchufa(client);
+ exit(0);
+ }
+
+ Enchufe peer = acepta(server);
+
+ /* Accumulate until EOF (child closes socket). */
+ Byte raw[64] = {0};
+ size_t total = 0;
+ while (total < sizeof(raw) - 1) {
+ Buffer b = {.buf = raw + total, .len = sizeof(raw) - 1 - total};
+ size_t n = recibe(peer, b);
+ if (n == 0) break;
+ total += n;
+ }
+
+ ASSERT(memcmp(raw, "foobar", 6) == 0);
+
+ desenchufa(peer);
+ desenchufa(server);
+
+ int status = 0;
+ waitpid(pid, &status, 0);
+ ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0);
+}
+
+int main(void) {
+ test_send_recv();
+ test_multi_send();
+ printf("test_network: all tests passed\n");
+ return 0;
+}