Add gemini client
This commit is contained in:
parent
3e017e6c78
commit
6622913d52
|
@ -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
|
||||
|
|
|
@ -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
|
||||
]
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
defmodule Chronoscope.Gemini.Request do
|
||||
def create(resource) do
|
||||
"gemini://#{resource.host}:#{resource.port}#{resource.path}\r\n"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
defmodule Chronoscope.Gemini.Response do
|
||||
def parse(response) do
|
||||
# TODO
|
||||
%{status: 20, type: "text/gemini", body: response}
|
||||
end
|
||||
end
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue