net-ldap 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of net-ldap might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +7 -0
- data/.travis.yml +19 -1
- data/CONTRIBUTING.md +54 -0
- data/Hacking.rdoc +2 -4
- data/History.rdoc +37 -0
- data/Manifest.txt +0 -4
- data/README.rdoc +8 -0
- data/Rakefile +1 -3
- data/lib/net/ber/core_ext.rb +5 -5
- data/lib/net/ber/core_ext/string.rb +7 -7
- data/lib/net/ber/core_ext/true_class.rb +2 -3
- data/lib/net/ldap.rb +134 -620
- data/lib/net/ldap/connection.rb +692 -0
- data/lib/net/ldap/dataset.rb +18 -4
- data/lib/net/ldap/entry.rb +1 -1
- data/lib/net/ldap/filter.rb +7 -7
- data/lib/net/ldap/password.rb +11 -11
- data/lib/net/ldap/pdu.rb +28 -4
- data/lib/net/ldap/version.rb +1 -1
- data/lib/net/snmp.rb +235 -241
- data/net-ldap.gemspec +7 -33
- data/script/install-openldap +47 -0
- data/script/package +7 -0
- data/script/release +16 -0
- data/test/ber/core_ext/test_array.rb +22 -0
- data/test/ber/core_ext/test_string.rb +25 -0
- data/test/ber/test_ber.rb +126 -0
- data/test/fixtures/openldap/memberof.ldif +33 -0
- data/test/fixtures/openldap/retcode.ldif +76 -0
- data/test/fixtures/openldap/slapd.conf.ldif +67 -0
- data/test/fixtures/seed.ldif +374 -0
- data/test/integration/test_add.rb +28 -0
- data/test/integration/test_ber.rb +30 -0
- data/test/integration/test_bind.rb +22 -0
- data/test/integration/test_delete.rb +31 -0
- data/test/integration/test_open.rb +88 -0
- data/test/integration/test_return_codes.rb +38 -0
- data/test/integration/test_search.rb +77 -0
- data/test/support/vm/openldap/.gitignore +1 -0
- data/test/support/vm/openldap/README.md +32 -0
- data/test/support/vm/openldap/Vagrantfile +33 -0
- data/test/test_dn.rb +44 -0
- data/test/test_entry.rb +62 -56
- data/test/test_filter.rb +98 -2
- data/test/test_filter_parser.rb +16 -0
- data/test/test_helper.rb +54 -0
- data/test/test_ldap.rb +60 -0
- data/test/test_ldap_connection.rb +382 -2
- data/test/test_ldif.rb +26 -1
- data/test/test_password.rb +3 -10
- data/test/test_rename.rb +2 -2
- data/test/test_search.rb +39 -0
- data/test/test_snmp.rb +1 -1
- data/test/test_ssl_ber.rb +40 -0
- metadata +70 -75
- data/.autotest +0 -11
- data/.gemtest +0 -0
- data/.rspec +0 -2
- data/autotest/discover.rb +0 -1
- data/spec/integration/ssl_ber_spec.rb +0 -39
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -28
- data/spec/unit/ber/ber_spec.rb +0 -141
- data/spec/unit/ber/core_ext/array_spec.rb +0 -24
- data/spec/unit/ber/core_ext/string_spec.rb +0 -51
- data/spec/unit/ldap/dn_spec.rb +0 -80
- data/spec/unit/ldap/entry_spec.rb +0 -51
- data/spec/unit/ldap/filter_parser_spec.rb +0 -26
- data/spec/unit/ldap/filter_spec.rb +0 -115
- data/spec/unit/ldap/search_spec.rb +0 -49
- data/spec/unit/ldap_spec.rb +0 -223
- data/test/common.rb +0 -3
@@ -1,26 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Net::LDAP::Filter::FilterParser do
|
5
|
-
|
6
|
-
describe "#parse" do
|
7
|
-
context "Given ASCIIs as filter string" do
|
8
|
-
let(:filter_string) { "(cn=name)" }
|
9
|
-
specify "should generate filter object" do
|
10
|
-
expect(Net::LDAP::Filter::FilterParser.parse(filter_string)).to be_a Net::LDAP::Filter
|
11
|
-
end
|
12
|
-
end
|
13
|
-
context "Given string including multibyte chars as filter string" do
|
14
|
-
let(:filter_string) { "(cn=名前)" }
|
15
|
-
specify "should generate filter object" do
|
16
|
-
expect(Net::LDAP::Filter::FilterParser.parse(filter_string)).to be_a Net::LDAP::Filter
|
17
|
-
end
|
18
|
-
end
|
19
|
-
context "Given string including colons ':'" do
|
20
|
-
let(:filter_string) { "(ismemberof=cn=edu:berkeley:app:calmessages:deans,ou=campus groups,dc=berkeley,dc=edu)" }
|
21
|
-
specify "should generate filter object" do
|
22
|
-
expect(Net::LDAP::Filter::FilterParser.parse(filter_string)).to be_a Net::LDAP::Filter
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,115 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Net::LDAP::Filter do
|
4
|
-
describe "<- .ex(attr, value)" do
|
5
|
-
context "('foo', 'bar')" do
|
6
|
-
attr_reader :filter
|
7
|
-
before(:each) do
|
8
|
-
@filter = Net::LDAP::Filter.ex('foo', 'bar')
|
9
|
-
end
|
10
|
-
it "should convert to 'foo:=bar'" do
|
11
|
-
filter.to_s.should == '(foo:=bar)'
|
12
|
-
end
|
13
|
-
it "should survive roundtrip via to_s/from_rfc2254" do
|
14
|
-
Net::LDAP::Filter.from_rfc2254(filter.to_s).should == filter
|
15
|
-
end
|
16
|
-
it "should survive roundtrip conversion to/from ber" do
|
17
|
-
ber = filter.to_ber
|
18
|
-
Net::LDAP::Filter.parse_ber(ber.read_ber(Net::LDAP::AsnSyntax)).should ==
|
19
|
-
filter
|
20
|
-
end
|
21
|
-
end
|
22
|
-
context "various legal inputs" do
|
23
|
-
[
|
24
|
-
'(o:dn:=Ace Industry)',
|
25
|
-
'(:dn:2.4.8.10:=Dino)',
|
26
|
-
'(cn:dn:1.2.3.4.5:=John Smith)',
|
27
|
-
'(sn:dn:2.4.6.8.10:=Barbara Jones)',
|
28
|
-
'(&(sn:dn:2.4.6.8.10:=Barbara Jones))'
|
29
|
-
].each do |filter_str|
|
30
|
-
context "from_rfc2254(#{filter_str.inspect})" do
|
31
|
-
attr_reader :filter
|
32
|
-
before(:each) do
|
33
|
-
@filter = Net::LDAP::Filter.from_rfc2254(filter_str)
|
34
|
-
end
|
35
|
-
|
36
|
-
it "should decode into a Net::LDAP::Filter" do
|
37
|
-
filter.should be_an_instance_of(Net::LDAP::Filter)
|
38
|
-
end
|
39
|
-
it "should survive roundtrip conversion to/from ber" do
|
40
|
-
ber = filter.to_ber
|
41
|
-
Net::LDAP::Filter.parse_ber(ber.read_ber(Net::LDAP::AsnSyntax)).should ==
|
42
|
-
filter
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
describe "<- .construct" do
|
49
|
-
it "should accept apostrophes in filters (regression)" do
|
50
|
-
Net::LDAP::Filter.construct("uid=O'Keefe").to_rfc2254.should == "(uid=O'Keefe)"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
describe "convenience filter constructors" do
|
55
|
-
def eq(attribute, value)
|
56
|
-
described_class.eq(attribute, value)
|
57
|
-
end
|
58
|
-
describe "<- .equals(attr, val)" do
|
59
|
-
it "should delegate to .eq with escaping" do
|
60
|
-
described_class.equals('dn', 'f*oo').should == eq('dn', 'f\2Aoo')
|
61
|
-
end
|
62
|
-
end
|
63
|
-
describe "<- .begins(attr, val)" do
|
64
|
-
it "should delegate to .eq with escaping" do
|
65
|
-
described_class.begins('dn', 'f*oo').should == eq('dn', 'f\2Aoo*')
|
66
|
-
end
|
67
|
-
end
|
68
|
-
describe "<- .ends(attr, val)" do
|
69
|
-
it "should delegate to .eq with escaping" do
|
70
|
-
described_class.ends('dn', 'f*oo').should == eq('dn', '*f\2Aoo')
|
71
|
-
end
|
72
|
-
end
|
73
|
-
describe "<- .contains(attr, val)" do
|
74
|
-
it "should delegate to .eq with escaping" do
|
75
|
-
described_class.contains('dn', 'f*oo').should == eq('dn', '*f\2Aoo*')
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
describe "<- .escape(str)" do
|
80
|
-
it "should escape nul, *, (, ) and \\" do
|
81
|
-
Net::LDAP::Filter.escape("\0*()\\").should == "\\00\\2A\\28\\29\\5C"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
context 'with a well-known BER string' do
|
86
|
-
ber = raw_string("\xa4\x2d" \
|
87
|
-
"\x04\x0b" "objectclass" \
|
88
|
-
"\x30\x1e" \
|
89
|
-
"\x80\x08" "foo" "*\\" "bar" \
|
90
|
-
"\x81\x08" "foo" "*\\" "bar" \
|
91
|
-
"\x82\x08" "foo" "*\\" "bar")
|
92
|
-
|
93
|
-
describe "<- .to_ber" do
|
94
|
-
[
|
95
|
-
"foo" "\\2A\\5C" "bar",
|
96
|
-
"foo" "\\2a\\5c" "bar",
|
97
|
-
"foo" "\\2A\\5c" "bar",
|
98
|
-
"foo" "\\2a\\5C" "bar"
|
99
|
-
].each do |escaped|
|
100
|
-
it 'unescapes escaped characters' do
|
101
|
-
filter = Net::LDAP::Filter.eq("objectclass", "#{escaped}*#{escaped}*#{escaped}")
|
102
|
-
filter.to_ber.should == ber
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
describe '<- .parse_ber' do
|
108
|
-
it 'escapes characters' do
|
109
|
-
escaped = Net::LDAP::Filter.escape("foo" "*\\" "bar")
|
110
|
-
filter = Net::LDAP::Filter.parse_ber(ber.read_ber(Net::LDAP::AsnSyntax))
|
111
|
-
filter.to_s.should == "(objectclass=#{escaped}*#{escaped}*#{escaped})"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# -*- ruby encoding: utf-8 -*-
|
2
|
-
|
3
|
-
describe Net::LDAP, "search method" do
|
4
|
-
class FakeConnection
|
5
|
-
def search(args)
|
6
|
-
OpenStruct.new(:result_code => 1, :message => "error", :success? => false)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
before(:each) do
|
11
|
-
@service = MockInstrumentationService.new
|
12
|
-
@connection = Net::LDAP.new :instrumentation_service => @service
|
13
|
-
@connection.instance_variable_set(:@open_connection, FakeConnection.new)
|
14
|
-
end
|
15
|
-
|
16
|
-
context "when :return_result => true" do
|
17
|
-
it "should return nil upon error" do
|
18
|
-
result_set = @connection.search(:return_result => true)
|
19
|
-
result_set.should be_nil
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context "when :return_result => false" do
|
24
|
-
it "should return false upon error" do
|
25
|
-
result = @connection.search(:return_result => false)
|
26
|
-
result.should be_false
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
context "When :return_result is not given" do
|
31
|
-
it "should return nil upon error" do
|
32
|
-
result_set = @connection.search
|
33
|
-
result_set.should be_nil
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context "when instrumentation_service is configured" do
|
38
|
-
it "should publish a search.net_ldap event" do
|
39
|
-
events = @service.subscribe "search.net_ldap"
|
40
|
-
|
41
|
-
@connection.search :filter => "test"
|
42
|
-
|
43
|
-
payload, result = events.pop
|
44
|
-
payload.should have_key(:result)
|
45
|
-
payload.should have_key(:filter)
|
46
|
-
payload[:filter].should == "test"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/spec/unit/ldap_spec.rb
DELETED
@@ -1,223 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Net::LDAP do
|
4
|
-
describe "initialize" do
|
5
|
-
context "when instrumentation is configured" do
|
6
|
-
before do
|
7
|
-
@connection = flexmock(:connection, :close => true)
|
8
|
-
flexmock(Net::LDAP::Connection).should_receive(:new).and_return(@connection)
|
9
|
-
|
10
|
-
@service = MockInstrumentationService.new
|
11
|
-
end
|
12
|
-
|
13
|
-
subject do
|
14
|
-
Net::LDAP.new \
|
15
|
-
:server => "test.mocked.com", :port => 636,
|
16
|
-
:force_no_page => true, # so server capabilities are not queried
|
17
|
-
:instrumentation_service => @service
|
18
|
-
end
|
19
|
-
|
20
|
-
it "should instrument bind" do
|
21
|
-
events = @service.subscribe "bind.net_ldap"
|
22
|
-
|
23
|
-
bind_result = flexmock(:bind_result, :success? => true)
|
24
|
-
@connection.should_receive(:bind).with(Hash).and_return(bind_result)
|
25
|
-
|
26
|
-
subject.bind.should be_true
|
27
|
-
|
28
|
-
payload, result = events.pop
|
29
|
-
result.should be_true
|
30
|
-
payload[:bind].should == bind_result
|
31
|
-
end
|
32
|
-
|
33
|
-
it "should instrument search" do
|
34
|
-
events = @service.subscribe "search.net_ldap"
|
35
|
-
|
36
|
-
@connection.should_receive(:bind).and_return(flexmock(:bind_result, :result_code => 0))
|
37
|
-
@connection.should_receive(:search).with(Hash, Proc).
|
38
|
-
yields(entry = Net::LDAP::Entry.new("uid=user1,ou=users,dc=example,dc=com")).
|
39
|
-
and_return(flexmock(:search_result, :success? => true, :result_code => 0))
|
40
|
-
|
41
|
-
subject.search(:filter => "(uid=user1)").should be_true
|
42
|
-
|
43
|
-
payload, result = events.pop
|
44
|
-
result.should == [entry]
|
45
|
-
payload[:result].should == [entry]
|
46
|
-
payload[:filter].should == "(uid=user1)"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe Net::LDAP::Connection do
|
53
|
-
describe "initialize" do
|
54
|
-
context "when host is not responding" do
|
55
|
-
before(:each) do
|
56
|
-
flexmock(TCPSocket).
|
57
|
-
should_receive(:new).and_raise(Errno::ECONNREFUSED)
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should raise LdapError" do
|
61
|
-
lambda {
|
62
|
-
Net::LDAP::Connection.new(
|
63
|
-
:server => 'test.mocked.com',
|
64
|
-
:port => 636)
|
65
|
-
}.should raise_error(Net::LDAP::LdapError)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
context "when host is blocking the port" do
|
69
|
-
before(:each) do
|
70
|
-
flexmock(TCPSocket).
|
71
|
-
should_receive(:new).and_raise(SocketError)
|
72
|
-
end
|
73
|
-
|
74
|
-
it "should raise LdapError" do
|
75
|
-
lambda {
|
76
|
-
Net::LDAP::Connection.new(
|
77
|
-
:server => 'test.mocked.com',
|
78
|
-
:port => 636)
|
79
|
-
}.should raise_error(Net::LDAP::LdapError)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
context "on other exceptions" do
|
83
|
-
before(:each) do
|
84
|
-
flexmock(TCPSocket).
|
85
|
-
should_receive(:new).and_raise(NameError)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "should rethrow the exception" do
|
89
|
-
lambda {
|
90
|
-
Net::LDAP::Connection.new(
|
91
|
-
:server => 'test.mocked.com',
|
92
|
-
:port => 636)
|
93
|
-
}.should raise_error(NameError)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
context "populate error messages" do
|
99
|
-
before do
|
100
|
-
@tcp_socket = flexmock(:connection)
|
101
|
-
@tcp_socket.should_receive(:write)
|
102
|
-
flexmock(TCPSocket).should_receive(:new).and_return(@tcp_socket)
|
103
|
-
end
|
104
|
-
|
105
|
-
subject { Net::LDAP::Connection.new(:server => 'test.mocked.com', :port => 636) }
|
106
|
-
|
107
|
-
it "should get back error messages if operation fails" do
|
108
|
-
ber = Net::BER::BerIdentifiedArray.new([53, "", "The provided password value was rejected by a password validator: The provided password did not contain enough characters from the character set 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. The minimum number of characters from that set that must be present in user passwords is 1"])
|
109
|
-
ber.ber_identifier = Net::LDAP::PDU::ModifyResponse
|
110
|
-
@tcp_socket.should_receive(:read_ber).and_return([2, ber])
|
111
|
-
|
112
|
-
result = subject.modify(:dn => "1", :operations => [[:replace, "mail", "something@sothsdkf.com"]])
|
113
|
-
result.should be_failure
|
114
|
-
result.error_message.should == "The provided password value was rejected by a password validator: The provided password did not contain enough characters from the character set 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. The minimum number of characters from that set that must be present in user passwords is 1"
|
115
|
-
end
|
116
|
-
|
117
|
-
it "shouldn't get back error messages if operation succeeds" do
|
118
|
-
ber = Net::BER::BerIdentifiedArray.new([0, "", ""])
|
119
|
-
ber.ber_identifier = Net::LDAP::PDU::ModifyResponse
|
120
|
-
@tcp_socket.should_receive(:read_ber).and_return([2, ber])
|
121
|
-
|
122
|
-
result = subject.modify(:dn => "1", :operations => [[:replace, "mail", "something@sothsdkf.com"]])
|
123
|
-
result.should be_success
|
124
|
-
result.error_message.should == ""
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
context "instrumentation" do
|
129
|
-
before do
|
130
|
-
@tcp_socket = flexmock(:connection)
|
131
|
-
# handle write
|
132
|
-
@tcp_socket.should_receive(:write)
|
133
|
-
# return this mock
|
134
|
-
flexmock(TCPSocket).should_receive(:new).and_return(@tcp_socket)
|
135
|
-
|
136
|
-
@service = MockInstrumentationService.new
|
137
|
-
end
|
138
|
-
|
139
|
-
subject do
|
140
|
-
Net::LDAP::Connection.new(:server => 'test.mocked.com', :port => 636,
|
141
|
-
:instrumentation_service => @service)
|
142
|
-
end
|
143
|
-
|
144
|
-
it "should publish a write.net_ldap_connection event" do
|
145
|
-
ber = Net::BER::BerIdentifiedArray.new([0, "", ""])
|
146
|
-
ber.ber_identifier = Net::LDAP::PDU::BindResult
|
147
|
-
read_result = [2, ber]
|
148
|
-
@tcp_socket.should_receive(:read_ber).and_return(read_result)
|
149
|
-
|
150
|
-
events = @service.subscribe "write.net_ldap_connection"
|
151
|
-
|
152
|
-
result = subject.bind(method: :anon)
|
153
|
-
result.should be_success
|
154
|
-
|
155
|
-
# a write event
|
156
|
-
payload, result = events.pop
|
157
|
-
payload.should have_key(:result)
|
158
|
-
payload.should have_key(:content_length)
|
159
|
-
end
|
160
|
-
|
161
|
-
it "should publish a read.net_ldap_connection event" do
|
162
|
-
ber = Net::BER::BerIdentifiedArray.new([0, "", ""])
|
163
|
-
ber.ber_identifier = Net::LDAP::PDU::BindResult
|
164
|
-
read_result = [2, ber]
|
165
|
-
@tcp_socket.should_receive(:read_ber).and_return(read_result)
|
166
|
-
|
167
|
-
events = @service.subscribe "read.net_ldap_connection"
|
168
|
-
|
169
|
-
result = subject.bind(method: :anon)
|
170
|
-
result.should be_success
|
171
|
-
|
172
|
-
# a read event
|
173
|
-
payload, result = events.pop
|
174
|
-
payload.should have_key(:result)
|
175
|
-
result.should == read_result
|
176
|
-
end
|
177
|
-
|
178
|
-
it "should publish a bind.net_ldap_connection event" do
|
179
|
-
ber = Net::BER::BerIdentifiedArray.new([0, "", ""])
|
180
|
-
ber.ber_identifier = Net::LDAP::PDU::BindResult
|
181
|
-
bind_result = [2, ber]
|
182
|
-
@tcp_socket.should_receive(:read_ber).and_return(bind_result)
|
183
|
-
|
184
|
-
events = @service.subscribe "bind.net_ldap_connection"
|
185
|
-
|
186
|
-
result = subject.bind(method: :anon)
|
187
|
-
result.should be_success
|
188
|
-
|
189
|
-
# a read event
|
190
|
-
payload, result = events.pop
|
191
|
-
payload.should have_key(:result)
|
192
|
-
result.should be_success
|
193
|
-
end
|
194
|
-
|
195
|
-
it "should publish a search.net_ldap_connection event" do
|
196
|
-
# search data
|
197
|
-
search_data_ber = Net::BER::BerIdentifiedArray.new([2, [
|
198
|
-
"uid=user1,ou=OrgUnit2,ou=OrgUnitTop,dc=openldap,dc=ghe,dc=local",
|
199
|
-
[ ["uid", ["user1"]] ]
|
200
|
-
]])
|
201
|
-
search_data_ber.ber_identifier = Net::LDAP::PDU::SearchReturnedData
|
202
|
-
search_data = [2, search_data_ber]
|
203
|
-
# search result (end of results)
|
204
|
-
search_result_ber = Net::BER::BerIdentifiedArray.new([0, "", ""])
|
205
|
-
search_result_ber.ber_identifier = Net::LDAP::PDU::SearchResult
|
206
|
-
search_result = [2, search_result_ber]
|
207
|
-
@tcp_socket.should_receive(:read_ber).and_return(search_data).
|
208
|
-
and_return(search_result)
|
209
|
-
|
210
|
-
events = @service.subscribe "search.net_ldap_connection"
|
211
|
-
|
212
|
-
result = subject.search(filter: "(uid=user1)")
|
213
|
-
result.should be_success
|
214
|
-
|
215
|
-
# a search event
|
216
|
-
payload, result = events.pop
|
217
|
-
payload.should have_key(:result)
|
218
|
-
payload.should have_key(:filter)
|
219
|
-
payload[:filter].to_s.should == "(uid=user1)"
|
220
|
-
result.should be_truthy
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
data/test/common.rb
DELETED