tkellem 0.9.0.beta3 → 0.9.0.beta4
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/lib/tkellem/bouncer.rb +14 -30
- data/lib/tkellem/bouncer_connection.rb +58 -42
- data/lib/tkellem/daemon.rb +18 -7
- data/lib/tkellem/irc_server.rb +124 -0
- data/lib/tkellem/migrations/001_init_db.rb +22 -39
- data/lib/tkellem/migrations/002_at_connect_columns.rb +4 -21
- data/lib/tkellem/migrations/003_settings.rb +9 -18
- data/lib/tkellem/models/host.rb +2 -2
- data/lib/tkellem/models/listen_address.rb +1 -13
- data/lib/tkellem/models/network.rb +7 -11
- data/lib/tkellem/models/network_user.rb +6 -22
- data/lib/tkellem/models/setting.rb +3 -3
- data/lib/tkellem/models/user.rb +6 -13
- data/lib/tkellem/plugins/backlog.rb +31 -43
- data/lib/tkellem/socket_server.rb +7 -15
- data/lib/tkellem/tkellem_bot.rb +19 -11
- data/lib/tkellem/tkellem_server.rb +70 -77
- data/lib/tkellem/version.rb +1 -1
- data/lib/tkellem.rb +10 -1
- data/spec/bouncer_connection_spec.rb +37 -0
- data/spec/irc_server_spec.rb +145 -0
- data/spec/spec_helper.rb +17 -4
- data/tkellem.gemspec +5 -6
- metadata +31 -43
- data/lib/tkellem/celluloid_tools.rb +0 -131
@@ -1,13 +1,13 @@
|
|
1
1
|
module Tkellem
|
2
2
|
|
3
|
-
class Setting <
|
3
|
+
class Setting < ActiveRecord::Base
|
4
4
|
def self.get(setting_name)
|
5
|
-
setting =
|
5
|
+
setting = first(:conditions => { :name => setting_name })
|
6
6
|
setting.try(:value)
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.set(setting_name, new_value)
|
10
|
-
setting =
|
10
|
+
setting = first(:conditions => { :name => setting_name })
|
11
11
|
setting.try(:update_attributes, :value => new_value.to_s, :unchanged => false)
|
12
12
|
setting
|
13
13
|
end
|
data/lib/tkellem/models/user.rb
CHANGED
@@ -1,20 +1,13 @@
|
|
1
1
|
module Tkellem
|
2
2
|
|
3
|
-
class User <
|
4
|
-
|
5
|
-
|
6
|
-
one_to_many :network_users, :dependent => :destroy
|
7
|
-
one_to_many :networks, :dependent => :destroy
|
3
|
+
class User < ActiveRecord::Base
|
4
|
+
has_many :network_users, :dependent => :destroy
|
5
|
+
has_many :networks, :dependent => :destroy
|
8
6
|
|
9
7
|
validates_presence_of :username
|
10
8
|
validates_uniqueness_of :username
|
11
9
|
validates_presence_of :role, :in => %w(user admin)
|
12
10
|
|
13
|
-
def before_validation
|
14
|
-
self.role ||= 'user'
|
15
|
-
super
|
16
|
-
end
|
17
|
-
|
18
11
|
# pluggable authentication -- add your own block, which takes |username, password|
|
19
12
|
# parameters. Return a User object if authentication succeeded, or a
|
20
13
|
# false/nil value if auth failed. You can create the user on-the-fly if
|
@@ -25,7 +18,7 @@ class User < Sequel::Model
|
|
25
18
|
# default database-based authentication
|
26
19
|
# TODO: proper password hashing
|
27
20
|
self.authentication_methods << proc do |username, password|
|
28
|
-
user =
|
21
|
+
user = find_by_username(username)
|
29
22
|
user && user.valid_password?(password) && user
|
30
23
|
end
|
31
24
|
|
@@ -38,7 +31,7 @@ class User < Sequel::Model
|
|
38
31
|
end
|
39
32
|
|
40
33
|
def username=(val)
|
41
|
-
|
34
|
+
write_attribute(:username, val.try(:downcase))
|
42
35
|
end
|
43
36
|
|
44
37
|
def name
|
@@ -51,7 +44,7 @@ class User < Sequel::Model
|
|
51
44
|
end
|
52
45
|
|
53
46
|
def password=(password)
|
54
|
-
|
47
|
+
write_attribute(:password, password ? OpenSSL::Digest::SHA1.hexdigest(password) : nil)
|
55
48
|
end
|
56
49
|
|
57
50
|
def admin?
|
@@ -15,11 +15,9 @@ module Tkellem
|
|
15
15
|
# different backlog implementation. Right now, it's always loaded though.
|
16
16
|
class Backlog
|
17
17
|
include Tkellem::EasyLogger
|
18
|
-
include Celluloid
|
19
|
-
|
20
|
-
cattr_accessor :replay_pool
|
21
18
|
|
22
19
|
Bouncer.add_plugin(self)
|
20
|
+
cattr_accessor :instances
|
23
21
|
|
24
22
|
def self.get_instance(bouncer)
|
25
23
|
bouncer.data(self)[:instance] ||= self.new(bouncer)
|
@@ -59,7 +57,7 @@ class Backlog
|
|
59
57
|
# open stream in append-only mode
|
60
58
|
return @streams[ctx] if @streams[ctx]
|
61
59
|
stream = @streams[ctx] = File.open(stream_filename(ctx), 'ab')
|
62
|
-
stream.seek(0,
|
60
|
+
stream.seek(0, IO::SEEK_END)
|
63
61
|
@starting_pos[ctx] = stream.pos
|
64
62
|
stream
|
65
63
|
end
|
@@ -118,46 +116,38 @@ class Backlog
|
|
118
116
|
|
119
117
|
def send_backlog(conn, device)
|
120
118
|
device.each do |ctx_name, pos|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
while line = stream.gets
|
136
|
-
timestamp, msg = parse_line(line, ctx_name)
|
137
|
-
next unless msg
|
138
|
-
privmsg = msg.args.first[0] != '#'[0]
|
139
|
-
if msg.prefix
|
140
|
-
# to this user
|
141
|
-
if privmsg
|
142
|
-
msg.args[0] = bouncer.nick
|
119
|
+
stream = File.open(stream_filename(ctx_name), 'rb')
|
120
|
+
stream.seek(pos)
|
121
|
+
|
122
|
+
while line = stream.gets
|
123
|
+
timestamp, msg = parse_line(line, ctx_name)
|
124
|
+
next unless msg
|
125
|
+
privmsg = msg.args.first[0] != '#'[0]
|
126
|
+
if msg.prefix
|
127
|
+
# to this user
|
128
|
+
if privmsg
|
129
|
+
msg.args[0] = @bouncer.nick
|
130
|
+
else
|
131
|
+
# do nothing, it's good to send
|
132
|
+
end
|
143
133
|
else
|
144
|
-
#
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
# it's a room, we can just replay
|
157
|
-
msg.prefix = bouncer.nick
|
134
|
+
# from this user
|
135
|
+
if privmsg
|
136
|
+
# a one-on-one chat -- every client i've seen doesn't know how to
|
137
|
+
# display messages from themselves here, so we fake it by just
|
138
|
+
# adding an arrow and pretending the other user said it. shame.
|
139
|
+
msg.prefix = msg.args.first
|
140
|
+
msg.args[0] = @bouncer.nick
|
141
|
+
msg.args[-1] = "-> #{msg.args.last}"
|
142
|
+
else
|
143
|
+
# it's a room, we can just replay
|
144
|
+
msg.prefix = @bouncer.nick
|
145
|
+
end
|
158
146
|
end
|
147
|
+
conn.send_msg(msg.with_timestamp(timestamp))
|
159
148
|
end
|
160
|
-
|
149
|
+
|
150
|
+
device[ctx_name] = get_stream(ctx_name).pos
|
161
151
|
end
|
162
152
|
end
|
163
153
|
|
@@ -182,6 +172,4 @@ class BacklogReplay
|
|
182
172
|
end
|
183
173
|
end
|
184
174
|
|
185
|
-
Backlog.replay_pool = BacklogReplay.pool(size: Celluloid.cores * 2)
|
186
|
-
|
187
175
|
end
|
@@ -1,30 +1,26 @@
|
|
1
|
-
require '
|
1
|
+
require 'eventmachine'
|
2
2
|
|
3
3
|
require 'tkellem/tkellem_bot'
|
4
4
|
|
5
5
|
module Tkellem
|
6
6
|
|
7
7
|
# listens on the unix domain socket and executes admin commands
|
8
|
-
|
9
|
-
|
10
|
-
include Celluloid::IO
|
8
|
+
module SocketServer
|
9
|
+
include EM::Protocols::LineText2
|
11
10
|
include Tkellem::EasyLogger
|
12
|
-
include Tkellem::CelluloidTools::LineReader
|
13
11
|
|
14
12
|
def log_name
|
15
13
|
"admin"
|
16
14
|
end
|
17
15
|
|
18
|
-
def
|
19
|
-
|
20
|
-
@delimiter = "\n"
|
21
|
-
async.run
|
16
|
+
def post_init
|
17
|
+
set_delimiter "\n"
|
22
18
|
end
|
23
19
|
|
24
20
|
def receive_line(line)
|
25
21
|
trace "admin socket: #{line}"
|
26
|
-
TkellemBot.run_command(line, nil, nil) do |
|
27
|
-
send_data("#{
|
22
|
+
TkellemBot.run_command(line, nil, nil) do |output|
|
23
|
+
send_data("#{output}\n")
|
28
24
|
end
|
29
25
|
send_data("\0\n")
|
30
26
|
rescue => e
|
@@ -32,10 +28,6 @@ class SocketServer
|
|
32
28
|
e.backtrace.each { |l| send_data("#{l}\n") }
|
33
29
|
send_data("\0\n")
|
34
30
|
end
|
35
|
-
|
36
|
-
def send_data(dat)
|
37
|
-
@socket.write(dat)
|
38
|
-
end
|
39
31
|
end
|
40
32
|
|
41
33
|
end
|
data/lib/tkellem/tkellem_bot.rb
CHANGED
@@ -156,7 +156,7 @@ class TkellemBot
|
|
156
156
|
end
|
157
157
|
|
158
158
|
def modify
|
159
|
-
instance = model.first(find_attributes)
|
159
|
+
instance = model.first(:conditions => find_attributes)
|
160
160
|
new_record = false
|
161
161
|
if instance
|
162
162
|
instance.attributes = attributes
|
@@ -181,7 +181,7 @@ class TkellemBot
|
|
181
181
|
end
|
182
182
|
|
183
183
|
def remove
|
184
|
-
instance = model.first(find_attributes)
|
184
|
+
instance = model.first(:conditions => find_attributes)
|
185
185
|
if instance
|
186
186
|
instance.destroy
|
187
187
|
respond "Removed #{show(instance)}"
|
@@ -258,7 +258,7 @@ class TkellemBot
|
|
258
258
|
|
259
259
|
if opts['username']
|
260
260
|
if Command.admin_user?(user)
|
261
|
-
user = User.
|
261
|
+
user = User.first(:conditions => { :username => opts['username'] })
|
262
262
|
else
|
263
263
|
raise Command::ArgumentError, "Only admins can change other passwords"
|
264
264
|
end
|
@@ -303,7 +303,7 @@ class TkellemBot
|
|
303
303
|
|
304
304
|
def execute
|
305
305
|
if opts['network'].present? # only settable by admins
|
306
|
-
target = Network.
|
306
|
+
target = Network.first(:conditions => ["name = ? AND user_id IS NULL", opts['network'].downcase])
|
307
307
|
else
|
308
308
|
target = network_user
|
309
309
|
end
|
@@ -339,7 +339,7 @@ class TkellemBot
|
|
339
339
|
admin_option('public', '--public', "Create new public network. Once created, public/private status can't be modified.")
|
340
340
|
|
341
341
|
def list
|
342
|
-
public_networks = Network.
|
342
|
+
public_networks = Network.all(:conditions => 'user_id IS NULL')
|
343
343
|
user_networks = user.try(:reload).try(:networks) || []
|
344
344
|
if user_networks.present? && public_networks.present?
|
345
345
|
r "Public networks are prefixed with [P], user-specific networks with [U]."
|
@@ -355,14 +355,15 @@ class TkellemBot
|
|
355
355
|
end
|
356
356
|
|
357
357
|
def execute
|
358
|
+
# TODO: this got gross
|
358
359
|
if args.empty? && !opts['remove']
|
359
360
|
list
|
360
361
|
return
|
361
362
|
end
|
362
363
|
|
363
364
|
if opts['network'].present?
|
364
|
-
target = Network.
|
365
|
-
target ||= Network.
|
365
|
+
target = Network.first(:conditions => ["name = ? AND user_id = ?", opts['network'].downcase, user.try(:id)])
|
366
|
+
target ||= Network.first(:conditions => ["name = ? AND user_id IS NULL", opts['network'].downcase]) if self.class.admin_user?(user)
|
366
367
|
else
|
367
368
|
target = network_user.try(:network)
|
368
369
|
if target && target.public? && !self.class.admin_user?(user)
|
@@ -378,7 +379,7 @@ class TkellemBot
|
|
378
379
|
raise(Command::ArgumentError, "No network found") unless target
|
379
380
|
raise(Command::ArgumentError, "You must explicitly specify the network to remove") unless opts['network']
|
380
381
|
if uri
|
381
|
-
target.hosts.
|
382
|
+
target.hosts.first(:conditions => addr_args).try(:destroy)
|
382
383
|
respond " #{show(target)}"
|
383
384
|
else
|
384
385
|
target.destroy
|
@@ -396,9 +397,16 @@ class TkellemBot
|
|
396
397
|
end
|
397
398
|
end
|
398
399
|
|
399
|
-
|
400
|
-
|
401
|
-
|
400
|
+
target.attributes = { :hosts_attributes => [addr_args] }
|
401
|
+
target.save
|
402
|
+
if target.errors.any?
|
403
|
+
respond "Error:"
|
404
|
+
target.errors.full_messages.each { |m| respond " #{m}" }
|
405
|
+
respond " #{show(target)}"
|
406
|
+
else
|
407
|
+
respond("updated:")
|
408
|
+
respond " #{show(target)}"
|
409
|
+
end
|
402
410
|
end
|
403
411
|
end
|
404
412
|
end
|
@@ -1,62 +1,47 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'active_record'
|
3
|
+
require 'rails/observers/activerecord/active_record'
|
4
4
|
|
5
|
-
require 'tkellem/bouncer'
|
6
5
|
require 'tkellem/bouncer_connection'
|
7
|
-
require 'tkellem/
|
6
|
+
require 'tkellem/bouncer'
|
7
|
+
|
8
|
+
require 'tkellem/models/host'
|
9
|
+
require 'tkellem/models/listen_address'
|
10
|
+
require 'tkellem/models/network'
|
11
|
+
require 'tkellem/models/network_user'
|
12
|
+
require 'tkellem/models/setting'
|
13
|
+
require 'tkellem/models/user'
|
8
14
|
|
9
15
|
require 'tkellem/plugins/backlog'
|
10
|
-
|
16
|
+
require 'tkellem/plugins/push_service'
|
11
17
|
|
12
18
|
module Tkellem
|
13
19
|
|
14
20
|
class TkellemServer
|
15
|
-
include Celluloid
|
16
21
|
include Tkellem::EasyLogger
|
17
22
|
|
18
|
-
attr_reader :bouncers
|
19
|
-
|
20
|
-
def self.initialize_database(path)
|
21
|
-
Sequel.extension :migration
|
22
|
-
db = Sequel.connect({
|
23
|
-
:adapter => 'sqlite',
|
24
|
-
:database => path,
|
25
|
-
})
|
26
|
-
migrations_path = File.expand_path("../migrations", __FILE__)
|
27
|
-
Sequel::Migrator.apply(db, migrations_path)
|
28
|
-
|
29
|
-
Sequel::Model.raise_on_save_failure = true
|
30
|
-
# Can't load the models until we've connected to the database and migrated
|
31
|
-
require 'tkellem/models/host'
|
32
|
-
require 'tkellem/models/listen_address'
|
33
|
-
require 'tkellem/models/network'
|
34
|
-
require 'tkellem/models/network_user'
|
35
|
-
require 'tkellem/models/setting'
|
36
|
-
require 'tkellem/models/user'
|
37
|
-
|
38
|
-
db
|
39
|
-
end
|
23
|
+
attr_reader :bouncers
|
40
24
|
|
41
|
-
def initialize
|
42
|
-
Celluloid.logger = Tkellem::EasyLogger.logger
|
43
|
-
@options = options
|
25
|
+
def initialize
|
44
26
|
@listeners = {}
|
45
|
-
@bouncers =
|
27
|
+
@bouncers = {}
|
46
28
|
$tkellem_server = self
|
47
29
|
|
48
|
-
|
49
|
-
|
30
|
+
unless ActiveRecord::Base.connected?
|
31
|
+
ActiveRecord::Base.establish_connection({
|
32
|
+
:adapter => 'sqlite3',
|
33
|
+
:database => File.expand_path("~/.tkellem/tkellem.sqlite3"),
|
34
|
+
})
|
35
|
+
ActiveRecord::Migrator.migrate(File.expand_path("../migrations", __FILE__), nil)
|
36
|
+
end
|
50
37
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
38
|
+
ListenAddress.all.each { |a| listen(a) }
|
39
|
+
NetworkUser.find_each { |nu| add_bouncer(nu) }
|
40
|
+
Observer.forward_to << self
|
41
|
+
end
|
55
42
|
|
56
|
-
|
57
|
-
|
58
|
-
rescue Interrupt
|
59
|
-
end
|
43
|
+
def stop
|
44
|
+
Observer.forward_to.delete(self)
|
60
45
|
end
|
61
46
|
|
62
47
|
# callbacks for AR observer events
|
@@ -73,75 +58,83 @@ class TkellemServer
|
|
73
58
|
case obj
|
74
59
|
when ListenAddress
|
75
60
|
stop_listening(obj)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
def start_unix_server
|
80
|
-
# This file relies on the models being loaded
|
81
|
-
# TODO: this is gross
|
82
|
-
require 'tkellem/socket_server'
|
83
|
-
CelluloidTools::UnixListener.start(socket_file) do |socket|
|
84
|
-
SocketServer.new(socket)
|
61
|
+
when NetworkUser
|
62
|
+
stop_bouncer(obj)
|
85
63
|
end
|
86
64
|
end
|
87
65
|
|
88
66
|
def listen(listen_address)
|
89
67
|
info "Listening on #{listen_address}"
|
90
68
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
listener = server_class.start(listen_address.address,
|
98
|
-
listen_address.port) do |socket|
|
99
|
-
BouncerConnection.new(self, socket).async.run
|
100
|
-
end
|
101
|
-
|
102
|
-
@listeners[listen_address.id] = listener
|
69
|
+
@listeners[listen_address.id] = EM.start_server(listen_address.address,
|
70
|
+
listen_address.port,
|
71
|
+
BouncerConnection,
|
72
|
+
self,
|
73
|
+
listen_address.ssl)
|
103
74
|
end
|
104
75
|
|
105
76
|
def stop_listening(listen_address)
|
106
77
|
listener = @listeners[listen_address.id]
|
107
78
|
return unless listener
|
108
|
-
listener
|
79
|
+
EM.stop_server(listener)
|
109
80
|
info "No longer listening on #{listen_address}"
|
110
81
|
end
|
111
82
|
|
112
83
|
def add_bouncer(network_user)
|
113
84
|
unless network_user.user && network_user.network
|
114
|
-
info "Terminating orphan network user #{network_user}"
|
85
|
+
info "Terminating orphan network user #{network_user.inspect}"
|
115
86
|
network_user.destroy
|
116
87
|
return
|
117
88
|
end
|
118
|
-
|
119
|
-
|
120
|
-
@bouncers.
|
89
|
+
|
90
|
+
key = bouncers_key(network_user)
|
91
|
+
raise("bouncer already exists: #{key}") if @bouncers.include?(key)
|
92
|
+
@bouncers[key] = Bouncer.new(bouncer)
|
93
|
+
end
|
94
|
+
|
95
|
+
def stop_bouncer(obj)
|
96
|
+
key = bouncers_key(network_user)
|
97
|
+
bouncer = @bouncers.delete(key)
|
98
|
+
if bouncer
|
99
|
+
bouncer.kill!
|
100
|
+
end
|
121
101
|
end
|
122
102
|
|
123
103
|
def find_bouncer(user, network_name)
|
124
104
|
key = [user.id, network_name]
|
125
|
-
bouncer = @bouncers
|
105
|
+
bouncer = @bouncers[key]
|
126
106
|
if !bouncer
|
127
107
|
# find the public network with this name, and attempt to auto-add this user to it
|
128
|
-
network = Network.first({ :user_id => nil, :name => network_name })
|
108
|
+
network = Network.first(:conditions => { :user_id => nil, :name => network_name })
|
129
109
|
if network
|
130
|
-
NetworkUser.create(:user => user, :network => network)
|
110
|
+
NetworkUser.create!(:user => user, :network => network)
|
131
111
|
# AR callback should create the bouncer in sync
|
132
|
-
bouncer = @bouncers
|
112
|
+
bouncer = @bouncers[key]
|
133
113
|
end
|
134
114
|
end
|
135
115
|
bouncer
|
136
116
|
end
|
137
117
|
|
138
|
-
def
|
139
|
-
|
118
|
+
def bouncers_key(network_user)
|
119
|
+
[network_user.user_id, network_user.network.name]
|
140
120
|
end
|
141
121
|
|
142
|
-
|
143
|
-
|
122
|
+
class Observer < ActiveRecord::Observer
|
123
|
+
observe 'Tkellem::ListenAddress', 'Tkellem::NetworkUser'
|
124
|
+
cattr_accessor :forward_to
|
125
|
+
self.forward_to = []
|
126
|
+
|
127
|
+
def after_create(obj)
|
128
|
+
forward_to.each { |f| f.after_create(obj) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def after_destroy(obj)
|
132
|
+
forward_to.each { |f| f.after_destroy(obj) }
|
133
|
+
end
|
144
134
|
end
|
135
|
+
|
136
|
+
ActiveRecord::Base.observers = Observer
|
137
|
+
ActiveRecord::Base.instantiate_observers
|
145
138
|
end
|
146
139
|
|
147
140
|
end
|
data/lib/tkellem/version.rb
CHANGED
data/lib/tkellem.rb
CHANGED
@@ -21,7 +21,7 @@ module Tkellem
|
|
21
21
|
@trace = val
|
22
22
|
end
|
23
23
|
def self.trace
|
24
|
-
@trace || @trace =
|
24
|
+
@trace || @trace = false
|
25
25
|
end
|
26
26
|
|
27
27
|
def log_name
|
@@ -32,6 +32,15 @@ module Tkellem
|
|
32
32
|
puts("TRACE: #{log_name}: #{msg}") if EasyLogger.trace
|
33
33
|
end
|
34
34
|
|
35
|
+
def failsafe(event)
|
36
|
+
yield
|
37
|
+
rescue => e
|
38
|
+
# if the failsafe rescue fails, we're in a really bad state and should probably just die
|
39
|
+
self.error "exception while handling #{event}"
|
40
|
+
self.error e.to_s
|
41
|
+
(e.backtrace || []).each { |line| self.error line }
|
42
|
+
end
|
43
|
+
|
35
44
|
::Logger::Severity.constants.each do |level|
|
36
45
|
next if level == "UNKNOWN"
|
37
46
|
module_eval(<<-EVAL, __FILE__, __LINE__)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tkellem/bouncer_connection'
|
3
|
+
|
4
|
+
include Tkellem
|
5
|
+
|
6
|
+
describe BouncerConnection, "connect" do
|
7
|
+
before do
|
8
|
+
@u = User.create(:username => 'speccer')
|
9
|
+
@u.password = 'test123'
|
10
|
+
@u.save
|
11
|
+
@tk = mock(TkellemServer)
|
12
|
+
@b = mock(Bouncer)
|
13
|
+
@bc = em(BouncerConnection).new(@tk, false)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should ignore blank lines" do
|
17
|
+
@bc.should_receive(:error!).never
|
18
|
+
@bc.receive_line("")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should connect after receiving credentials" do
|
22
|
+
@tk.should_receive(:find_bouncer).with(@u, 'testhost').and_return(@b)
|
23
|
+
@bc.receive_line("NICK speccer")
|
24
|
+
@bc.receive_line("PASS test123")
|
25
|
+
@b.should_receive(:connect_client).with(@bc)
|
26
|
+
@bc.receive_line("USER speccer@testhost")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should connect when receiving user before pass" do
|
30
|
+
@tk.should_receive(:find_bouncer).with(@u, 'testhost').and_return(@b)
|
31
|
+
@bc.receive_line("USER speccer@testhost")
|
32
|
+
@bc.receive_line("PASS test123")
|
33
|
+
@b.should_receive(:connect_client).with(@bc)
|
34
|
+
@bc.receive_line("NICK speccer")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|