summaryrefslogtreecommitdiffstats
path: root/tests/test_server.hpp
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-26 12:28:10 -0300
committerMurilo Ijanc <[email protected]>2026-07-26 12:28:10 -0300
commitb71faa6e95a0736f1ff171e006b171467b65db90 (patch)
tree7580c3ab515e8f50726dc68dbc240995ff4a1a4c /tests/test_server.hpp
downloadcpp-b71faa6e95a0736f1ff171e006b171467b65db90.tar.gz
Initial importv0.1.0
Diffstat (limited to 'tests/test_server.hpp')
-rw-r--r--tests/test_server.hpp74
1 files changed, 74 insertions, 0 deletions
diff --git a/tests/test_server.hpp b/tests/test_server.hpp
new file mode 100644
index 0000000..54c6e0c
--- /dev/null
+++ b/tests/test_server.hpp
@@ -0,0 +1,74 @@
+#ifndef CEPIS_TESTS_TEST_SERVER_HPP
+#define CEPIS_TESTS_TEST_SERVER_HPP
+
+#include <atomic>
+#include <functional>
+#include <map>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <utility>
+#include <vector>
+
+namespace cepis::testing {
+
+/// One canned HTTP response.
+struct Reply {
+ int status = 200;
+ std::string body;
+ std::vector<std::pair<std::string, std::string>> headers;
+};
+
+/// A throwaway HTTP/1.1 server on a random loopback port.
+///
+/// The tests point `Config::base_url` at it, so the code path they exercise is
+/// the one that runs in production — request construction, header reading and
+/// error-body reading included. Nothing is mocked below the socket, and it
+/// doubles as the test that `base_url` works: without it none of this runs.
+class Server {
+ public:
+ Server();
+ ~Server();
+
+ Server(const Server&) = delete;
+ Server& operator=(const Server&) = delete;
+ Server(Server&&) = delete;
+ Server& operator=(Server&&) = delete;
+
+ /// `http://127.0.0.1:<port>`, ready to hand to `Config::base_url`.
+ [[nodiscard]] std::string url() const;
+
+ /// Registers the response for a path. The last entry of a queue repeats once
+ /// the earlier ones are consumed, which is how retrying is tested.
+ void respond(std::string path, Reply reply);
+ void respond(std::string path, std::vector<Reply> replies);
+
+ /// Installs a catch-all that sees every request. Takes precedence over the
+ /// registered paths.
+ void handle(std::function<Reply(const std::string& path)> handler);
+
+ /// How many requests so far began with `prefix`.
+ [[nodiscard]] int count(std::string_view prefix) const;
+
+ /// Every path requested so far, in order.
+ [[nodiscard]] std::vector<std::string> calls() const;
+
+ private:
+ void serve();
+ Reply next(const std::string& path);
+
+ int listen_fd_ = -1;
+ int port_ = 0;
+ std::atomic<bool> stopping_{false};
+ std::thread thread_;
+
+ mutable std::mutex mutex_;
+ std::map<std::string, std::vector<Reply>> routes_;
+ std::function<Reply(const std::string&)> handler_;
+ std::vector<std::string> calls_;
+};
+
+} // namespace cepis::testing
+
+#endif // CEPIS_TESTS_TEST_SERVER_HPP