net-dnd 1.1.2
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/.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
|