sortable_events_spec.rb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. require 'rails_helper'
  2. describe SortableEvents do
  3. let(:agent_class) {
  4. Class.new(Agent) do
  5. include SortableEvents
  6. default_schedule 'never'
  7. def self.valid_type?(name)
  8. true
  9. end
  10. end
  11. }
  12. def new_agent(events_order = nil)
  13. options = {}
  14. options['events_order'] = events_order if events_order
  15. agent_class.new(name: 'test', options: options) { |agent|
  16. agent.user = users(:bob)
  17. }
  18. end
  19. describe 'validations' do
  20. let(:agent_class) {
  21. Class.new(Agent) do
  22. include SortableEvents
  23. default_schedule 'never'
  24. def self.valid_type?(name)
  25. true
  26. end
  27. end
  28. }
  29. def new_agent(events_order = nil)
  30. options = {}
  31. options['events_order'] = events_order if events_order
  32. agent_class.new(name: 'test', options: options) { |agent|
  33. agent.user = users(:bob)
  34. }
  35. end
  36. it 'should allow events_order to be unspecified, null or an empty array' do
  37. expect(new_agent()).to be_valid
  38. expect(new_agent(nil)).to be_valid
  39. expect(new_agent([])).to be_valid
  40. end
  41. it 'should not allow events_order to be a non-array object' do
  42. agent = new_agent(0)
  43. expect(agent).not_to be_valid
  44. expect(agent.errors[:base]).to include(/events_order/)
  45. agent = new_agent('')
  46. expect(agent).not_to be_valid
  47. expect(agent.errors[:base]).to include(/events_order/)
  48. agent = new_agent({})
  49. expect(agent).not_to be_valid
  50. expect(agent.errors[:base]).to include(/events_order/)
  51. end
  52. it 'should not allow events_order to be an array containing unexpected objects' do
  53. agent = new_agent(['{{key}}', 1])
  54. expect(agent).not_to be_valid
  55. expect(agent.errors[:base]).to include(/events_order/)
  56. agent = new_agent(['{{key1}}', ['{{key2}}', 'unknown']])
  57. expect(agent).not_to be_valid
  58. expect(agent.errors[:base]).to include(/events_order/)
  59. end
  60. it 'should allow events_order to be an array containing strings and valid tuples' do
  61. agent = new_agent(['{{key1}}', ['{{key2}}'], ['{{key3}}', 'number']])
  62. expect(agent).to be_valid
  63. agent = new_agent(['{{key1}}', ['{{key2}}'], ['{{key3}}', 'number'], ['{{key4}}', 'time', true]])
  64. expect(agent).to be_valid
  65. end
  66. end
  67. describe 'sort_events' do
  68. let(:payloads) {
  69. [
  70. { 'title' => 'TitleA', 'score' => 4, 'updated_on' => '7 Jul 2015' },
  71. { 'title' => 'TitleB', 'score' => 2, 'updated_on' => '25 Jun 2014' },
  72. { 'title' => 'TitleD', 'score' => 10, 'updated_on' => '10 Jan 2015' },
  73. { 'title' => 'TitleC', 'score' => 10, 'updated_on' => '9 Feb 2015' },
  74. ]
  75. }
  76. let(:events) {
  77. payloads.map { |payload| Event.new(payload: payload) }
  78. }
  79. it 'should sort events by a given key' do
  80. agent = new_agent(['{{title}}'])
  81. expect(agent.__send__(:sort_events, events).map { |e| e.payload['title'] }).to eq(%w[TitleA TitleB TitleC TitleD])
  82. agent = new_agent([['{{title}}', 'string', true]])
  83. expect(agent.__send__(:sort_events, events).map { |e| e.payload['title'] }).to eq(%w[TitleD TitleC TitleB TitleA])
  84. end
  85. it 'should sort events by multiple keys' do
  86. agent = new_agent([['{{score}}', 'number'], '{{title}}'])
  87. expect(agent.__send__(:sort_events, events).map { |e| e.payload['title'] }).to eq(%w[TitleB TitleA TitleC TitleD])
  88. agent = new_agent([['{{score}}', 'number'], ['{{title}}', 'string', true]])
  89. expect(agent.__send__(:sort_events, events).map { |e| e.payload['title'] }).to eq(%w[TitleB TitleA TitleD TitleC])
  90. end
  91. it 'should sort events by time' do
  92. agent = new_agent([['{{updated_on}}', 'time']])
  93. expect(agent.__send__(:sort_events, events).map { |e| e.payload['title'] }).to eq(%w[TitleB TitleD TitleC TitleA])
  94. end
  95. it 'should sort events stably' do
  96. agent = new_agent(['<constant>'])
  97. expect(agent.__send__(:sort_events, events).map { |e| e.payload['title'] }).to eq(%w[TitleA TitleB TitleD TitleC])
  98. agent = new_agent([['<constant>', 'string', true]])
  99. expect(agent.__send__(:sort_events, events).map { |e| e.payload['title'] }).to eq(%w[TitleA TitleB TitleD TitleC])
  100. end
  101. it 'should support _index_' do
  102. agent = new_agent([['{{_index_}}', 'number', true]])
  103. expect(agent.__send__(:sort_events, events).map { |e| e.payload['title'] }).to eq(%w[TitleC TitleD TitleB TitleA])
  104. end
  105. end
  106. describe 'automatic event sorter' do
  107. describe 'declaration' do
  108. let(:passive_agent_class) {
  109. Class.new(Agent) do
  110. include SortableEvents
  111. cannot_create_events!
  112. end
  113. }
  114. let(:active_agent_class) {
  115. Class.new(Agent) do
  116. include SortableEvents
  117. end
  118. }
  119. describe 'can_order_created_events!' do
  120. it 'should refuse to work if called from an Agent that cannot create events' do
  121. expect {
  122. passive_agent_class.class_eval do
  123. can_order_created_events!
  124. end
  125. }.to raise_error('Cannot order events for agent that cannot create events')
  126. end
  127. it 'should work if called from an Agent that can create events' do
  128. expect {
  129. active_agent_class.class_eval do
  130. can_order_created_events!
  131. end
  132. }.not_to raise_error()
  133. end
  134. end
  135. describe 'can_order_created_events?' do
  136. it 'should return false unless an Agent declares can_order_created_events!' do
  137. expect(active_agent_class.can_order_created_events?).to eq(false)
  138. expect(active_agent_class.new.can_order_created_events?).to eq(false)
  139. end
  140. it 'should return true if an Agent declares can_order_created_events!' do
  141. active_agent_class.class_eval do
  142. can_order_created_events!
  143. end
  144. expect(active_agent_class.can_order_created_events?).to eq(true)
  145. expect(active_agent_class.new.can_order_created_events?).to eq(true)
  146. end
  147. end
  148. end
  149. describe 'behavior' do
  150. class Agents::EventOrderableAgent < Agent
  151. include SortableEvents
  152. default_schedule 'never'
  153. can_order_created_events!
  154. attr_accessor :payloads_to_emit
  155. def self.valid_type?(name)
  156. true
  157. end
  158. def check
  159. payloads_to_emit.each do |payload|
  160. create_event payload: payload
  161. end
  162. end
  163. def receive(events)
  164. events.each do |event|
  165. payloads_to_emit.each do |payload|
  166. create_event payload: payload.merge('title' => payload['title'] + event.payload['title_suffix'])
  167. end
  168. end
  169. end
  170. end
  171. let :new_agent do
  172. options = {}
  173. options['events_order'] = @events_order
  174. Agents::EventOrderableAgent.new(name: 'test', options: options) { |agent|
  175. agent.user = users(:bob)
  176. agent.payloads_to_emit = payloads
  177. }
  178. end
  179. let(:payloads) {
  180. [
  181. { 'title' => 'TitleA', 'score' => 4, 'updated_on' => '7 Jul 2015' },
  182. { 'title' => 'TitleB', 'score' => 2, 'updated_on' => '25 Jun 2014' },
  183. { 'title' => 'TitleD', 'score' => 10, 'updated_on' => '10 Jan 2015' },
  184. { 'title' => 'TitleC', 'score' => 10, 'updated_on' => '9 Feb 2015' },
  185. ]
  186. }
  187. it 'should keep the order of created events unless events_order is specified' do
  188. [nil, []].each do |events_order|
  189. @events_order = events_order
  190. agent = new_agent
  191. agent.save!
  192. expect { agent.check }.to change { Event.count }.by(4)
  193. events = agent.events.last(4).sort_by(&:id)
  194. expect(events.map(&:payload)).to match_array(payloads)
  195. expect(events.map { |event| event.payload['title'] }).to eq(%w[TitleA TitleB TitleD TitleC])
  196. end
  197. end
  198. it 'should sort events created in check() in the order specified in events_order' do
  199. @events_order = [['{{score}}', 'number'], ['{{title}}', 'string', true]]
  200. agent = new_agent
  201. agent.save!
  202. expect { agent.check }.to change { Event.count }.by(4)
  203. events = agent.events.last(4).sort_by(&:id)
  204. expect(events.map(&:payload)).to match_array(payloads)
  205. expect(events.map { |event| event.payload['title'] }).to eq(%w[TitleB TitleA TitleD TitleC])
  206. end
  207. it 'should sort events created in receive() in the order specified in events_order' do
  208. @events_order = [['{{score}}', 'number'], ['{{title}}', 'string', true]]
  209. agent = new_agent
  210. agent.save!
  211. expect {
  212. agent.receive([Event.new(payload: { 'title_suffix' => ' [new]' }),
  213. Event.new(payload: { 'title_suffix' => ' [popular]' })])
  214. }.to change { Event.count }.by(8)
  215. events = agent.events.last(8).sort_by(&:id)
  216. expect(events.map { |event| event.payload['title'] }).to eq([
  217. 'TitleB [new]', 'TitleA [new]', 'TitleD [new]', 'TitleC [new]',
  218. 'TitleB [popular]', 'TitleA [popular]', 'TitleD [popular]', 'TitleC [popular]',
  219. ])
  220. end
  221. describe 'with the include_sort_info option enabled' do
  222. let :new_agent do
  223. agent = super()
  224. agent.options['include_sort_info'] = true
  225. agent
  226. end
  227. it 'should add sort_info to events created in check() when events_order is not specified' do
  228. agent = new_agent
  229. agent.save!
  230. expect { agent.check }.to change { Event.count }.by(4)
  231. events = agent.events.last(4).sort_by(&:id)
  232. expect(events.map { |event| event.payload['title'] }).to eq(%w[TitleA TitleB TitleD TitleC])
  233. expect(events.map { |event| event.payload['sort_info'] }).to eq((1..4).map{ |pos| { 'position' => pos, 'count' => 4 } })
  234. end
  235. it 'should add sort_info to events created in check() when events_order is specified' do
  236. @events_order = [['{{score}}', 'number'], ['{{title}}', 'string', true]]
  237. agent = new_agent
  238. agent.save!
  239. expect { agent.check }.to change { Event.count }.by(4)
  240. events = agent.events.last(4).sort_by(&:id)
  241. expect(events.map { |event| event.payload['title'] }).to eq(%w[TitleB TitleA TitleD TitleC])
  242. expect(events.map { |event| event.payload['sort_info'] }).to eq((1..4).map{ |pos| { 'position' => pos, 'count' => 4 } })
  243. end
  244. it 'should add sort_info to events created in receive() when events_order is specified' do
  245. @events_order = [['{{score}}', 'number'], ['{{title}}', 'string', true]]
  246. agent = new_agent
  247. agent.save!
  248. expect {
  249. agent.receive([Event.new(payload: { 'title_suffix' => ' [new]' }),
  250. Event.new(payload: { 'title_suffix' => ' [popular]' })])
  251. }.to change { Event.count }.by(8)
  252. events = agent.events.last(8).sort_by(&:id)
  253. expect(events.map { |event| event.payload['title'] }).to eq([
  254. 'TitleB [new]', 'TitleA [new]', 'TitleD [new]', 'TitleC [new]',
  255. 'TitleB [popular]', 'TitleA [popular]', 'TitleD [popular]', 'TitleC [popular]',
  256. ])
  257. expect(events.map { |event| event.payload['sort_info'] }).to eq((1..4).map{ |pos| { 'position' => pos, 'count' => 4 } } * 2)
  258. end
  259. end
  260. end
  261. end
  262. end