From 6622913d521ce8578e734d8fa867fd21d3d77168 Mon Sep 17 00:00:00 2001 From: Mike Cifelli Date: Sat, 20 Apr 2024 13:41:19 -0400 Subject: [PATCH] Add gemini client --- config/test.exs | 15 ++-- lib/chronoscope/application.ex | 3 + lib/chronoscope/{nts => }/certificate.ex | 13 +++- lib/chronoscope/gemini.ex | 60 ++++++++++++++- lib/chronoscope/gemini/client.ex | 73 +++++++++++++++++++ lib/chronoscope/gemini/connection_client.ex | 60 +++++++++++++++ lib/chronoscope/gemini/request.ex | 5 ++ lib/chronoscope/gemini/response.ex | 6 ++ lib/chronoscope/nts.ex | 6 +- lib/chronoscope/nts/client.ex | 11 +-- .../nts/key_establishment_client.ex | 4 +- .../v1/nts/key_establishment_controller.ex | 2 +- .../{nts => }/certificate_test.exs | 18 ++--- test/chronoscope/nts/client_test.exs | 21 +++--- .../nts/key_establishment_client_test.exs | 27 ++++--- test/chronoscope/nts_test.exs | 27 ++++--- .../nts/key_establishment_controller_test.exs | 10 ++- test/support/case.ex | 2 +- test/support/conn_case.ex | 2 +- test/support/mocks.ex | 13 ++-- test/support/stubs.ex | 2 +- 21 files changed, 295 insertions(+), 85 deletions(-) rename lib/chronoscope/{nts => }/certificate.ex (74%) create mode 100644 lib/chronoscope/gemini/client.ex create mode 100644 lib/chronoscope/gemini/connection_client.ex create mode 100644 lib/chronoscope/gemini/request.ex create mode 100644 lib/chronoscope/gemini/response.ex rename test/chronoscope/{nts => }/certificate_test.exs (65%) diff --git a/config/test.exs b/config/test.exs index 4e2df64..e445d94 100644 --- a/config/test.exs +++ b/config/test.exs @@ -19,10 +19,11 @@ config :logger, level: :warning # Initialize plugs at runtime for faster test compilation config :phoenix, :plug_init_mode, :runtime -config :chronoscope, Chronoscope.NTS, - behaviour: Chronoscope.NTS.BehaviourMock, - date_time: Chronoscope.NTS.DateTimeMock, - ssl: Chronoscope.NTS.SSLMock, - registry: Chronoscope.NTS.RegistryMock, - dynamic_supervisor: Chronoscope.NTS.DynamicSupervisorMock, - gen_server: Chronoscope.NTS.GenServerMock +config :chronoscope, + date_time: Chronoscope.DateTimeMock, + ssl: Chronoscope.SSLMock, + dynamic_supervisor: Chronoscope.DynamicSupervisorMock, + registry: Chronoscope.RegistryMock, + gen_server: Chronoscope.GenServerMock, + nts: Chronoscope.NTSMock, + gemini: Chronoscope.GeminiMock diff --git a/lib/chronoscope/application.ex b/lib/chronoscope/application.ex index df51260..770f140 100644 --- a/lib/chronoscope/application.ex +++ b/lib/chronoscope/application.ex @@ -18,6 +18,9 @@ defmodule Chronoscope.Application do {DynamicSupervisor, name: Chronoscope.NTS.DynamicSupervisor, strategy: :one_for_one}, {Task.Supervisor, name: Chronoscope.NTS.TaskSupervisor}, {Registry, [keys: :unique, name: Chronoscope.NTS.Registry]}, + {DynamicSupervisor, name: Chronoscope.Gemini.DynamicSupervisor, strategy: :one_for_one}, + {Task.Supervisor, name: Chronoscope.Gemini.TaskSupervisor}, + {Registry, [keys: :unique, name: Chronoscope.Gemini.Registry]}, # Start to serve requests, typically the last entry ChronoscopeWeb.Endpoint ] diff --git a/lib/chronoscope/nts/certificate.ex b/lib/chronoscope/certificate.ex similarity index 74% rename from lib/chronoscope/nts/certificate.ex rename to lib/chronoscope/certificate.ex index e9f4e76..7ab51a8 100644 --- a/lib/chronoscope/nts/certificate.ex +++ b/lib/chronoscope/certificate.ex @@ -1,13 +1,18 @@ -defmodule Chronoscope.NTS.Certificate do - @date_time Application.compile_env(:chronoscope, Chronoscope.NTS)[:date_time] || DateTime +defmodule Chronoscope.Certificate do + @date_time Application.compile_env(:chronoscope, :date_time, DateTime) def expiration_date(certificate) do - {:Validity, _, {:utcTime, expiration}} = + case( certificate |> X509.Certificate.from_der!() |> X509.Certificate.validity() + ) do + {:Validity, _, {:utcTime, expiration}} -> + cert_time_to_iso8601(expiration) - cert_time_to_iso8601(expiration) + {:Validity, {:utcTime, expiration}, _} -> + cert_time_to_iso8601(expiration) + end end def cert_time_to_iso8601(cert_time) do diff --git a/lib/chronoscope/gemini.ex b/lib/chronoscope/gemini.ex index b7ca140..4e6bc56 100644 --- a/lib/chronoscope/gemini.ex +++ b/lib/chronoscope/gemini.ex @@ -1,2 +1,60 @@ -defmodule Chronoscope.Gemini do +defmodule Chronoscope.Gemini.Behaviour do + @callback(connect(host :: String.t(), port :: integer(), path :: String.t()) :: {:ok, Map.t()}, {:error, any()}) +end + +defmodule Chronoscope.Gemini do + @behaviour Chronoscope.Gemini.Behaviour + + require Logger + + alias Chronoscope.Gemini + + @registry Application.compile_env(:chronoscope, :registry, Registry) + @genserver Application.compile_env(:chronoscope, :gen_server, GenServer) + @dynamic_supervisor Application.compile_env(:chronoscope, :dynamic_supervisor, DynamicSupervisor) + + def healthy?() do + true + end + + def list() do + Gemini.DynamicSupervisor + |> @dynamic_supervisor.which_children() + |> Enum.map(fn {_, pid, _, _} -> @genserver.call(pid, :list) end) + end + + def remove(host, port) do + name = client_name(%{host: host, port: port}) + + case @registry.lookup(Gemini.Registry, name) do + [{pid, _}] -> {:ok, @genserver.call(pid, :terminate)} + [] -> {:error, :not_found} + end + end + + @impl true + def connect(host, port, path) do + %{host: host, port: port, path: path} + |> client_pid() + |> @genserver.call(:connect) + end + + defp client_pid(resource) do + name = client_name(resource) + + case @registry.lookup(Gemini.Registry, name) do + [{pid, _}] -> pid + [] -> start_client(resource, name) + end + end + + defp client_name(%{host: host, port: port, path: path}) do + "#{host}:#{port}#{path}" + end + + defp start_client(resource, name) do + Gemini.DynamicSupervisor + |> @dynamic_supervisor.start_child({Gemini.Client, resource: resource, name: {:via, @registry, {Gemini.Registry, name}}}) + |> then(fn {:ok, pid} -> pid end) + end end diff --git a/lib/chronoscope/gemini/client.ex b/lib/chronoscope/gemini/client.ex new file mode 100644 index 0000000..41e4c15 --- /dev/null +++ b/lib/chronoscope/gemini/client.ex @@ -0,0 +1,73 @@ +defmodule Chronoscope.Gemini.Client do + use GenServer, restart: :transient + + alias Chronoscope.Gemini + alias Chronoscope.Gemini.ConnectionClient + + @interval_in_seconds 30 + @date_time Application.compile_env(:chronoscope, :date_time, DateTime) + + def start_link(resource: resource, name: name) do + GenServer.start_link(__MODULE__, resource, name: name) + end + + @impl true + def init(resource) do + now = utc_now() + + {:ok, + %{ + resource: resource, + reponse: {:error, "initializing"}, + last_request: DateTime.add(now, -@interval_in_seconds, :second) + }} + end + + @impl true + def handle_call(:terminate, _from, state) do + {:stop, :normal, self(), state} + end + + @impl true + def handle_call(:list, _from, state) do + {:reply, state, state} + end + + @impl true + def handle_call(:connect, _from, state) do + new_state = update_state(state) + + {:reply, new_state.response, new_state} + end + + defp update_state(state) do + now = utc_now() + + if interval_surpassed?(now, state.last_request) do + Map.merge(state, current_data(state, now)) + else + state + end + end + + defp current_data(state, now) do + %{ + response: server_response(state), + last_request: now + } + end + + defp server_response(%{resource: resource}) do + Gemini.TaskSupervisor + |> Task.Supervisor.async(fn -> ConnectionClient.connect(resource) end) + |> Task.await() + end + + defp interval_surpassed?(now, last_request) do + DateTime.diff(now, last_request, :second) >= @interval_in_seconds + end + + defp utc_now() do + @date_time.utc_now() + end +end diff --git a/lib/chronoscope/gemini/connection_client.ex b/lib/chronoscope/gemini/connection_client.ex new file mode 100644 index 0000000..37a63f2 --- /dev/null +++ b/lib/chronoscope/gemini/connection_client.ex @@ -0,0 +1,60 @@ +defmodule Chronoscope.Gemini.ConnectionClient do + require Logger + + alias Chronoscope.Certificate + alias Chronoscope.Gemini.Request + alias Chronoscope.Gemini.Response + + @timeout_in_milliseconds 3000 + @ssl Application.compile_env(:chronoscope, :ssl, :ssl) + + def connect(%{host: host, port: port, path: _} = resource) do + case ssl_connect(host, port) do + {:ok, socket} -> make_request(socket, resource) + {:error, {:tls_alert, {:handshake_failure, error}}} -> {:error, String.trim("#{error}")} + {:error, {:tls_alert, {:no_application_protocol, error}}} -> {:error, String.trim("#{error}")} + {:error, :timeout} -> {:error, :timeout} + {:error, error} -> {:error, inspect(error)} + error -> {:error, inspect(error)} + end + end + + defp ssl_connect(host, port) do + host + |> String.to_charlist() + |> @ssl.connect(port, tls_options(host), @timeout_in_milliseconds) + end + + defp tls_options(_host) do + # TODO + [] + end + + defp make_request(socket, url) do + :ok = @ssl.send(socket, Request.create(url)) + {:ok, peercert} = @ssl.peercert(socket) + + receive do + {:ssl, _socket, response} -> + @ssl.close(socket) + parse_response(response, peercert) + + msg -> + @ssl.close(socket) + Logger.error("received unexpected message: #{inspect(msg)}") + {:error, :no_response} + after + @timeout_in_milliseconds -> + @ssl.close(socket) + Logger.error("timed out waiting for response") + {:error, :timeout} + end + end + + defp parse_response(response, peercert) do + response + |> Response.parse() + |> Map.put(:cert_expiration, Certificate.expiration_date(peercert)) + |> then(&{:ok, &1}) + end +end diff --git a/lib/chronoscope/gemini/request.ex b/lib/chronoscope/gemini/request.ex new file mode 100644 index 0000000..ac88210 --- /dev/null +++ b/lib/chronoscope/gemini/request.ex @@ -0,0 +1,5 @@ +defmodule Chronoscope.Gemini.Request do + def create(resource) do + "gemini://#{resource.host}:#{resource.port}#{resource.path}\r\n" + end +end diff --git a/lib/chronoscope/gemini/response.ex b/lib/chronoscope/gemini/response.ex new file mode 100644 index 0000000..26c7e17 --- /dev/null +++ b/lib/chronoscope/gemini/response.ex @@ -0,0 +1,6 @@ +defmodule Chronoscope.Gemini.Response do + def parse(response) do + # TODO + %{status: 20, type: "text/gemini", body: response} + end +end diff --git a/lib/chronoscope/nts.ex b/lib/chronoscope/nts.ex index ccd8ed8..5866dde 100644 --- a/lib/chronoscope/nts.ex +++ b/lib/chronoscope/nts.ex @@ -9,9 +9,9 @@ defmodule Chronoscope.NTS do alias Chronoscope.NTS - @registry Application.compile_env(:chronoscope, Chronoscope.NTS)[:registry] || Registry - @genserver Application.compile_env(:chronoscope, Chronoscope.NTS)[:gen_server] || GenServer - @dynamic_supervisor Application.compile_env(:chronoscope, Chronoscope.NTS)[:dynamic_supervisor] || DynamicSupervisor + @registry Application.compile_env(:chronoscope, :registry, Registry) + @genserver Application.compile_env(:chronoscope, :gen_server, GenServer) + @dynamic_supervisor Application.compile_env(:chronoscope, :dynamic_supervisor, DynamicSupervisor) def healthy?() do true diff --git a/lib/chronoscope/nts/client.ex b/lib/chronoscope/nts/client.ex index c887c32..fb20c88 100644 --- a/lib/chronoscope/nts/client.ex +++ b/lib/chronoscope/nts/client.ex @@ -5,20 +5,19 @@ defmodule Chronoscope.NTS.Client do alias Chronoscope.NTS.KeyEstablishmentClient @interval_in_seconds 30 - @date_time Application.compile_env(:chronoscope, Chronoscope.NTS)[:date_time] || DateTime + @date_time Application.compile_env(:chronoscope, :date_time, DateTime) def start_link(server: server, name: name) do GenServer.start_link(__MODULE__, server, name: name) end @impl true - def init(%{host: host, port: port}) do + def init(server) do now = utc_now() {:ok, %{ - host: host, - port: port, + server: server, key_establishment_response: {:error, "initializing"}, last_key_establishment: DateTime.add(now, -@interval_in_seconds, :second) }} @@ -58,9 +57,7 @@ defmodule Chronoscope.NTS.Client do } end - defp server_response(state) do - server = Map.take(state, [:host, :port]) - + defp server_response(%{server: server}) do NTS.TaskSupervisor |> Task.Supervisor.async(fn -> KeyEstablishmentClient.key_establishment(server) end) |> Task.await() diff --git a/lib/chronoscope/nts/key_establishment_client.ex b/lib/chronoscope/nts/key_establishment_client.ex index a72e2c8..cfea965 100644 --- a/lib/chronoscope/nts/key_establishment_client.ex +++ b/lib/chronoscope/nts/key_establishment_client.ex @@ -1,12 +1,12 @@ defmodule Chronoscope.NTS.KeyEstablishmentClient do require Logger - alias Chronoscope.NTS.Certificate + alias Chronoscope.Certificate alias Chronoscope.NTS.KeyEstablishmentRequest alias Chronoscope.NTS.KeyEstablishmentResponse @timeout_in_milliseconds 3000 - @ssl Application.compile_env(:chronoscope, Chronoscope.NTS)[:ssl] || :ssl + @ssl Application.compile_env(:chronoscope, :ssl, :ssl) def key_establishment(%{host: host, port: port}) do case ssl_connect(host, port) do diff --git a/lib/chronoscope_web/controllers/api/v1/nts/key_establishment_controller.ex b/lib/chronoscope_web/controllers/api/v1/nts/key_establishment_controller.ex index ffe5c94..1bcc51c 100644 --- a/lib/chronoscope_web/controllers/api/v1/nts/key_establishment_controller.ex +++ b/lib/chronoscope_web/controllers/api/v1/nts/key_establishment_controller.ex @@ -7,7 +7,7 @@ defmodule ChronoscopeWeb.API.V1.NTS.KeyEstablishmentController do @default_port 4460 @max_host_length 255 - @nts Application.compile_env(:chronoscope, NTS)[:behaviour] || NTS + @nts Application.compile_env(:chronoscope, :nts, NTS) def get(conn, %{"host" => host, "port" => port}) do try do diff --git a/test/chronoscope/nts/certificate_test.exs b/test/chronoscope/certificate_test.exs similarity index 65% rename from test/chronoscope/nts/certificate_test.exs rename to test/chronoscope/certificate_test.exs index f7fa187..f85ce95 100644 --- a/test/chronoscope/nts/certificate_test.exs +++ b/test/chronoscope/certificate_test.exs @@ -1,21 +1,21 @@ -defmodule Chronoscope.NTS.CertificateTest do +defmodule Chronoscope.CertificateTest do use Chronoscope.Case, async: true - alias Chronoscope.NTS.DateTimeMock + alias Chronoscope.Certificate + alias Chronoscope.DateTimeMock - import Chronoscope.NTS.Certificate import Mox setup :verify_on_exit! - describe "Chronoscope.NTS.Certificate.expiration_date()" do + describe "Chronoscope.Certificate.expiration_date()" do test "parses the expiration date of a certificate" do {:ok, expiration, _} = :secp256r1 |> X509.PrivateKey.new_ec() |> X509.Certificate.self_signed("/C=US/ST=CA/L=San Francisco/O=Acme/CN=Test", validity: 12) |> X509.Certificate.to_der() - |> expiration_date() + |> Certificate.expiration_date() |> DateTime.from_iso8601() expiration_time_in_minutes = DateTime.diff(expiration, DateTime.utc_now(), :minute) @@ -25,26 +25,26 @@ defmodule Chronoscope.NTS.CertificateTest do end end - describe "Chronoscope.NTS.Certificate.cert_time_to_iso8601()" do + describe "Chronoscope.Certificate.cert_time_to_iso8601()" do test "converts certificate datetime to iso8601" do DateTimeMock |> expect(:utc_now, fn -> ~U[2024-03-31 01:23:45Z] end) - assert cert_time_to_iso8601("240326110000Z") == "2024-03-26T11:00:00Z" + assert Certificate.cert_time_to_iso8601("240326110000Z") == "2024-03-26T11:00:00Z" end test "handles century rollover" do DateTimeMock |> expect(:utc_now, fn -> ~U[2024-03-31 01:23:45Z] end) - assert cert_time_to_iso8601("010326110000Z") == "2101-03-26T11:00:00Z" + assert Certificate.cert_time_to_iso8601("010326110000Z") == "2101-03-26T11:00:00Z" end test "handles millenium rollover" do DateTimeMock |> expect(:utc_now, fn -> ~U[2999-03-31 01:23:45Z] end) - assert cert_time_to_iso8601("010326110000Z") == "3001-03-26T11:00:00Z" + assert Certificate.cert_time_to_iso8601("010326110000Z") == "3001-03-26T11:00:00Z" end end end diff --git a/test/chronoscope/nts/client_test.exs b/test/chronoscope/nts/client_test.exs index 7d347c3..5571fd7 100644 --- a/test/chronoscope/nts/client_test.exs +++ b/test/chronoscope/nts/client_test.exs @@ -1,9 +1,9 @@ defmodule Chronoscope.NTS.ClientTest do use Chronoscope.Case, async: true - alias Chronoscope.NTS.Certificate - alias Chronoscope.NTS.DateTimeMock - alias Chronoscope.NTS.SSLMock + alias Chronoscope.Certificate + alias Chronoscope.DateTimeMock + alias Chronoscope.SSLMock import Chronoscope.NTS.Client import Mox @@ -22,10 +22,9 @@ defmodule Chronoscope.NTS.ClientTest do assert init(%{host: "localhost", port: 3333}) == {:ok, %{ - host: "localhost", + server: %{host: "localhost", port: 3333}, key_establishment_response: {:error, "initializing"}, - last_key_establishment: ~U[2024-03-31 01:23:15Z], - port: 3333 + last_key_establishment: ~U[2024-03-31 01:23:15Z] }} end end @@ -67,16 +66,14 @@ defmodule Chronoscope.NTS.ClientTest do assert {:reply, {:ok, %{cert_expiration: ^peercert_expiration}}, %{ - host: "localhost", + server: %{host: "localhost", port: 3333}, key_establishment_response: {:ok, %{cert_expiration: ^peercert_expiration}}, - last_key_establishment: ~U[2024-03-31 01:23:45Z], - port: 3333 + last_key_establishment: ~U[2024-03-31 01:23:45Z] }} = handle_call(:key_establishment, nil, %{ - host: "localhost", + server: %{host: "localhost", port: 3333}, key_establishment_response: {:error, "initializing"}, - last_key_establishment: ~U[2024-03-31 01:23:00Z], - port: 3333 + last_key_establishment: ~U[2024-03-31 01:23:00Z] }) end end diff --git a/test/chronoscope/nts/key_establishment_client_test.exs b/test/chronoscope/nts/key_establishment_client_test.exs index bbabe55..9de6a99 100644 --- a/test/chronoscope/nts/key_establishment_client_test.exs +++ b/test/chronoscope/nts/key_establishment_client_test.exs @@ -1,11 +1,11 @@ defmodule Chronoscope.NTS.KeyEstablishmentClientTest do use Chronoscope.Case, async: true - alias Chronoscope.NTS.Certificate + alias Chronoscope.Certificate + alias Chronoscope.NTS.KeyEstablishmentClient alias Chronoscope.NTS.KeyEstablishmentRequest - alias Chronoscope.NTS.SSLMock + alias Chronoscope.SSLMock - import Chronoscope.NTS.KeyEstablishmentClient import Mox setup :verify_on_exit! @@ -23,7 +23,7 @@ defmodule Chronoscope.NTS.KeyEstablishmentClientTest do |> expect(:peercert, fn :socket -> {:ok, peercert()} end) |> expect(:close, fn :socket -> :ok end) - key_establishment(%{host: "localhost", port: 2222}) + KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) end test "handles an empty response" do @@ -37,7 +37,8 @@ defmodule Chronoscope.NTS.KeyEstablishmentClientTest do |> expect(:peercert, fn :socket -> {:ok, peercert} end) |> expect(:close, fn :socket -> :ok end) - assert {:ok, %{cert_expiration: ^peercert_expiration}} = key_establishment(%{host: "localhost", port: 2222}) + assert {:ok, %{cert_expiration: ^peercert_expiration}} = + KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) end test "handles a full response" do @@ -69,7 +70,7 @@ defmodule Chronoscope.NTS.KeyEstablishmentClientTest do next_protocols: ["NTPv4"], port: 1230, server: "127.0.0.1" - }} = key_establishment(%{host: "localhost", port: 2222}) + }} = KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) end test "handles a bad certificate hostname failure" do @@ -78,7 +79,7 @@ defmodule Chronoscope.NTS.KeyEstablishmentClientTest do {:error, {:tls_alert, {:handshake_failure, "connection failed {bad_cert,hostname_check_failed}"}}} end) - assert key_establishment(%{host: "localhost", port: 2222}) == + assert KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) == {:error, "The certificate is NOT trusted. The name in the certificate does not match the expected."} end @@ -88,7 +89,7 @@ defmodule Chronoscope.NTS.KeyEstablishmentClientTest do {:error, {:tls_alert, {:handshake_failure, "unsatisfactory handshake"}}} end) - assert key_establishment(%{host: "localhost", port: 2222}) == {:error, "unsatisfactory handshake"} + assert KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) == {:error, "unsatisfactory handshake"} end test "handles a no application protocol failure" do @@ -97,28 +98,30 @@ defmodule Chronoscope.NTS.KeyEstablishmentClientTest do {:error, {:tls_alert, {:no_application_protocol, "unsatisfactory protocol"}}} end) - assert key_establishment(%{host: "localhost", port: 2222}) == {:error, "unsatisfactory protocol"} + assert KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) == {:error, "unsatisfactory protocol"} end test "handles a timeout failure" do SSLMock |> expect(:connect, fn ~c"localhost", 2222, _tls_options, @timeout -> {:error, :timeout} end) - assert key_establishment(%{host: "localhost", port: 2222}) == {:error, :timeout} + assert KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) == {:error, :timeout} end test "handles an unknown error" do SSLMock |> expect(:connect, fn ~c"localhost", 2222, _tls_options, @timeout -> {:error, {:unsatisfactory, "client"}} end) - assert key_establishment(%{host: "localhost", port: 2222}) == {:error, "{:unsatisfactory, \"client\"}"} + assert KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) == + {:error, "{:unsatisfactory, \"client\"}"} end test "handles an unexpected error" do SSLMock |> expect(:connect, fn ~c"localhost", 2222, _tls_options, @timeout -> {:unsatisfactory, "client"} end) - assert key_establishment(%{host: "localhost", port: 2222}) == {:error, "{:unsatisfactory, \"client\"}"} + assert KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) == + {:error, "{:unsatisfactory, \"client\"}"} end end end diff --git a/test/chronoscope/nts_test.exs b/test/chronoscope/nts_test.exs index f7ec5dd..427bee3 100644 --- a/test/chronoscope/nts_test.exs +++ b/test/chronoscope/nts_test.exs @@ -1,19 +1,18 @@ defmodule Chronoscope.NTSTest do use Chronoscope.Case, async: true + alias Chronoscope.DynamicSupervisorMock + alias Chronoscope.GenServerMock alias Chronoscope.NTS - alias Chronoscope.NTS.DynamicSupervisorMock - alias Chronoscope.NTS.GenServerMock - alias Chronoscope.NTS.RegistryMock + alias Chronoscope.RegistryMock - import Chronoscope.NTS import Mox setup :verify_on_exit! describe "Chronoscope.NTS.healthy?()" do test "is healthy" do - assert healthy?() == true + assert NTS.healthy?() == true end end @@ -22,7 +21,7 @@ defmodule Chronoscope.NTSTest do DynamicSupervisorMock |> expect(:which_children, fn _ -> [] end) - assert list() == [] + assert NTS.list() == [] end test "lists all children" do @@ -33,7 +32,7 @@ defmodule Chronoscope.NTSTest do |> expect(:call, fn 2, :list -> :one end) |> expect(:call, fn 6, :list -> :two end) - assert list() == [:one, :two] + assert NTS.list() == [:one, :two] end end @@ -42,7 +41,7 @@ defmodule Chronoscope.NTSTest do RegistryMock |> expect(:lookup, fn _, _ -> [] end) - assert remove("localhost", 1111) == {:error, :not_found} + assert NTS.remove("localhost", 1111) == {:error, :not_found} end test "removes a client" do @@ -52,7 +51,7 @@ defmodule Chronoscope.NTSTest do GenServerMock |> expect(:call, fn 1, :terminate -> :terminating end) - assert remove("localhost", 1111) == {:ok, :terminating} + assert NTS.remove("localhost", 1111) == {:ok, :terminating} end end @@ -62,11 +61,11 @@ defmodule Chronoscope.NTSTest do |> expect(:lookup, fn _, _ -> [] end) DynamicSupervisorMock - |> expect(:start_child, fn NTS.DynamicSupervisor, - {NTS.Client, + |> expect(:start_child, fn Chronoscope.NTS.DynamicSupervisor, + {Chronoscope.NTS.Client, [ server: %{host: "localhost", port: 1111}, - name: {:via, RegistryMock, {NTS.Registry, "localhost:1111"}} + name: {:via, RegistryMock, {Chronoscope.NTS.Registry, "localhost:1111"}} ]} -> {:ok, 1} end) @@ -74,7 +73,7 @@ defmodule Chronoscope.NTSTest do GenServerMock |> expect(:call, fn 1, :key_establishment -> :result end) - assert key_establishment("localhost", 1111) == :result + assert NTS.key_establishment("localhost", 1111) == :result end test "reuses an existing client" do @@ -84,7 +83,7 @@ defmodule Chronoscope.NTSTest do GenServerMock |> expect(:call, fn 1, :key_establishment -> :result end) - assert key_establishment("localhost", 1111) == :result + assert NTS.key_establishment("localhost", 1111) == :result end end end diff --git a/test/chronoscope_web/controllers/api/v1/nts/key_establishment_controller_test.exs b/test/chronoscope_web/controllers/api/v1/nts/key_establishment_controller_test.exs index a873e10..726509d 100644 --- a/test/chronoscope_web/controllers/api/v1/nts/key_establishment_controller_test.exs +++ b/test/chronoscope_web/controllers/api/v1/nts/key_establishment_controller_test.exs @@ -1,6 +1,8 @@ defmodule ChronoscopeWeb.API.V1.NTS.KeyEstablishmentControllerTest do use ChronoscopeWeb.ConnCase, async: true + alias Chronoscope.NTSMock + import Mox setup :verify_on_exit! @@ -16,7 +18,7 @@ defmodule ChronoscopeWeb.API.V1.NTS.KeyEstablishmentControllerTest do end test "truncates the host name", %{conn: conn} do - Chronoscope.NTS.BehaviourMock + NTSMock |> expect( :key_establishment, fn "test.example.com.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456", @@ -36,7 +38,7 @@ defmodule ChronoscopeWeb.API.V1.NTS.KeyEstablishmentControllerTest do end test "uses the given port number", %{conn: conn} do - Chronoscope.NTS.BehaviourMock + NTSMock |> expect(:key_establishment, fn "localhost", 4461 -> {:ok, %{status: :ok}} end) response = @@ -75,7 +77,7 @@ defmodule ChronoscopeWeb.API.V1.NTS.KeyEstablishmentControllerTest do end test "returns an empty successful response", %{conn: conn} do - Chronoscope.NTS.BehaviourMock + NTSMock |> expect(:key_establishment, fn "localhost", 4460 -> {:ok, %{status: :ok}} end) response = @@ -87,7 +89,7 @@ defmodule ChronoscopeWeb.API.V1.NTS.KeyEstablishmentControllerTest do end test "returns a full successful response", %{conn: conn} do - Chronoscope.NTS.BehaviourMock + NTSMock |> expect(:key_establishment, fn "localhost", 4460 -> {:ok, %{cookies: [[], [], []], cookie_length: 300}} end) diff --git a/test/support/case.ex b/test/support/case.ex index ec1a8ba..47a4d00 100644 --- a/test/support/case.ex +++ b/test/support/case.ex @@ -8,7 +8,7 @@ defmodule Chronoscope.Case do end setup _tags do - Mox.stub_with(Chronoscope.NTS.DateTimeMock, Chronoscope.DateTime.Stub) + Mox.stub_with(Chronoscope.DateTimeMock, Chronoscope.DateTimeStub) :ok end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index dcfe251..97ff838 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -32,7 +32,7 @@ defmodule ChronoscopeWeb.ConnCase do end setup _tags do - Mox.stub_with(Chronoscope.NTS.DateTimeMock, Chronoscope.DateTime.Stub) + Mox.stub_with(Chronoscope.DateTimeMock, Chronoscope.DateTimeStub) {:ok, conn: Phoenix.ConnTest.build_conn()} end end diff --git a/test/support/mocks.ex b/test/support/mocks.ex index 53a182e..dcc3552 100644 --- a/test/support/mocks.ex +++ b/test/support/mocks.ex @@ -1,6 +1,7 @@ -Mox.defmock(Chronoscope.NTS.BehaviourMock, for: Chronoscope.NTS.Behaviour) -Mox.defmock(Chronoscope.NTS.DateTimeMock, for: Chronoscope.DateTime.Behaviour) -Mox.defmock(Chronoscope.NTS.SSLMock, for: Chronoscope.SSL.Behaviour) -Mox.defmock(Chronoscope.NTS.RegistryMock, for: Chronoscope.Registry.Behaviour) -Mox.defmock(Chronoscope.NTS.DynamicSupervisorMock, for: Chronoscope.DynamicSupervisor.Behaviour) -Mox.defmock(Chronoscope.NTS.GenServerMock, for: Chronoscope.GenServer.Behaviour) +Mox.defmock(Chronoscope.DateTimeMock, for: Chronoscope.DateTime.Behaviour) +Mox.defmock(Chronoscope.SSLMock, for: Chronoscope.SSL.Behaviour) +Mox.defmock(Chronoscope.RegistryMock, for: Chronoscope.Registry.Behaviour) +Mox.defmock(Chronoscope.DynamicSupervisorMock, for: Chronoscope.DynamicSupervisor.Behaviour) +Mox.defmock(Chronoscope.GenServerMock, for: Chronoscope.GenServer.Behaviour) +Mox.defmock(Chronoscope.NTSMock, for: Chronoscope.NTS.Behaviour) +Mox.defmock(Chronoscope.GeminiMock, for: Chronoscope.Gemini.Behaviour) diff --git a/test/support/stubs.ex b/test/support/stubs.ex index 92ec93e..9aa6cba 100644 --- a/test/support/stubs.ex +++ b/test/support/stubs.ex @@ -1,4 +1,4 @@ -defmodule Chronoscope.DateTime.Stub do +defmodule Chronoscope.DateTimeStub do @behaviour Chronoscope.DateTime.Behaviour def utc_now(), do: DateTime.utc_now() end