1 $:.unshift File.join(File.dirname(__FILE__), '../lib')
6 require 'rbot/journal/postgres.rb'
7 require 'rbot/journal/mongo.rb'
13 class JournalMessageTest < Test::Unit::TestCase
15 include Irc::Bot::Journal
18 m = JournalMessage.create('foo', {'bar': 42, 'baz': nil, 'qux': {'quxx': 23}})
19 assert_equal(42, m.get('bar'))
20 assert_raise ArgumentError do
23 assert_nil(m.get('nope', nil))
24 assert_nil(m.get('baz'))
25 assert_equal(23, m.get('qux.quxx'))
30 class QueryTest < Test::Unit::TestCase
32 include Irc::Bot::Journal
40 topic 'log.core', 'baz'
41 timestamp from: Time.now, to: Time.now + 60 * 10
42 payload 'action': :privmsg, 'alice': 'bob'
43 payload 'channel': '#rbot'
44 payload 'foo.bar': 'baz'
46 assert_equal(['foo', 'bar', 'baz'], q.id)
47 assert_equal(['log.irc.*', 'log.core', 'baz'], q.topic)
48 assert_equal([:from, :to], q.timestamp.keys)
49 assert_equal(Time, q.timestamp[:to].class)
50 assert_equal(Time, q.timestamp[:from].class)
52 'action': :privmsg, 'alice': 'bob',
59 def test_topic_matches
63 assert_true(q.topic_matches?('foo'))
64 assert_false(q.topic_matches?('bar'))
65 assert_false(q.topic_matches?('foo.bar'))
70 assert_false(q.topic_matches?('foo'))
71 assert_false(q.topic_matches?('bar'))
72 assert_true(q.topic_matches?('foo.bar'))
77 assert_false(q.topic_matches?('foo'))
78 assert_false(q.topic_matches?('bar'))
79 assert_true(q.topic_matches?('foo.bar'))
80 assert_true(q.topic_matches?('foo.baz'))
85 assert_false(q.topic_matches?('foo'))
86 assert_false(q.topic_matches?('bar'))
87 assert_true(q.topic_matches?('foo.bar'))
88 assert_true(q.topic_matches?('bar.bar'))
89 assert_false(q.topic_matches?('foo.foo'))
94 assert_false(q.topic_matches?('foo'))
95 assert_true(q.topic_matches?('foo.bar'))
100 topic 'baz.alice.bob.*.foo'
102 assert_true(q.topic_matches?('foo'))
103 assert_true(q.topic_matches?('bar'))
104 assert_true(q.topic_matches?('baz.alice.bob.asdf.foo'))
105 assert_false(q.topic_matches?('baz.alice.bob..foo'))
111 topic 'log.irc.*', 'log.core'
112 timestamp from: Time.now - DAY, to: Time.now + DAY
113 payload 'action': 'privmsg', 'foo.bar': 'baz'
115 assert_true(q.matches? JournalMessage.create('log.irc.raw', {'action' => 'privmsg'}))
116 assert_false(q.matches? JournalMessage.create('baz', {}))
117 assert_true(q.matches? JournalMessage.create('log.core', {foo: {bar: 'baz'}}))
119 # tests timestamp from/to:
120 assert_true(q.matches? JournalMessage.new(
124 payload: {action: 'privmsg'}))
125 assert_false(q.matches? JournalMessage.new(
128 timestamp: Time.now - DAY*3,
129 payload: {action: 'privmsg'}))
130 assert_false(q.matches? JournalMessage.new(
133 timestamp: Time.now + DAY*3,
134 payload: {action: 'privmsg'}))
139 class JournalBrokerTest < Test::Unit::TestCase
141 include Irc::Bot::Journal
145 journal = JournalBroker.new(consumer: Proc.new { |message|
149 # publish some messages:
150 journal.publish 'log.irc',
151 source: 'alice', message: '<3 pg'
152 journal.publish 'log.irc',
153 source: 'bob', message: 'mysql > pg'
154 journal.publish 'log.irc',
155 source: 'alice', target: 'bob', action: :kick
157 # wait for messages to be consumed:
159 assert_equal(3, received.length)
164 journal = JournalBroker.new
166 # subscribe to messages:
167 sub = journal.subscribe(Query.define { topic 'foo' }) do |message|
171 # publish some messages:
172 journal.publish 'foo', {}
173 journal.publish 'bar', {}
174 journal.publish 'foo', {}
176 # wait for messages to be consumed:
178 assert_equal(2, received.length)
182 journal.publish 'foo', {}
185 journal.publish 'foo', {}
187 assert_equal(1, received.length)
192 module JournalStorageTestMixin
194 include Irc::Bot::Journal
202 m = JournalMessage.create('log.core', {foo: {bar: 'baz', qux: 42}})
206 res = @storage.find(Query.define { id m.id })
207 assert_equal(1, res.length)
208 assert_equal(m, res.first)
210 # check timestamp was returned correctly:
211 assert_equal(m.timestamp.strftime('%Y-%m-%d %H:%M:%S%z'),
212 res.first.timestamp.strftime('%Y-%m-%d %H:%M:%S%z'))
214 # check if payload was returned correctly:
215 assert_equal({'foo' => {'bar' => 'baz', 'qux' => 42}}, res.first.payload)
218 assert_equal(m, @storage.find(Query.define { topic('log.core') }).first)
219 assert_equal(m, @storage.find(Query.define { topic('log.*') }).first)
220 assert_equal(m, @storage.find(Query.define { topic('*.*') }).first)
222 # query by timestamp range
223 assert_equal(1, @storage.find(Query.define {
224 timestamp(from: Time.now-DAY, to: Time.now+DAY) }).length)
225 assert_equal(0, @storage.find(Query.define {
226 timestamp(from: Time.now-DAY*2, to: Time.now-DAY) }).length)
229 res = @storage.find(Query.define { payload('foo.bar' => 'baz') })
230 assert_equal(m, res.first)
231 res = @storage.find(Query.define { payload('foo.bar' => 'x') })
232 assert_true(res.empty?)
234 # without arguments: find and count
235 assert_equal(1, @storage.count)
236 assert_equal(m, @storage.find.first)
239 def test_operations_multiple
240 # test operations on multiple messages
242 @storage.insert(JournalMessage.create('test.topic', {name: 'one'}))
243 @storage.insert(JournalMessage.create('test.topic', {name: 'two'}))
244 @storage.insert(JournalMessage.create('test.topic', {name: 'three'}))
245 @storage.insert(JournalMessage.create('archived.topic', {name: 'four'},
246 timestamp: Time.now - DAY*100))
247 @storage.insert(JournalMessage.create('complex', {name: 'five', country: {
250 @storage.insert(JournalMessage.create('complex', {name: 'six', country: {
255 assert_equal(3, @storage.find(Query.define { topic 'test.*' }).length)
257 assert_equal(1, @storage.find(Query.define {
258 payload('country.name' => 'Austria') }).length)
259 # query by timestamp range
260 assert_equal(1, @storage.find(Query.define {
261 timestamp(from: Time.now - DAY*150, to: Time.now - DAY*50) }).length)
264 assert_equal(2, @storage.count(Query.define { topic('complex') }))
265 assert_equal(6, @storage.count)
266 @storage.remove(Query.define { topic('archived.*') })
267 assert_equal(5, @storage.count)
269 assert_equal(0, @storage.count)
273 # this journal persists messages in the test storage:
274 journal = JournalBroker.new(storage: @storage)
275 journal.publish 'log.irc', action: 'message'
277 assert_equal(1, journal.count)
284 assert_equal(0, @storage.count)
285 # prepare messages to insert, we benchmark the storage backend not ruby
287 messages = (0...NUM).map do
289 JournalMessage.create(
290 'test.topic.num_'+num.to_s, {answer: {number: '42', word: 'forty-two'}})
293 # iter is the number of operations performed WITHIN block
294 def benchmark(label, iter, &block)
295 time = Benchmark.realtime do
298 puts label + ' %d iterations, duration: %.3fms (%.3fms / iteration)' % [iter, time*1000, (time*1000) / iter]
301 benchmark(@storage.class.to_s+'~insert', messages.length) do
307 benchmark(@storage.class.to_s+'~find_by_id', messages.length) do
309 @storage.find(Query.define { id m.id })
312 benchmark(@storage.class.to_s+'~find_by_topic', messages.length) do
314 @storage.find(Query.define { topic m.topic })
317 benchmark(@storage.class.to_s+'~find_by_topic_wildcard', messages.length) do
319 @storage.find(Query.define { topic m.topic.gsub('topic', '*') })
326 class JournalStoragePostgresTest < Test::Unit::TestCase
328 include JournalStorageTestMixin
331 @storage = Storage::PostgresStorage.new(
332 uri: ENV['DB_URI'] || 'postgresql://localhost/rbot_journal',
336 def test_query_to_sql
341 topic 'log.core', 'baz'
342 timestamp from: Time.now, to: Time.now + 60 * 10
343 payload 'action': :privmsg, 'alice': 'bob'
344 payload 'channel': '#rbot'
345 payload 'foo.bar': 'baz'
347 sql = @storage.query_to_sql(q)
348 assert_equal("(id = $1 OR id = $2 OR id = $3) AND (topic ILIKE $4 OR topic ILIKE $5 OR topic ILIKE $6) AND (timestamp >= $7 AND timestamp <= $8) AND (payload->>'action' = $9 OR payload->>'alice' = $10 OR payload->>'channel' = $11 OR payload->'foo'->>'bar' = $12)", sql[0])
352 assert_equal('(id = $1)', @storage.query_to_sql(q)[0])
356 assert_equal('(topic ILIKE $1)', @storage.query_to_sql(q)[0])
357 assert_equal(['foo.%.bar'], @storage.query_to_sql(q)[1])
362 class JournalStorageMongoTest < Test::Unit::TestCase
364 include JournalStorageTestMixin
367 @storage = Storage::MongoStorage.new(