Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions test/default_session_registry_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule ExFix.DefaultSessionRegistryTest do
use ExUnit.Case

alias ExFix.DefaultSessionRegistry

test "session_on_init replies according to status" do
table = :ex_fix_registry
:ets.delete_all_objects(table)

assert {:error, :notfound} = DefaultSessionRegistry.session_on_init("s1")

DefaultSessionRegistry.session_update_status("s2", :connecting)
assert :ok = DefaultSessionRegistry.session_on_init("s2")

DefaultSessionRegistry.session_update_status("s3", :connected)
assert :wait_to_reconnect = DefaultSessionRegistry.session_on_init("s3")

DefaultSessionRegistry.session_update_status("s4", :disconnecting)
assert {:error, :disconnected} = DefaultSessionRegistry.session_on_init("s4")
end

test "handles process DOWN messages" do
table = :ex_fix_registry
:ets.delete_all_objects(table)

DefaultSessionRegistry.session_update_status("dn1", :connecting)
:ok = DefaultSessionRegistry.session_on_init("dn1")
state = :sys.get_state(DefaultSessionRegistry)
ref1 = Enum.find_value(state.monitor_map, fn {ref, name} -> if name == "dn1", do: ref end)
send(DefaultSessionRegistry, {:DOWN, ref1, :process, self(), :normal})
Process.sleep(10)
assert DefaultSessionRegistry.get_session_status("dn1") == :disconnected

DefaultSessionRegistry.session_update_status("dn2", :connecting)
:ok = DefaultSessionRegistry.session_on_init("dn2")
state = :sys.get_state(DefaultSessionRegistry)
ref2 = Enum.find_value(state.monitor_map, fn {ref, name} -> if name == "dn2", do: ref end)
send(DefaultSessionRegistry, {:DOWN, ref2, :process, self(), :shutdown})
Process.sleep(10)
assert DefaultSessionRegistry.get_session_status("dn2") == :reconnecting
end
end
57 changes: 57 additions & 0 deletions test/ex_fix_api_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule ExFix.ApiTest do
use ExUnit.Case

alias ExFix.SessionConfig
alias ExFix.TestHelper.FixEmptySessionHandler

defmodule CaptureRegistry do
@behaviour ExFix.SessionRegistry

def get_session_status(_), do: :disconnected

def start_session(name, %SessionConfig{} = config) do
send(self(), {:start_session, name, config})
:ok
end

def stop_session(name) do
send(self(), {:stop_session, name})
:ok
end

def session_on_init(_), do: :ok
def session_update_status(_, _), do: :ok
end

test "start_session_initiator builds config and delegates" do
ExFix.start_session_initiator(
"test_sess",
"S",
"T",
FixEmptySessionHandler,
session_registry: CaptureRegistry,
hostname: "myhost",
port: 12_345,
log_incoming_msg: false
)

assert_receive {:start_session, "test_sess", config}
assert %SessionConfig{name: "test_sess", sender_comp_id: "S", target_comp_id: "T"} = config
assert config.hostname == "myhost"
assert config.port == 12_345
assert config.log_incoming_msg == false

ExFix.stop_session("test_sess", CaptureRegistry)
assert_receive {:stop_session, "test_sess"}
end

test "start_session_initiator uses defaults" do
ExFix.start_session_initiator("defaults", "S", "T", FixEmptySessionHandler, session_registry: CaptureRegistry)

assert_receive {:start_session, "defaults", config}
assert config.hostname == "localhost"
assert config.port == 9876
assert config.log_incoming_msg == true
assert config.transport_mod == :gen_tcp
end
end
7 changes: 5 additions & 2 deletions test/in_message_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ defmodule ExFix.InMessageTest do
%{bin_msg: bin_msg}
end

test "InMessage tests", %{bin_msg: bin_msg} do
test "get_field returns value", %{bin_msg: bin_msg} do
fix_msg = Parser.parse1(bin_msg, Dictionary, 12_345)
assert InMessage.get_field(fix_msg, "49") == "MARKET"
assert InMessage.get_field(fix_msg, "52") == "20161007-16:28:50.802"
end

# assert InMessage.get_field(fix_msg, "SendingTime", TestDict) == Calendar.DateTime.Parse.rfc3339_utc
test "get_field returns nil for missing field", %{bin_msg: bin_msg} do
fix_msg = Parser.parse1(bin_msg, Dictionary, 12_345)
assert InMessage.get_field(fix_msg, "9999") == nil
end
end
17 changes: 17 additions & 0 deletions test/parser_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,23 @@ defmodule ExFix.ParserTest do
assert fix_msg.original_fix_msg == data
end

test "Parse message with invalid begin string" do
data = msg("8=FIX4.2|9=12|10=000|")
fix_msg = Parser.parse1(data, Dictionary, 1)
assert fix_msg.valid == false
assert fix_msg.error_reason == :begin_string_error
assert fix_msg.original_fix_msg == data
end

