net-dnd 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +22 -0
- data/Rakefile +2 -0
- data/bin/dndwho +5 -0
- data/lib/net/dnd.rb +82 -0
- data/lib/net/dnd/connection.rb +89 -0
- data/lib/net/dnd/errors.rb +22 -0
- data/lib/net/dnd/expires.rb +25 -0
- data/lib/net/dnd/field.rb +58 -0
- data/lib/net/dnd/profile.rb +74 -0
- data/lib/net/dnd/response.rb +95 -0
- data/lib/net/dnd/session.rb +114 -0
- data/lib/net/dnd/user_spec.rb +56 -0
- data/lib/net/dnd/version.rb +11 -0
- data/net-dnd.gemspec +25 -0
- data/spec/dnd/connection_spec.rb +94 -0
- data/spec/dnd/field_spec.rb +72 -0
- data/spec/dnd/profile_spec.rb +66 -0
- data/spec/dnd/response_spec.rb +161 -0
- data/spec/dnd/session_spec.rb +254 -0
- data/spec/dnd/user_spec_spec.rb +70 -0
- data/spec/spec_helper.rb +14 -0
- metadata +128 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
folder_path = File.dirname(__FILE__)
|
2
|
+
require "#{folder_path}/connection"
|
3
|
+
require "#{folder_path}/profile"
|
4
|
+
require "#{folder_path}/field"
|
5
|
+
require "#{folder_path}/errors"
|
6
|
+
|
7
|
+
module Net
|
8
|
+
module DND
|
9
|
+
|
10
|
+
# This is the DND session object. It provides the high-level interface for performing
|
11
|
+
# lookup-style searches against a given DND host. It manages the Connection object,
|
12
|
+
# which is the low-level socket stuff, and sends back DND Profile objects, either singly or
|
13
|
+
# an array, as the result of the find method.
|
14
|
+
|
15
|
+
class Session
|
16
|
+
|
17
|
+
attr_reader :fields, :connection
|
18
|
+
|
19
|
+
# Constructor method. Only called directly by unit tests (specs). The start method
|
20
|
+
# handles the setting up of a full DND session.
|
21
|
+
|
22
|
+
def initialize(host)
|
23
|
+
@connection = Connection.new(host)
|
24
|
+
raise ConnectionError, connection.error unless connection.open?
|
25
|
+
end
|
26
|
+
|
27
|
+
# Starts a new DND session/connection. Called by the module-level start methods.
|
28
|
+
|
29
|
+
def self.start(host, field_list=[])
|
30
|
+
session = Session.new(host)
|
31
|
+
session.set_fields(field_list)
|
32
|
+
session
|
33
|
+
end
|
34
|
+
|
35
|
+
# Are we still open?
|
36
|
+
|
37
|
+
def open?
|
38
|
+
connection.open?
|
39
|
+
end
|
40
|
+
|
41
|
+
# Set the list of fields used by subsequent find commands. These fields are then passed
|
42
|
+
# to the Profile.new method when one or more users are returned by the find command. This
|
43
|
+
# method will raise a couple of error messages, that you might want to trap for:
|
44
|
+
#
|
45
|
+
# FieldNotFound is raised when a specified field is not found in the list of fields known
|
46
|
+
# by the currently connected to DND server.
|
47
|
+
#
|
48
|
+
# FieldAccessDenied is raised when a speficied field is one whose value is not world
|
49
|
+
# readable, meaning you need to be in an authenticated session to access it.
|
50
|
+
#
|
51
|
+
# You can manually send the set_fields command, if you happen to need to change the list of
|
52
|
+
# fields returned by your find commands, after you've instantiated the session object.
|
53
|
+
|
54
|
+
def set_fields(field_list=[])
|
55
|
+
response = request(:fields, field_list)
|
56
|
+
@fields = []
|
57
|
+
raise FieldNotFound, response.error unless response.ok?
|
58
|
+
response.items.each do |item|
|
59
|
+
field = Field.from_field_line(item)
|
60
|
+
if field.read_all? # only world readable fields are valid for DND Profiles
|
61
|
+
@fields << field.to_sym
|
62
|
+
else
|
63
|
+
raise FieldAccessDenied, "#{field.to_s} is not world readable." unless field_list.empty?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# The find command is the real reason for the Net::DND libray. It provides the ability to
|
69
|
+
# send a 'user specifier' to a connected DND server and then parse the returned data into
|
70
|
+
# one or more Net::DND::Profile objects.
|
71
|
+
#
|
72
|
+
# You can send the find command in two flavors: the first, when you simply submit the look_for
|
73
|
+
# argument will assume that you're expecting more than one user to match the look_for string.
|
74
|
+
# Thus it will always return a array as its result. This array will contain zero, one or more
|
75
|
+
# Profile objects.
|
76
|
+
#
|
77
|
+
# In it's second flavor, you are submitting a value for the 'one' argument. Normally, this
|
78
|
+
# means you've sent a :one as the second argument to the call, but any non-false value will
|
79
|
+
# work. When called in this manner, you're telling the Session that you only want a Profile
|
80
|
+
# object if your 'look_for' returns a single match, otherwise the find will return nil. This
|
81
|
+
# flavor is recommended when you are performing a find using a 'uid' or a 'dctsnum' value.
|
82
|
+
|
83
|
+
def find(look_for, one=nil)
|
84
|
+
response = request(:lookup, look_for.to_s, fields)
|
85
|
+
if one
|
86
|
+
return nil unless response.items.length == 1
|
87
|
+
Profile.new(fields, response.items[0])
|
88
|
+
else
|
89
|
+
response.items.map { |item| Profile.new(fields, item) }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# The manual session close command. You only call this method if you aren't using the block
|
94
|
+
# version of the module 'start' command. If you use the block, it will automatically close
|
95
|
+
# the session when the block exits.
|
96
|
+
|
97
|
+
def close
|
98
|
+
request(:quit, nil)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# This method handles the sending of the raw protocol commands to the Connection object. It
|
104
|
+
# will only send commands if the Session is still 'open'. It always returns the Response object
|
105
|
+
# back from the result of calling into the Connection.
|
106
|
+
|
107
|
+
def request(type, *args)
|
108
|
+
raise ConnectionClosed, "Connection closed." unless open?
|
109
|
+
response = connection.send(type, *args)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Net
|
2
|
+
module DND
|
3
|
+
|
4
|
+
# This is a container class for the User Specifier portion of the DND protocol lookup command.
|
5
|
+
# Something like this isn't expressly needed, but because there are 3 types of specifier,
|
6
|
+
# based around 4 different patterns of specifier, which leads to three slightly different
|
7
|
+
# types of output, it seemed like a class was the best way to abstract those determinations.
|
8
|
+
|
9
|
+
# Once a string specifier is passed into the constructer method, it's stored and then
|
10
|
+
# matched against one of several patterns to determine it's type. This type is then used
|
11
|
+
# to choose the output format of the specifier, when the instantiated class object is
|
12
|
+
# coerced back to a string.
|
13
|
+
|
14
|
+
class UserSpec
|
15
|
+
|
16
|
+
attr_reader :type
|
17
|
+
|
18
|
+
# Construct our specifier object and set its type attribute.
|
19
|
+
|
20
|
+
def initialize(specifier)
|
21
|
+
@spec = specifier.to_s
|
22
|
+
@type = case @spec.downcase
|
23
|
+
when /^\d+$/
|
24
|
+
:uid
|
25
|
+
when /^z\d+$/
|
26
|
+
:did
|
27
|
+
when /^\d+[a-z]\d*$/
|
28
|
+
:did
|
29
|
+
else
|
30
|
+
:name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Output the correct 'string' format for our specifier. The :uid and :did types have
|
35
|
+
# special characters prepended to their value, so that they are correctly formatted
|
36
|
+
# for use in a DND connection/protocol lookup command.
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
case @type
|
40
|
+
when :uid
|
41
|
+
"##{@spec}"
|
42
|
+
when :did
|
43
|
+
"#*#{@spec}"
|
44
|
+
else
|
45
|
+
@spec
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Inspection string for the specifier object.
|
50
|
+
|
51
|
+
def inspect
|
52
|
+
"#<#{self.class} specifier=\"#{@spec}\" type=:#{@type}>"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/net-dnd.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "net/dnd/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "net-dnd"
|
7
|
+
s.version = Net::DND::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Brian V. Hughes"]
|
10
|
+
s.email = ["brianvh@dartmouth.edu"]
|
11
|
+
s.homepage = %(https://github.com/brianvh/net-dnd/)
|
12
|
+
s.summary = %(#{s.name}-#{s.version})
|
13
|
+
s.description = %(Ruby library for DND lookups.)
|
14
|
+
|
15
|
+
s.required_rubygems_version = ">= 1.3.7"
|
16
|
+
s.rubyforge_project = "net-dnd"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_development_dependency 'bundler', '~> 1.0.10'
|
24
|
+
s.add_development_dependency 'rspec', '~> 2.5.0'
|
25
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'net/dnd/connection'
|
3
|
+
|
4
|
+
module Net ; module DND
|
5
|
+
|
6
|
+
describe "a good socket", :shared => true do
|
7
|
+
before(:each) do
|
8
|
+
@socket = flexmock("TCP Socket")
|
9
|
+
@tcp = flexmock(TCPSocket)
|
10
|
+
@response = flexmock(Response)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Connection, "to a bad host" do
|
15
|
+
|
16
|
+
it_should_behave_like "a good socket"
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
@tcp.should_receive(:open).and_raise(Errno::ECONNREFUSED, "Connection refused")
|
20
|
+
@connection = Connection.new('my.fakehost.com')
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should not indicate an open connection" do
|
24
|
+
@connection.should_not be_open
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should return the 'Could not connect' error message" do
|
28
|
+
@connection.error.should match(/^Could not connect to/)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe Connection, "to a busy/slow host" do
|
33
|
+
|
34
|
+
it_should_behave_like "a good socket"
|
35
|
+
|
36
|
+
before(:each) do
|
37
|
+
flexmock(Timeout).should_receive(:timeout).and_raise(Timeout::Error, "Connection timed out")
|
38
|
+
@connection = Connection.new('my.slowhost.com')
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should not indicate an open connection" do
|
42
|
+
@connection.should_not be_open
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should return the 'Connection timed out' error message" do
|
46
|
+
@connection.error.should match(/^Connection attempt .* has timed out/)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe Connection, "to a good host" do
|
51
|
+
|
52
|
+
it_should_behave_like "a good socket"
|
53
|
+
|
54
|
+
before(:each) do
|
55
|
+
@tcp.should_receive(:open).once.and_return(@socket)
|
56
|
+
@response.should_receive(:process).at_least.once.and_return(@response)
|
57
|
+
@response.should_receive(:ok?).at_least.once.and_return(true)
|
58
|
+
@connection = Connection.new('my.goodhost.com')
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should indicate an open connection" do
|
62
|
+
@connection.should be_open
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not have any error messages" do
|
66
|
+
@connection.error.should be_nil
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "sending commands" do
|
70
|
+
|
71
|
+
it "should send the correct command when fields is called with empty field list" do
|
72
|
+
@socket.should_receive(:puts).once.with('fields')
|
73
|
+
@connection.fields
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should send the correct command when fields is called with a field list" do
|
77
|
+
@socket.should_receive(:puts).once.with('fields name nickname')
|
78
|
+
@connection.fields(['name', 'nickname'])
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should send the correct command when lookup is called" do
|
82
|
+
@socket.should_receive(:puts).once.with('lookup joe user,name nickname')
|
83
|
+
@connection.lookup('joe user', ['name', 'nickname'])
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should send the correct command when quit is called" do
|
87
|
+
@socket.should_receive(:puts).once.with('quit')
|
88
|
+
@socket.should_receive(:close)
|
89
|
+
@connection.quit
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end ; end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'net/dnd/field'
|
3
|
+
|
4
|
+
module Net ; module DND
|
5
|
+
|
6
|
+
describe Field, "created normally" do
|
7
|
+
before :each do
|
8
|
+
@name, @write, @read = %w(nickname U A)
|
9
|
+
@field = Field.new(@name, @write, @read)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should properly set the writeable flag" do
|
13
|
+
@field.writeable.should == @write
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should properly set the readable flag" do
|
17
|
+
@field.readable.should == @read
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should report as readable by all if readable value is 'A'" do
|
21
|
+
@field.should be_read_all
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should report back the proper name" do
|
25
|
+
@field.name.should == @name
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should report back the proper inspection string" do
|
29
|
+
@field.inspect.should match(/<Net::DND::Field name=".*" writeable="[AUNT]" readable="[AUNT]">/)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return the name when coerced to a string" do
|
33
|
+
@field.to_s.should == @name
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return :name when coerced to a symbol" do
|
37
|
+
@field.to_sym.should == @name.to_sym
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not report as readable by all if readable value is not 'A'" do
|
41
|
+
@read = "T"
|
42
|
+
@field = Field.new(@name, @write, @read)
|
43
|
+
@field.should_not be_read_all
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe Field, "created using from_field_line with a proper line format" do
|
49
|
+
|
50
|
+
before(:each) do
|
51
|
+
@values = %w(nickname U A)
|
52
|
+
line = @values.join(" ")
|
53
|
+
@field = Field.from_field_line(line)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should have the correct name" do
|
57
|
+
@field.name.should == @values[0]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should have to correct readable value" do
|
61
|
+
@field.readable.should == @values[2]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe Field, "created using from_field_line with an improper line format" do
|
66
|
+
it "should raise the proper error" do
|
67
|
+
line = "This is a bad field line"
|
68
|
+
lambda { Field.from_field_line(line) }.should raise_error(FieldLineInvalid)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end ; end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'net/dnd/profile'
|
3
|
+
|
4
|
+
module Net ; module DND
|
5
|
+
|
6
|
+
describe Profile, "for Joe D. User" do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@fields = [:name, :nickname, :deptclass, :email]
|
10
|
+
@items = ['Joe D. User', 'joey jdu', 'Student', 'Joe.D.User@Dartmouth.edu']
|
11
|
+
@profile = Profile.new(@fields, @items)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return the correct object" do
|
15
|
+
@profile.should be_instance_of(Profile)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return the correct inspection string" do
|
19
|
+
@profile.inspect.should match(/<Net::DND::Profile length=4, .*deptclass="Student".*>/)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have the correct number of entries" do
|
23
|
+
@profile.length.should == 4
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return the correct name" do
|
27
|
+
@profile.name.should == @items[0]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return the correct email" do
|
31
|
+
@profile[:email].should == @items[3]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should contain nickname field" do
|
35
|
+
@profile.should be_nickname
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not contain did field" do
|
39
|
+
@profile.should_not be_did
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should raise Field Not Found error if did field is requested" do
|
43
|
+
lambda { @profile.did }.should raise_error(FieldNotFound)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe Profile, "for Joe D. Expired" do
|
49
|
+
|
50
|
+
before(:each) do
|
51
|
+
@fields = [:name, :expires]
|
52
|
+
@items = ['Joe D. User', '01-Jan-2010']
|
53
|
+
@profile = Profile.new(@fields, @items)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should have a valid expire_date" do
|
57
|
+
@profile.expires_on.should_not be_nil
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should be expired" do
|
61
|
+
@profile.should be_expired
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end ; end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'net/dnd/response'
|
3
|
+
|
4
|
+
module Net ; module DND
|
5
|
+
|
6
|
+
describe Response, "on initial create" do
|
7
|
+
before(:each) do
|
8
|
+
@socket = flexmock("TCP Socket")
|
9
|
+
@response = Response.new(@socket)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should have no :code value" do
|
13
|
+
@response.code.should be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have no :error value" do
|
17
|
+
@response.error.should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have an empty :items value" do
|
21
|
+
@response.items.should be_empty
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe Response, "after create status to a good socket" do
|
26
|
+
before(:each) do
|
27
|
+
@socket = flexmock("TCP Socket")
|
28
|
+
@response = Response.new(@socket)
|
29
|
+
@socket.should_receive(:gets).once.and_return('220 DND server ready.')
|
30
|
+
@response.status_line
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have a :code of 220" do
|
34
|
+
@response.code.should == 220
|
35
|
+
end
|
36
|
+
|
37
|
+
it do
|
38
|
+
@response.should be_ok
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe Response, "parsing a bad command" do
|
43
|
+
|
44
|
+
before(:each) do
|
45
|
+
@code = 501
|
46
|
+
@msg = "unknown field name foo"
|
47
|
+
@socket = flexmock("DND Socket after bad :fields command")
|
48
|
+
@socket.should_receive(:gets).once.and_return("#{@code} #{@msg}\r\n")
|
49
|
+
@response = Response.process(@socket)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should return a code of 501" do
|
53
|
+
@response.code == @code
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should have the appropriate error message" do
|
57
|
+
@response.error == @msg
|
58
|
+
end
|
59
|
+
|
60
|
+
it do
|
61
|
+
@response.should_not be_ok
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe Response, "parsing a :quit command" do
|
66
|
+
|
67
|
+
before(:each) do
|
68
|
+
@code = 200
|
69
|
+
@msg = "Ok"
|
70
|
+
@socket = flexmock("DND Socket after :quit command")
|
71
|
+
@socket.should_receive(:gets).once.and_return("#{@code} #{@msg}\r\n")
|
72
|
+
@response = Response.process(@socket)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should return a code of 200" do
|
76
|
+
@response.code == @code
|
77
|
+
end
|
78
|
+
|
79
|
+
it do
|
80
|
+
@response.should be_ok
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe Response, "parsing a :fields command" do
|
85
|
+
|
86
|
+
before(:each) do
|
87
|
+
@code = [102, 200]
|
88
|
+
@count = 2
|
89
|
+
@data = ['120 name N A', '120 nickname U A']
|
90
|
+
@status = 'Done'
|
91
|
+
@socket = flexmock("DND Socket after :fields command")
|
92
|
+
@socket.should_receive(:gets).times(4).and_return(
|
93
|
+
"#{@code[0]} #{@count}\r\n", "#{@data[0]}\r\n",
|
94
|
+
"#{@data[1]}\r\n", "#{@code[1]} #{@status}\r\n")
|
95
|
+
@response = Response.process(@socket)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should have a sub_count of 0" do
|
99
|
+
@response.sub_count == 0
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should have the correct number of items" do
|
103
|
+
@response.should have(2).items
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should have 'nickname' as the second item" do
|
107
|
+
@response.items[1].split[0] == 'nickname'
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should have a code of 200" do
|
111
|
+
@response.code.should == @code[1]
|
112
|
+
end
|
113
|
+
|
114
|
+
it do
|
115
|
+
@response.should be_ok
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe Response, "parsing a :lookup command" do
|
120
|
+
|
121
|
+
before(:each) do
|
122
|
+
@code = [102, 201]
|
123
|
+
@count = 2
|
124
|
+
@sub_count = 2
|
125
|
+
@data = ['110 Joe Q. User', '110 joey, jqu', '110 Jane P. User', '110 janes, jp']
|
126
|
+
@status = 'Additional matches not returned'
|
127
|
+
@socket = flexmock("DND Socket after :lookup command")
|
128
|
+
@socket.should_receive(:gets).times(6).and_return(
|
129
|
+
"#{@code[0]} #{@count} #{@sub_count}\r\n",
|
130
|
+
"#{@data[0]}\r\n", "#{@data[1]}\r\n",
|
131
|
+
"#{@data[2]}\r\n", "#{@data[3]}\r\n",
|
132
|
+
"#{@code[1]} #{@status}\r\n")
|
133
|
+
@response = Response.process(@socket)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should have the correct count" do
|
137
|
+
@response.count == @count
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should have the correct sub_count" do
|
141
|
+
@response.sub_count == @sub_count
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should have items stored as an array of arrays" do
|
145
|
+
@response.items[0].should be_an_instance_of(Array)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should have the correct name for the sub-array of the second item" do
|
149
|
+
@response.items[1][0] == 'Jane P. User'
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should have a code of 201" do
|
153
|
+
@response.code.should == @code[1]
|
154
|
+
end
|
155
|
+
|
156
|
+
it do
|
157
|
+
@response.should be_ok
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end; end
|