From ebefa5e914b7298605bba1b6c6f99b08348f047e Mon Sep 17 00:00:00 2001 From: syeopite Date: Wed, 9 Apr 2025 18:29:00 -0700 Subject: [PATCH] Add tests for connection pool --- .../networking/connection_pool_spec.cr | 95 +++++++++++++++++++ spec/load_config.cr | 15 +++ 2 files changed, 110 insertions(+) create mode 100644 spec/helpers/networking/connection_pool_spec.cr create mode 100644 spec/load_config.cr diff --git a/spec/helpers/networking/connection_pool_spec.cr b/spec/helpers/networking/connection_pool_spec.cr new file mode 100644 index 00000000..949558b9 --- /dev/null +++ b/spec/helpers/networking/connection_pool_spec.cr @@ -0,0 +1,95 @@ +# Due to the way that specs are handled this file cannot be run +# together with everything else without causing a compile time error +# +# TODO: Allow running different isolated spec through make +# +# For now run this with `crystal spec -p spec/helpers/networking/connection_pool_spec.cr -Drunning_by_self` +{% skip_file unless flag?(:running_by_self) %} + +# Based on https://github.com/jgaskins/http_client/blob/958cf56064c0d31264a117467022b90397eb65d7/spec/http_client_spec.cr +require "wait_group" +require "uri" +require "http" +require "http/server" +require "http_proxy" + +require "db" +require "pg" +require "spectator" + +require "../../load_config" +require "../../../src/invidious/helpers/crystal_class_overrides" +require "../../../src/invidious/connection/*" + +server = HTTP::Server.new do |context| + request = context.request + response = context.response + + case {request.method, request.path} + when {"GET", "/get"} + response << "get" + when {"POST", "/post"} + response.status = :created + response << "post" + when {"GET", "/sleep"} + duration = request.query_params["duration_sec"].to_i.seconds + sleep duration + end +end + +spawn server.listen 12345 + +Fiber.yield + +Spectator.describe Invidious::ConnectionPool do + describe "Pool" do + it "Can make a requests through standard HTTP methods" do + pool = Invidious::ConnectionPool::Pool.new(URI.parse("http://localhost:12345"), max_capacity: 100) + + expect(pool.get("/get").body).to eq("get") + expect(pool.post("/post").body).to eq("post") + end + + it "Can make streaming requests" do + pool = Invidious::ConnectionPool::Pool.new(URI.parse("http://localhost:12345"), max_capacity: 100) + + expect(pool.get("/get") { |r| r.body_io.gets_to_end }).to eq("get") + expect(pool.get("/post") { |r| r.body }).to eq("") + expect(pool.post("/post") { |r| r.body_io.gets_to_end }).to eq("post") + end + + # it "Can checkout a client" do + # end + + it "Allows concurrent requests" do + pool = Invidious::ConnectionPool::Pool.new(URI.parse("http://localhost:12345"), max_capacity: 100) + responses = [] of HTTP::Client::Response + + WaitGroup.wait do |wg| + 100.times do + wg.spawn { responses << pool.get("/get") } + end + end + + expect(responses.map(&.body)).to eq(["get"] * 100) + end + + it "Raises on checkout timeout" do + pool = Invidious::ConnectionPool::Pool.new(URI.parse("http://localhost:12345"), max_capacity: 2, timeout: 0.01) + + # Long running requests + 2.times do + spawn { pool.get("/sleep?duration_sec=2") } + end + + Fiber.yield + + expect { pool.get("/get") }.to raise_error(Invidious::ConnectionPool::Error) + end + + it "Raises when an error is encounter" do + pool = Invidious::ConnectionPool::Pool.new(URI.parse("http://localhost:12345"), max_capacity: 100, timeout: 0.01) + expect { pool.get("/get") { raise IO::Error.new } }.to raise_error(Invidious::ConnectionPool::Error) + end + end +end diff --git a/spec/load_config.cr b/spec/load_config.cr new file mode 100644 index 00000000..07a63718 --- /dev/null +++ b/spec/load_config.cr @@ -0,0 +1,15 @@ +require "yaml" +require "log" + +abstract class Kemal::BaseLogHandler +end + +require "../src/invidious/config" +require "../src/invidious/jobs/base_job" +require "../src/invidious/jobs.cr" +require "../src/invidious/user/preferences.cr" +require "../src/invidious/helpers/logger" +require "../src/invidious/helpers/utils" + +CONFIG = Config.from_yaml(File.open("config/config.example.yml")) +HMAC_KEY = CONFIG.hmac_key