agent_fix 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +12 -0
- data/CONTRIBUTION_GUIDELINES.md +22 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +86 -0
- data/LICENSE.txt +14 -0
- data/QUICKFIX_LICENSE.txt +46 -0
- data/README.md +210 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/agent_fix.gemspec +83 -0
- data/config/fix_agents.rb +14 -0
- data/config/logging.properties +6 -0
- data/features/inspect_all.feature +28 -0
- data/features/scope.feature +68 -0
- data/features/step_definitions/steps.rb +32 -0
- data/features/support/FIX42.xml +2670 -0
- data/features/support/env.rb +29 -0
- data/lib/agent_fix/agent.rb +157 -0
- data/lib/agent_fix/configuration.rb +46 -0
- data/lib/agent_fix/cucumber/report.rb +79 -0
- data/lib/agent_fix/cucumber.rb +146 -0
- data/lib/agent_fix/message_cache.rb +31 -0
- data/lib/agent_fix.rb +75 -0
- data/spec/agent_fix/configuration_spec.rb +12 -0
- data/spec/agent_fix/message_cache_spec.rb +16 -0
- data/spec/spec_helper.rb +8 -0
- metadata +189 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
$: << File.expand_path("../../../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'agent_fix/cucumber'
|
4
|
+
require 'agent_fix/cucumber/report'
|
5
|
+
require 'fix_spec/cucumber'
|
6
|
+
require 'rspec'
|
7
|
+
require 'anticipate'
|
8
|
+
|
9
|
+
Around('@inspect_all') do |scenario, block|
|
10
|
+
old_scope = AgentFIX.include_session_level?
|
11
|
+
AgentFIX::include_session_level = true
|
12
|
+
|
13
|
+
#hard reset, forces logout/logon
|
14
|
+
AgentFIX.hard_reset
|
15
|
+
block.call
|
16
|
+
AgentFIX::include_session_level = old_scope
|
17
|
+
end
|
18
|
+
|
19
|
+
Before('~@inspect_all') do
|
20
|
+
AgentFIX.reset
|
21
|
+
end
|
22
|
+
|
23
|
+
FIXSpec::data_dictionary= quickfix.DataDictionary.new "features/support/FIX42.xml"
|
24
|
+
|
25
|
+
World(Anticipate)
|
26
|
+
|
27
|
+
AgentFIX.start
|
28
|
+
at_exit {AgentFIX.stop}
|
29
|
+
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'agent_fix/message_cache'
|
3
|
+
|
4
|
+
module AgentFIX
|
5
|
+
class Agent
|
6
|
+
include quickfix.Application
|
7
|
+
|
8
|
+
attr_reader :name, :connection_type
|
9
|
+
attr_accessor :default, :session
|
10
|
+
|
11
|
+
attr_accessor :bookmark
|
12
|
+
|
13
|
+
def initialize name, connection_type
|
14
|
+
@name = name
|
15
|
+
@connection_type = connection_type
|
16
|
+
@logged_on = false
|
17
|
+
@bookmark = 0
|
18
|
+
@all_messages = MessageCache.new
|
19
|
+
@logger = Java::org.slf4j.LoggerFactory.getLogger("AgentFIX.Agent")
|
20
|
+
end
|
21
|
+
|
22
|
+
def init
|
23
|
+
parse_settings
|
24
|
+
@connector = case @connection_type
|
25
|
+
when :acceptor then quickfix.SocketAcceptor.new(self, @storeFactory, @settings, @logFactory, @messageFactory)
|
26
|
+
when :initiator then quickfix.SocketInitiator.new(self, @storeFactory, @settings, @logFactory, @messageFactory)
|
27
|
+
else nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def onCreate(sessionId)
|
32
|
+
@default_session = sessionId
|
33
|
+
end
|
34
|
+
|
35
|
+
def onLogon(sessionId)
|
36
|
+
@logger.debug "#{@name} onLogon: #{sessionId.to_s}"
|
37
|
+
|
38
|
+
lock.synchronize do
|
39
|
+
@logged_on = true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def onLogout(sessionId)
|
44
|
+
@logger.debug "#{@name} onLogout: #{sessionId.to_s}"
|
45
|
+
|
46
|
+
lock.synchronize do
|
47
|
+
@logged_on = false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def toApp(message, sessionId)
|
52
|
+
@logger.debug "#{@name} toApp #{sessionId.to_s}: #{message.to_s.gsub("","|")}"
|
53
|
+
@all_messages.add_message(message:message,sent:true,admin:false)
|
54
|
+
end
|
55
|
+
|
56
|
+
def fromApp(message, sessionId)
|
57
|
+
@logger.debug "#{@name} fromApp #{sessionId.to_s}: #{message.to_s.gsub("","|")}"
|
58
|
+
@all_messages.add_message(message:message,sent:false,admin:false)
|
59
|
+
end
|
60
|
+
|
61
|
+
def toAdmin(message, sessionId)
|
62
|
+
@logger.debug "#{@name} toAdmin #{sessionId.to_s}: #{message.to_s.gsub("","|")}"
|
63
|
+
@all_messages.add_message(message:message,sent:true,admin:true)
|
64
|
+
end
|
65
|
+
|
66
|
+
def fromAdmin(message, sessionId)
|
67
|
+
@logger.debug "#{@name} fromAdmin #{sessionId.to_s}: #{message.to_s.gsub("","|")}"
|
68
|
+
@all_messages.add_message(message:message,sent:false,admin:true)
|
69
|
+
end
|
70
|
+
|
71
|
+
def loggedOn?
|
72
|
+
lock.synchronize do
|
73
|
+
return @logged_on
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def sendToTarget msg
|
78
|
+
msg.getHeader.setField(quickfix.field.BeginString.new(@default_session.getBeginString))
|
79
|
+
msg.getHeader.setField(quickfix.field.TargetCompID.new(@default_session.getTargetCompID))
|
80
|
+
msg.getHeader.setField(quickfix.field.SenderCompID.new(@default_session.getSenderCompID))
|
81
|
+
|
82
|
+
quickfix.Session.sendToTarget(msg)
|
83
|
+
end
|
84
|
+
|
85
|
+
def reset
|
86
|
+
clear_state!
|
87
|
+
end
|
88
|
+
|
89
|
+
def start
|
90
|
+
@connector.start
|
91
|
+
end
|
92
|
+
|
93
|
+
def stop
|
94
|
+
@connector.stop
|
95
|
+
clear_state!
|
96
|
+
end
|
97
|
+
|
98
|
+
def history opts={}
|
99
|
+
opts[:since] ||= 0
|
100
|
+
opts[:received_only] ||= false
|
101
|
+
opts[:include_session]||= AgentFIX::include_session_level?
|
102
|
+
|
103
|
+
indexed_msgs = []
|
104
|
+
@all_messages.messages.each_with_index { |m,i| indexed_msgs << m.merge(index:i) }
|
105
|
+
indexed_msgs = indexed_msgs.slice(opts[:since], indexed_msgs.length)
|
106
|
+
|
107
|
+
if opts[:received_only]
|
108
|
+
indexed_msgs = indexed_msgs.find_all {|m| !m[:sent]}
|
109
|
+
end
|
110
|
+
|
111
|
+
unless opts[:include_session]
|
112
|
+
indexed_msgs = indexed_msgs.find_all {|m| !m[:admin]}
|
113
|
+
end
|
114
|
+
|
115
|
+
indexed_msgs
|
116
|
+
end
|
117
|
+
|
118
|
+
def messages_received opts = {}
|
119
|
+
history opts.merge(:received_only=>true)
|
120
|
+
end
|
121
|
+
|
122
|
+
protected
|
123
|
+
|
124
|
+
def clear_state!
|
125
|
+
@all_messages.clear!
|
126
|
+
@bookmark = 0
|
127
|
+
end
|
128
|
+
|
129
|
+
def parse_settings
|
130
|
+
session_settings = "[DEFAULT]\n"
|
131
|
+
session_settings << "ConnectionType=#{@connection_type}\n"
|
132
|
+
|
133
|
+
@default ||= {}
|
134
|
+
AgentFIX::session_defaults.merge(@default).each do |k,v|
|
135
|
+
session_settings << "#{k}=#{v}\n"
|
136
|
+
end
|
137
|
+
|
138
|
+
unless @session.nil?
|
139
|
+
session_settings << "[SESSION]\n"
|
140
|
+
@session.each { |k,v| session_settings << "#{k}=#{v}\n"}
|
141
|
+
end
|
142
|
+
|
143
|
+
@logger.info "Settings for #{@name}: #{session_settings}"
|
144
|
+
|
145
|
+
@storeFactory = quickfix.MemoryStoreFactory.new()
|
146
|
+
@messageFactory = quickfix.DefaultMessageFactory.new()
|
147
|
+
@settings = quickfix.SessionSettings.new( Java::java.io.ByteArrayInputStream.new(session_settings.to_java_bytes) )
|
148
|
+
@logFactory = quickfix.FileLogFactory.new(@settings)
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def lock
|
154
|
+
@lock||=Mutex.new
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module AgentFIX
|
2
|
+
module Configuration
|
3
|
+
REASONABLE_SESSION_DEFAULTS = {
|
4
|
+
StartTime: "00:00:00",
|
5
|
+
EndTime: "00:00:00",
|
6
|
+
FileLogPath: "fixlog",
|
7
|
+
HeartBtInt: 60
|
8
|
+
}
|
9
|
+
|
10
|
+
def include_session_level=(opt)
|
11
|
+
@include_session_level = opt
|
12
|
+
end
|
13
|
+
|
14
|
+
def include_session_level?
|
15
|
+
@include_session_level ||=false
|
16
|
+
end
|
17
|
+
|
18
|
+
def cucumber_sleep_seconds=(secs)
|
19
|
+
@cucumber_sleep_seconds = secs
|
20
|
+
end
|
21
|
+
|
22
|
+
def cucumber_sleep_seconds
|
23
|
+
@cucumber_sleep_seconds ||= 0.5
|
24
|
+
end
|
25
|
+
|
26
|
+
def cucumber_retries=(retries)
|
27
|
+
@cucumber_retries = retries
|
28
|
+
end
|
29
|
+
|
30
|
+
def cucumber_retries
|
31
|
+
@cucumber_retries ||= 10
|
32
|
+
end
|
33
|
+
|
34
|
+
def session_defaults=(defaults)
|
35
|
+
@session_defaults = defaults
|
36
|
+
end
|
37
|
+
|
38
|
+
def session_defaults
|
39
|
+
@session_defaults ||= REASONABLE_SESSION_DEFAULTS
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset_config
|
43
|
+
instance_variables.each{|ivar| remove_instance_variable(ivar) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class String
|
2
|
+
# colorization
|
3
|
+
def colorize(color_code)
|
4
|
+
"\e[#{color_code}m#{self}\e[0m"
|
5
|
+
end
|
6
|
+
|
7
|
+
def red
|
8
|
+
colorize(31)
|
9
|
+
end
|
10
|
+
|
11
|
+
def green
|
12
|
+
colorize(32)
|
13
|
+
end
|
14
|
+
|
15
|
+
def yellow
|
16
|
+
colorize(33)
|
17
|
+
end
|
18
|
+
|
19
|
+
def pink
|
20
|
+
colorize(35)
|
21
|
+
end
|
22
|
+
|
23
|
+
def white
|
24
|
+
colorize(37)
|
25
|
+
end
|
26
|
+
|
27
|
+
def blue
|
28
|
+
colorize(34)
|
29
|
+
end
|
30
|
+
|
31
|
+
def magenta
|
32
|
+
colorize(35)
|
33
|
+
end
|
34
|
+
|
35
|
+
def cyan
|
36
|
+
colorize(36)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def print_results agent
|
41
|
+
STDERR.puts "\nMessages for ".yellow + agent.name.to_s.white + ": ".yellow
|
42
|
+
|
43
|
+
agent.history(:include_session=>true).each do |msg|
|
44
|
+
if msg[:sent]
|
45
|
+
STDERR.puts "\tsent >>\t #{msg[:message].to_s.gsub!(/\001/, '|')}".green
|
46
|
+
else
|
47
|
+
outbound = "\trecv <<\t #{msg[:message].to_s.gsub!(/\001/, '|')}"
|
48
|
+
|
49
|
+
if @message!=nil and msg[:message] == @message
|
50
|
+
STDERR.puts outbound.red
|
51
|
+
else
|
52
|
+
if msg[:index] >= @agent.bookmark
|
53
|
+
STDERR.puts outbound.blue
|
54
|
+
else
|
55
|
+
|
56
|
+
if @message_scope.include? msg[:message]
|
57
|
+
STDERR.puts outbound.pink
|
58
|
+
else
|
59
|
+
STDERR.puts outbound.green
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
After do |scenario|
|
68
|
+
if scenario.failed? then
|
69
|
+
#last selected agent gets priority
|
70
|
+
unless @agent.nil?
|
71
|
+
print_results(@agent)
|
72
|
+
end
|
73
|
+
|
74
|
+
AgentFIX.agents_hash.values.each do |agent|
|
75
|
+
next if agent == @agent
|
76
|
+
print_results(agent)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require File.expand_path("../../agent_fix", __FILE__)
|
2
|
+
|
3
|
+
require 'fix_spec/builder'
|
4
|
+
require 'fix_spec/cucumber'
|
5
|
+
|
6
|
+
module FIXMessageCache
|
7
|
+
# accessor for fix_spec
|
8
|
+
def last_fix
|
9
|
+
@message
|
10
|
+
end
|
11
|
+
|
12
|
+
def recall_agent agent
|
13
|
+
agent = AgentFIX.agents_hash[agent.to_sym]
|
14
|
+
throw "Unknown agent #{agent}" if agent.nil?
|
15
|
+
|
16
|
+
agent
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
World(FIXMessageCache)
|
21
|
+
|
22
|
+
def anticipate_fix
|
23
|
+
sleeping(AgentFIX.cucumber_sleep_seconds).seconds.between_tries.failing_after(AgentFIX.cucumber_retries).tries do
|
24
|
+
yield
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
When(/^I send the following FIX message(?:s)? from agent "(.*?)":$/) do |agent, fix|
|
29
|
+
messages = fix.split("\n")
|
30
|
+
|
31
|
+
messages.each do |msg|
|
32
|
+
steps %Q{
|
33
|
+
Given the following fix message:
|
34
|
+
"""
|
35
|
+
#{msg}
|
36
|
+
"""
|
37
|
+
}
|
38
|
+
|
39
|
+
AgentFIX.agents_hash[agent.to_sym].sendToTarget(FIXSpec::Builder.message)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Then(/^I should receive (exactly )?(\d+)(?: FIX|fix)? messages(?: (?:on|over) FIX)?(?: of type "(.*?)")? with agent "(.*?)"$/) do |exact, count, type, agent|
|
44
|
+
@agent = recall_agent(agent)
|
45
|
+
count = count.to_i
|
46
|
+
|
47
|
+
scope = []
|
48
|
+
anticipate_fix do
|
49
|
+
messages = @agent.messages_received :since=>@agent.bookmark
|
50
|
+
|
51
|
+
if exact
|
52
|
+
(messages.length).should be == count, "Expected exactly #{count} messages, but got #{messages.length}"
|
53
|
+
else
|
54
|
+
(messages.length).should be >= count, "Expected #{count} messages, but got #{messages.length}"
|
55
|
+
end
|
56
|
+
|
57
|
+
scope=messages.slice(0, count)
|
58
|
+
|
59
|
+
unless type.nil?
|
60
|
+
unless FIXSpec::data_dictionary.nil?
|
61
|
+
type = FIXSpec::data_dictionary.get_msg_type(type)
|
62
|
+
end
|
63
|
+
|
64
|
+
@scope.each do |msg|
|
65
|
+
msg[:message].header.get_string(35).should == type
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
unless scope.empty?
|
71
|
+
@agent.bookmark = scope.last[:index]+1
|
72
|
+
end
|
73
|
+
|
74
|
+
@message_scope=scope.collect {|m| m[:message]}
|
75
|
+
|
76
|
+
#if we only requested one message for the scope, inspect that message
|
77
|
+
if count == 1
|
78
|
+
@message = @message_scope.first
|
79
|
+
else
|
80
|
+
@message = nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Then(/^I should not receive any(?: more)?(?: FIX| fix)? messages with agent "(.*?)"$/) do |agent|
|
85
|
+
steps %Q{Then I should receive exactly 0 FIX messages with agent "#{agent}"}
|
86
|
+
end
|
87
|
+
|
88
|
+
Then(/^I should receive a(?: FIX| fix)? message(?: (?:on|over) FIX)?(?: of type "(.*?)")? with agent "(.*?)"$/) do |type, agent|
|
89
|
+
if type.nil?
|
90
|
+
steps %Q{Then I should receive 1 FIX messages with agent "#{agent}"}
|
91
|
+
else
|
92
|
+
steps %Q{Then I should receive 1 FIX messages of type "#{type}" with agent "#{agent}"}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
When(/^I inspect the (\d+)(?:.*?)(?: FIX| fix)? message$/) do |index|
|
97
|
+
index = index.to_i-1
|
98
|
+
|
99
|
+
@message_scope.should_not be_nil, "No message scope defined"
|
100
|
+
@message_scope.length.should be >index, "There are only #{@message_scope.length} messages in the scope"
|
101
|
+
@message = @message_scope[index]
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
Then(/^the (\d+)(?:.*?)(?: FIX| fix)? message should have the following:$/) do |index, table|
|
106
|
+
table_raw =""
|
107
|
+
table.raw.each do |path, val|
|
108
|
+
table_raw << "|#{path}|#{val}|\n"
|
109
|
+
end
|
110
|
+
|
111
|
+
steps %Q{
|
112
|
+
When I inspect the #{index}th FIX message
|
113
|
+
Then the FIX message should have the following:
|
114
|
+
#{table_raw}
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
Then(/^the(?: FIX|fix)? messages should include(?: a message with)? the following:$/) do |table|
|
119
|
+
@message_scope.should_not be_nil, "No message scope defined"
|
120
|
+
|
121
|
+
table_raw =""
|
122
|
+
table.raw.each do |path, val|
|
123
|
+
table_raw << "|#{path}|#{val}|\n"
|
124
|
+
end
|
125
|
+
|
126
|
+
found = false
|
127
|
+
error_accum = ""
|
128
|
+
index = 1
|
129
|
+
@message_scope.each do |m|
|
130
|
+
@message = m
|
131
|
+
begin
|
132
|
+
steps %Q{
|
133
|
+
When I inspect the #{index}th FIX message
|
134
|
+
Then the FIX message should have the following:
|
135
|
+
#{table_raw}
|
136
|
+
}
|
137
|
+
found = true
|
138
|
+
rescue Exception => e
|
139
|
+
error_accum << "\n#{m.to_s.gsub!(/\001/, '|')}\n #{e}"
|
140
|
+
end
|
141
|
+
index += 1
|
142
|
+
end
|
143
|
+
|
144
|
+
found.should be_true, "Message not included in FIX messages\n #{error_accum}"
|
145
|
+
|
146
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'thread'
|
2
|
+
module AgentFIX
|
3
|
+
class MessageCache
|
4
|
+
def messages
|
5
|
+
lock.synchronize do
|
6
|
+
return msgs.dup
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_message msg
|
11
|
+
lock.synchronize do
|
12
|
+
msgs << msg
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def clear!
|
17
|
+
lock.synchronize do
|
18
|
+
msgs.clear
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def msgs
|
24
|
+
@messages||=[]
|
25
|
+
end
|
26
|
+
|
27
|
+
def lock
|
28
|
+
@lock||=Mutex.new
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/agent_fix.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'quickfix'
|
2
|
+
require 'agent_fix/configuration'
|
3
|
+
require 'agent_fix/agent'
|
4
|
+
|
5
|
+
module AgentFIX
|
6
|
+
extend Configuration
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def agent_path
|
10
|
+
"./config/fix_agents.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
def agents
|
14
|
+
return @agents if @agents
|
15
|
+
|
16
|
+
(@agents=[]).tap do
|
17
|
+
load_agents if agent_files_loaded.empty?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def agent_files_loaded
|
22
|
+
@agent_files_loaded ||=[]
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_agents path=nil
|
26
|
+
path = File.expand_path(path || agent_path, Dir.pwd)
|
27
|
+
return if agent_files_loaded.include? path
|
28
|
+
agent_files_loaded << path
|
29
|
+
load path
|
30
|
+
|
31
|
+
agents.each {|a| a.init}
|
32
|
+
end
|
33
|
+
|
34
|
+
def define_agent(agent, &blk)
|
35
|
+
yield agent
|
36
|
+
agents << agent
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_acceptor(name, &blk)
|
40
|
+
define_agent(Agent.new(name, :acceptor), &blk)
|
41
|
+
end
|
42
|
+
|
43
|
+
def define_initiator(name, &blk)
|
44
|
+
define_agent(Agent.new(name, :initiator), &blk)
|
45
|
+
end
|
46
|
+
|
47
|
+
#starts all configured agents
|
48
|
+
def start
|
49
|
+
raise RuntimeError, "No FIX Agents Defined" if agents.empty?
|
50
|
+
|
51
|
+
agents.each do |a|
|
52
|
+
a.start
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def stop
|
57
|
+
agents.each {|a| a.stop}
|
58
|
+
end
|
59
|
+
|
60
|
+
def reset
|
61
|
+
agents.each {|a| a.reset}
|
62
|
+
end
|
63
|
+
|
64
|
+
def hard_reset
|
65
|
+
stop
|
66
|
+
sleep 0.5
|
67
|
+
start
|
68
|
+
end
|
69
|
+
|
70
|
+
def agents_hash
|
71
|
+
Hash[agents.map { |a| [a.name.to_sym, a]}]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AgentFIX::Configuration do
|
4
|
+
it "inspects just app messages by default" do
|
5
|
+
AgentFIX.include_session_level?.should be_false
|
6
|
+
end
|
7
|
+
|
8
|
+
it "can inspect both app and session level messages" do
|
9
|
+
AgentFIX.include_session_level = true
|
10
|
+
AgentFIX.include_session_level?.should be_true
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AgentFIX::MessageCache do
|
4
|
+
it "is basically a read-only queue" do
|
5
|
+
cache = AgentFIX::MessageCache.new
|
6
|
+
cache.add_message "1"
|
7
|
+
cache.add_message "2"
|
8
|
+
cache.add_message "3"
|
9
|
+
|
10
|
+
msgs = cache.messages
|
11
|
+
msgs.should == ["1","2","3"]
|
12
|
+
|
13
|
+
cache.clear!
|
14
|
+
msgs.should == ["1","2","3"]
|
15
|
+
end
|
16
|
+
end
|