test "Parse message with unexpected seqnum" do
now = DateTime.from_naive!(~N[2017-07-17 17:50:56], "Etc/UTC")
data = build_message("0", 10, "SENDER", "TARGET", now)
fix_msg = Parser.parse1(data, Dictionary, 5)
assert fix_msg.valid == false
assert fix_msg.error_reason == :unexpected_seqnum
assert fix_msg.seqnum == 10
end

test "Parse message - stage 1 - subject with 2 fields" do
data =
msg(
Expand Down
14 changes: 14 additions & 0 deletions test/session_sup_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
defmodule ExFix.SessionSupTest do
use ExUnit.Case

test "start_link when already started" do
assert {:error, {:already_started, _}} = ExFix.SessionSup.start_link([])
end

test "dynamic supervisor can start child" do
{:ok, pid} = DynamicSupervisor.start_child(ExFix.SessionSup, {Task, fn -> :timer.sleep(10) end})
assert is_pid(pid)
ref = Process.monitor(pid)
assert_receive {:DOWN, ^ref, :process, ^pid, :normal}, 50
end
end
36 changes: 36 additions & 0 deletions test/session_worker_failure_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
defmodule ExFix.SessionWorkerFailureTest do
use ExUnit.Case

alias ExFix.SessionConfig
alias ExFix.TestHelper.FixEmptySessionHandler

defmodule FailTransport do
def connect(_host, _port, _opts), do: {:error, :econnrefused}
def send(_conn, _data), do: :ok
def close(_conn), do: :ok
end

alias ExFix.TestHelper.TestSessionRegistry

test "worker reports reconnecting on connection failure" do
Process.flag(:trap_exit, true)
{:ok, _} = TestSessionRegistry.start_link()
config = %SessionConfig{
name: "fail1",
mode: :initiator,
sender_comp_id: "S",
target_comp_id: "T",
session_handler: FixEmptySessionHandler,
transport_mod: FailTransport,
transport_options: [],
log_incoming_msg: false,
log_outgoing_msg: false,
reconnect_interval: 0
}

{:ok, pid} = ExFix.SessionWorker.start_link(config, TestSessionRegistry)
ref = Process.monitor(pid)
assert_receive {:DOWN, ^ref, :process, ^pid, :econnrefused}
assert TestSessionRegistry.get_session_status("fail1") == :reconnecting
end
end
61 changes: 61 additions & 0 deletions test/session_worker_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,65 @@ defmodule ExFix.SessionWorkerTest do
SessionWorker.stop("sessiontest1")
assert TestSessionRegistry.get_session_status("sessiontest1") == :disconnected
end

test "higher seqnum triggers resend request" do

cfg = %SessionConfig{
name: "resend_sess",
mode: :initiator,
sender_comp_id: "SENDER",
target_comp_id: "TARGET",
session_handler: FixEmptySessionHandler,
dictionary: DefaultDictionary,
transport_mod: TestTransport,
transport_options: [test_pid: self()]
}

{:ok, _pid} = SessionWorker.start_link(cfg, TestSessionRegistry)
assert_receive {:data, _logon_msg}

now = DateTime.utc_now()
logon = %MessageToSend{seqnum: 1, sender: "TARGET", orig_sending_time: now, target: "SENDER", msg_type: "A", body: []}
TestTransport.receive_data("resend_sess", Serializer.serialize(logon, now))
Process.sleep(20)

msg = %MessageToSend{seqnum: 12, sender: "TARGET", orig_sending_time: now, target: "SENDER", msg_type: "8", body: []}
TestTransport.receive_data("resend_sess", Serializer.serialize(msg, now))

assert_receive {:data, resend}
parsed = Parser.parse(resend, DefaultDictionary, 1)
assert parsed.msg_type == "2"
SessionWorker.stop("resend_sess")
end

test "invalid SenderCompID triggers logout" do
cfg = %SessionConfig{
name: "badcomp",
mode: :initiator,
sender_comp_id: "SENDER",
target_comp_id: "TARGET",
session_handler: FixEmptySessionHandler,
dictionary: DefaultDictionary,
transport_mod: TestTransport,
transport_options: [test_pid: self()]
}

{:ok, pid} = SessionWorker.start_link(cfg, TestSessionRegistry)
ref = Process.monitor(pid)
assert_receive {:data, _}

now = DateTime.utc_now()
logon = %MessageToSend{seqnum: 1, sender: "TARGET", orig_sending_time: now, target: "SENDER", msg_type: "A", body: []}
TestTransport.receive_data("badcomp", Serializer.serialize(logon, now))
Process.sleep(20)

msg = %MessageToSend{seqnum: 2, sender: "OTHER", orig_sending_time: now, target: "SENDER", msg_type: "8", body: []}
TestTransport.receive_data("badcomp", Serializer.serialize(msg, now))

assert_receive {:data, reject}
assert_receive {:data, logout}
assert Parser.parse(reject, DefaultDictionary, 1).msg_type == "3"
assert Parser.parse(logout, DefaultDictionary, 1).msg_type == "5"
assert_receive {:DOWN, ^ref, :process, ^pid, :normal}, 1000
end
end