aemo 0.5.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,12 +10,13 @@ describe AEMO::NMI do
10
10
  # CLASS CONSTANTS
11
11
  # ---
12
12
  describe '::TNI_CODES' do
13
- it 'should be a hash' do
13
+ it 'is a hash' do
14
14
  expect(AEMO::NMI::TNI_CODES.class).to eq(Hash)
15
15
  end
16
16
  end
17
+
17
18
  describe '::DLF_CODES' do
18
- it 'should be a hash' do
19
+ it 'is a hash' do
19
20
  expect(AEMO::NMI::DLF_CODES.class).to eq(Hash)
20
21
  end
21
22
  end
@@ -25,52 +26,57 @@ describe AEMO::NMI do
25
26
  # ---
26
27
  describe '.valid_nmi?(nmi)' do
27
28
  context 'valid' do
28
- it 'should validate nmi' do
29
+ it 'validates nmi' do
29
30
  json.each do |nmi|
30
- expect(AEMO::NMI.valid_nmi?(nmi['nmi'])).to eq(true)
31
+ expect(described_class.valid_nmi?(nmi['nmi'])).to be(true)
31
32
  end
32
33
  end
33
34
  end
35
+
34
36
  context 'invalid' do
35
- it 'should invalidate' do
36
- expect(AEMO::NMI.valid_nmi?('OOOOOOOOOO')).to eq(false)
37
+ it 'invalidates' do
38
+ expect(described_class.valid_nmi?('OOOOOOOOOO')).to be(false)
37
39
  end
38
- it 'should invalidate' do
39
- expect(AEMO::NMI.valid_nmi?('NM100')).to eq(false)
40
+
41
+ it 'invalidates' do
42
+ expect(described_class.valid_nmi?('NM100')).to be(false)
40
43
  end
41
- it 'should invalidate' do
42
- expect { AEMO::NMI.valid_nmi? }.to raise_error(ArgumentError)
44
+
45
+ it 'invalidates' do
46
+ expect { described_class.valid_nmi? }.to raise_error(ArgumentError)
43
47
  end
44
48
  end
45
49
  end
46
50
 
47
51
  describe '.valid_checksum?(nmi, checksum)' do
48
- it 'should validate valid nmi and checksums' do
52
+ it 'validates valid nmi and checksums' do
49
53
  json.each do |nmi|
50
- expect(AEMO::NMI.valid_checksum?(nmi['nmi'], nmi['checksum'])).to eq(true)
54
+ expect(described_class.valid_checksum?(nmi['nmi'], nmi['checksum'])).to be(true)
51
55
  end
52
56
  end
53
57
  end
54
58
 
55
59
  describe '.network(nmi)' do
56
- it 'should return a network for an allocated NMI' do
57
- network = AEMO::NMI.network('NCCCC00000')
60
+ it 'returns a network for an allocated NMI' do
61
+ network = described_class.network('NCCCC00000')
58
62
  expect(network.title).to eq('Ausgrid')
59
63
  end
60
- it 'should return NIL for an unallocated NMI' do
61
- network = AEMO::NMI.network('ZZZZZZZZZZZZ')
62
- expect(network).to eq(nil)
64
+
65
+ it 'returns NIL for an unallocated NMI' do
66
+ network = described_class.network('ZZZZZZZZZZZZ')
67
+ expect(network).to be_nil
63
68
  end
64
69
  end
65
70
 
66
71
  describe '.allocation(nmi)' do
67
- it 'should return an Allocation for a NMI' do
68
- allocation = AEMO::NMI.allocation('NCCCC00000')
72
+ it 'returns an Allocation for a NMI' do
73
+ allocation = described_class.allocation('NCCCC00000')
69
74
  expect(allocation.title).to eq('Ausgrid')
70
75
  end
71
- it 'should return NIL for an unallocated NMI' do
72
- allocation = AEMO::NMI.allocation('ZZZZZZZZZZZZ')
73
- expect(allocation).to eq(nil)
76
+
77
+ it 'returns NIL for an unallocated NMI' do
78
+ allocation = described_class.allocation('ZZZZZZZZZZZZ')
79
+ expect(allocation).to be_nil
74
80
  end
75
81
  end
76
82
 
@@ -79,145 +85,161 @@ describe AEMO::NMI do
79
85
  # ---
80
86
  describe '#initialize' do
81
87
  context 'valid' do
82
- it 'should return a valid NMI' do
83
- expect(AEMO::NMI.new('NM10000000')).to be_a(AEMO::NMI)
88
+ it 'returns a valid NMI' do
89
+ expect(described_class.new('NM10000000')).to be_a(described_class)
84
90
  end
85
- it 'should return a valid NMI with MSATS' do
86
- expect(AEMO::NMI.new('NM10000000', msats_detail: {})).to be_a(AEMO::NMI)
91
+
92
+ it 'returns a valid NMI with MSATS' do
93
+ expect(described_class.new('NM10000000', msats_detail: {})).to be_a(described_class)
87
94
  end
88
95
  end
96
+
89
97
  context 'invalid' do
90
- it 'should raise an ArgumentError error' do
91
- expect { AEMO::NMI.new('OOOOOOOOOO') }.to raise_error(ArgumentError)
98
+ it 'raises an ArgumentError error' do
99
+ expect { described_class.new('OOOOOOOOOO') }.to raise_error(ArgumentError)
92
100
  end
93
- it 'should raise an ArgumentError error' do
94
- expect { AEMO::NMI.new('NM100') }.to raise_error(ArgumentError)
101
+
102
+ it 'raises an ArgumentError error' do
103
+ expect { described_class.new('NM100') }.to raise_error(ArgumentError)
95
104
  end
96
- it 'should raise an ArgumentError error' do
97
- expect { AEMO::NMI.new }.to raise_error(ArgumentError)
105
+
106
+ it 'raises an ArgumentError error' do
107
+ expect { described_class.new }.to raise_error(ArgumentError)
98
108
  end
99
109
  end
100
110
  end
101
111
 
102
112
  describe '#valid_nmi?' do
103
- it 'should validate nmi' do
113
+ it 'validates nmi' do
104
114
  json.each do |nmi|
105
- a_nmi = AEMO::NMI.new(nmi['nmi'])
106
- expect(a_nmi.valid_nmi?).to eq(true)
115
+ a_nmi = described_class.new(nmi['nmi'])
116
+ expect(a_nmi.valid_nmi?).to be(true)
107
117
  end
108
118
  end
109
119
  end
110
120
 
111
121
  describe '#checksum' do
112
- it 'should return NMI\'s checksum' do
122
+ it 'returns NMI checksum' do
113
123
  json.each do |nmi|
114
- a_nmi = AEMO::NMI.new(nmi['nmi'])
124
+ a_nmi = described_class.new(nmi['nmi'])
115
125
  expect(a_nmi.checksum).to eq(nmi['checksum'])
116
126
  end
117
127
  end
118
128
  end
119
129
 
120
130
  describe '#valid_checksum?(checksum)' do
121
- it 'should validate valid checksums' do
131
+ it 'validates valid checksums' do
122
132
  json.each do |nmi|
123
- a_nmi = AEMO::NMI.new(nmi['nmi'])
124
- expect(a_nmi.valid_checksum?(nmi['checksum'])).to eq(true)
133
+ a_nmi = described_class.new(nmi['nmi'])
134
+ expect(a_nmi.valid_checksum?(nmi['checksum'])).to be(true)
125
135
  end
126
136
  end
127
- it 'should not validate invalid checksums' do
137
+
138
+ it 'does not validate invalid checksums' do
128
139
  json.each do |nmi|
129
- a_nmi = AEMO::NMI.new(nmi['nmi'])
130
- expect(a_nmi.valid_checksum?((1 + nmi['checksum']) % 10)).to eq(false)
140
+ a_nmi = described_class.new(nmi['nmi'])
141
+ expect(a_nmi.valid_checksum?((1 + nmi['checksum']) % 10)).to be(false)
131
142
  end
132
143
  end
133
144
  end
134
145
 
135
146
  describe '#network' do
136
147
  # Positive test cases
137
- it 'should return a network for a valid NMI' do
148
+ it 'returns a network for a valid NMI' do
138
149
  json.each do |nmi|
139
- a_nmi = AEMO::NMI.new(nmi['nmi'])
150
+ a_nmi = described_class.new(nmi['nmi'])
140
151
  next if a_nmi.network.nil?
152
+
141
153
  expect(a_nmi.network).to be_a AEMO::NMI::Allocation
142
154
  expect(a_nmi.allocation).to be_a AEMO::NMI::Allocation
143
155
  end
144
156
  end
157
+
145
158
  # Negative test cases
146
- it 'should not return a network for a NMI not allocated to a network' do
159
+ it 'does not return a network for a NMI not allocated to a network' do
147
160
  json.each do |nmi|
148
- a_nmi = AEMO::NMI.new(nmi['nmi'])
161
+ a_nmi = described_class.new(nmi['nmi'])
149
162
  expect(a_nmi.network.class).to eq(NilClass) if a_nmi.network.nil?
150
163
  end
151
164
  end
152
165
  end
153
166
 
154
167
  describe '#friendly_address' do
155
- it 'should return the empty string if the address is not a hash' do
156
- nmi = AEMO::NMI.new('4001234567')
168
+ it 'returns the empty string if the address is not a hash' do
169
+ nmi = described_class.new('4001234567')
157
170
  nmi.address = 'An address'
158
171
  expect(nmi.friendly_address).to eq('')
159
172
  end
160
- it 'should return a friendly address if the address is a hash' do
161
- nmi = AEMO::NMI.new('4001234567')
173
+
174
+ it 'returns a friendly address if the address is a hash' do
175
+ nmi = described_class.new('4001234567')
162
176
  nmi.address = { number: '1', street: 'Bob', street_type: 'Street' }
163
177
  expect(nmi.friendly_address).to eq('1, Bob, Street')
164
178
  end
165
- it 'should return a friendly address if the address is a nested hash' do
166
- nmi = AEMO::NMI.new('4001234567')
179
+
180
+ it 'returns a friendly address if the address is a nested hash' do
181
+ nmi = described_class.new('4001234567')
167
182
  nmi.address = { house: { number: '1', suffix: 'B' }, street: 'Bob', street_type: 'Street' }
168
183
  expect(nmi.friendly_address).to eq('1 B, Bob, Street')
169
184
  end
170
185
  end
171
186
 
172
187
  describe '#current_daily_load' do
173
- it 'should return zero for no data' do
174
- @nmi = AEMO::NMI.new('4001234567')
188
+ it 'returns zero for no data' do
189
+ @nmi = described_class.new('4001234567')
175
190
  expect(@nmi.current_daily_load).to eq(0)
176
191
  end
177
192
  end
178
193
 
179
194
  describe '#current_annual_load' do
180
- it 'should return zero for no data' do
181
- @nmi = AEMO::NMI.new('4001234567')
195
+ it 'returns zero for no data' do
196
+ @nmi = described_class.new('4001234567')
182
197
  expect(@nmi.current_annual_load).to eq(0)
183
198
  end
184
199
  end
185
200
 
186
201
  describe '#meters_by_status' do
187
- before(:each) do
188
- @nmi = AEMO::NMI.new('4001234567')
189
- @nmi.meters = [OpenStruct.new(status: 'C'), OpenStruct.new(status: 'R')]
202
+ before do
203
+ @nmi = described_class.new('4001234567')
204
+ @nmi.meters = [Struct::DataStream.new(status: 'C'), Struct::DataStream.new(status: 'R')]
190
205
  end
191
- it 'should return current meters' do
206
+
207
+ it 'returns current meters' do
192
208
  expect(@nmi.meters_by_status.count).to eq(1)
193
209
  end
194
- it 'should return retired meters' do
210
+
211
+ it 'returns retired meters' do
195
212
  expect(@nmi.meters_by_status('R').count).to eq(1)
196
213
  end
197
- it 'should return zero meters for weird status' do
214
+
215
+ it 'returns zero meters for weird status' do
198
216
  expect(@nmi.meters_by_status('TMP').count).to eq(0)
199
217
  end
200
218
  end
201
219
 
202
220
  describe 'distribution loss factors' do
203
- before(:each) do
204
- @nmi = AEMO::NMI.new('4001234567')
221
+ before do
222
+ @nmi = described_class.new('4001234567')
205
223
  @nmi.dlf = 'BL0A'
206
224
  @nmi.tni = 'NGN2'
207
225
  end
226
+
208
227
  it 'has a valid DLF Code' do
209
228
  expect(@nmi.dlf).to eq('BL0A')
210
229
  end
230
+
211
231
  it 'has a DLF value' do
212
232
  Timecop.freeze('2016-06-01T00:00:00+1000') do
213
233
  expect(@nmi.dlfc_value.class).to eq(Float)
214
234
  end
215
235
  end
236
+
216
237
  it 'has DLF values' do
217
238
  Timecop.freeze('2016-06-01T00:00:00+1000') do
218
239
  expect(@nmi.dlfc_values.class).to eq(Array)
219
240
  end
220
241
  end
242
+
221
243
  it 'has historical values for DLF values' do
222
244
  valid_dlfc_values = [
223
245
  { datetime: '2003-06-01T00:00:00+1000', value: 1.0713 },
@@ -247,8 +269,8 @@ describe AEMO::NMI do
247
269
  end
248
270
 
249
271
  describe 'transmission node identifiers and loss factors' do
250
- before(:each) do
251
- @nmi = AEMO::NMI.new('4001234567')
272
+ before do
273
+ @nmi = described_class.new('4001234567')
252
274
  @nmi.dlf = 'BL0A'
253
275
  @nmi.tni = 'NGN2'
254
276
  end
@@ -256,16 +278,19 @@ describe AEMO::NMI do
256
278
  it 'has a valid TNI Code' do
257
279
  expect(@nmi.tni).to eq('NGN2')
258
280
  end
281
+
259
282
  it 'has a TNI value' do
260
283
  Timecop.freeze('2016-06-01T00:00:00+1000') do
261
284
  expect(@nmi.tni_value(Time.now).class).to eq(Float)
262
285
  end
263
286
  end
287
+
264
288
  it 'has TNI values' do
265
289
  Timecop.freeze('2016-06-01T00:00:00+1000') do
266
290
  expect(@nmi.tni_values.class).to eq(Array)
267
291
  end
268
292
  end
293
+
269
294
  it 'has historical values for TNI values' do
270
295
  valid_tni_values = [
271
296
  { datetime: '2014-06-01T00:00:00+1000', value: 1.0383 },
@@ -284,15 +309,16 @@ describe AEMO::NMI do
284
309
  end
285
310
 
286
311
  describe 'MSATS functions' do
287
- it 'should get data' do
312
+ it 'gets data' do
288
313
  AEMO::MSATS.authorize('ER', 'ER', 'ER')
289
- nmi = AEMO::NMI.new('4001234567')
314
+ nmi = described_class.new('4001234567')
290
315
  nmi.update_from_msats!
291
- expect(nmi.msats_detail).to_not eq(nil)
316
+ expect(nmi.msats_detail).not_to be_nil
292
317
  end
293
- it 'should return the hash of raw data' do
318
+
319
+ it 'returns the hash of raw data' do
294
320
  AEMO::MSATS.authorize('ER', 'ER', 'ER')
295
- nmi = AEMO::NMI.new('4001234567')
321
+ nmi = described_class.new('4001234567')
296
322
  expect(nmi.raw_msats_nmi_detail.class).to eq(Hash)
297
323
  end
298
324
  end
@@ -4,55 +4,60 @@ require 'spec_helper'
4
4
 
5
5
  describe AEMO::Region do
6
6
  describe '.REGIONS' do
7
- it 'should be a hash' do
7
+ it 'is a hash' do
8
8
  expect(AEMO::Region::REGIONS).to be_instance_of(Hash)
9
9
  end
10
10
  end
11
11
 
12
12
  describe 'creating a region' do
13
- it 'should raise an error if invalid region' do
14
- expect { AEMO::Region.new('BOTTOMS') }.to raise_error(ArgumentError)
13
+ it 'raises an error if invalid region' do
14
+ expect { described_class.new('BOTTOMS') }.to raise_error(ArgumentError)
15
15
  end
16
- it 'should create if region valid' do
17
- expect { AEMO::Region.new('NSW') }.not_to raise_error
16
+
17
+ it 'creates if region valid' do
18
+ expect { described_class.new('NSW') }.not_to raise_error
18
19
  end
19
20
  end
20
21
 
21
22
  describe 'AEMO::Region instance methods' do
22
- before(:each) do
23
- @nsw = AEMO::Region.new('NSW')
23
+ before do
24
+ @nsw = described_class.new('NSW')
24
25
  end
25
- it 'should have an abbreviation' do
26
+
27
+ it 'has an abbreviation' do
26
28
  expect(@nsw.abbr).to eq('NSW')
27
29
  end
28
- it 'should have a fullname' do
30
+
31
+ it 'has a fullname' do
29
32
  expect(@nsw.fullname).to eq('New South Wales')
30
33
  end
31
- it 'should have to_s method' do
34
+
35
+ it 'has to_s method' do
32
36
  expect(@nsw.to_s).to eq('NSW')
33
37
  end
34
38
 
35
- it 'should have a valid region' do
36
- expect(@nsw.send(:valid_region?, 'NSW')).to eq(true)
39
+ it 'has a valid region' do
40
+ expect(@nsw.send(:valid_region?, 'NSW')).to be(true)
37
41
  end
38
42
 
39
- it 'should have return invalid for invalid region' do
40
- expect(@nsw.send(:valid_region?, 'BOB')).to eq(false)
43
+ it 'has return invalid for invalid region' do
44
+ expect(@nsw.send(:valid_region?, 'BOB')).to be(false)
41
45
  end
42
46
 
43
47
  describe 'AEMO::Region dispatch information' do
44
- it 'should return current dispatch data' do
48
+ it 'returns current dispatch data' do
45
49
  expect(@nsw.current_dispatch.count).to eq(AEMO::Market.current_dispatch(@nsw.abbr).count)
46
50
  end
47
- it 'should return current trading data' do
51
+
52
+ it 'returns current trading data' do
48
53
  expect(@nsw.current_trading.count).to eq(AEMO::Market.current_trading(@nsw.abbr).count)
49
54
  end
50
55
  end
51
56
  end
52
57
 
53
58
  describe 'AEMO::Region class methods' do
54
- it 'should return all regions' do
55
- expect(AEMO::Region.all.count).to eq(AEMO::Region::REGIONS.keys.count)
59
+ it 'returns all regions' do
60
+ expect(described_class.all.count).to eq(AEMO::Region::REGIONS.keys.count)
56
61
  end
57
62
  end
58
63
  end
data/spec/spec_helper.rb CHANGED
@@ -18,7 +18,7 @@ require 'aemo'
18
18
 
19
19
  RSpec.configure do |config|
20
20
  # WebMock
21
- config.before(:each) do
21
+ config.before do
22
22
  csv_headers = { 'Content-Type' => 'text/csv' }
23
23
  xml_headers = { 'Content-Type' => 'text/xml' }
24
24
 
@@ -45,43 +45,43 @@ RSpec.configure do |config|
45
45
  .to_return(status: 200, body: File.new('spec/fixtures/Market/DATA201502_NSW1.csv'), headers: csv_headers)
46
46
 
47
47
  # MSATS
48
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/C4\/ER})
48
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/C4/ER})
49
49
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
50
50
  .to_return(status: 200, body: File.new('spec/fixtures/MSATS/c4.xml'), headers: xml_headers)
51
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/MSATSLimits\/ER})
51
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/MSATSLimits/ER})
52
52
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
53
53
  .to_return(status: 200, body: File.new('spec/fixtures/MSATS/msats_limits.xml'), headers: xml_headers)
54
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/NMIDetail\/ER})
54
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/NMIDetail/ER})
55
55
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
56
56
  .to_return(status: 200, body: File.new('spec/fixtures/MSATS/nmi_details.xml'), headers: xml_headers)
57
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/NMIDiscovery\/ER})
57
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/NMIDiscovery/ER})
58
58
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
59
59
  .to_return(status: 200, body: File.new('spec/fixtures/MSATS/nmi_discovery_by_address.xml'), headers: xml_headers)
60
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/ParticipantSystemStatus\/ER})
60
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/ParticipantSystemStatus/ER})
61
61
  .with(headers: { 'Accept' => 'text/xml', 'Content-Type' => 'text/xml' })
62
62
  .to_return(status: 200, body: File.new('spec/fixtures/MSATS/participant_system_status.xml'), headers: xml_headers)
63
63
  # MSATS ERRORS
64
64
  # Invalid MSATS User
65
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/C4\/NOTER})
65
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/C4/NOTER})
66
66
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
67
67
  .to_return(status: 404, body: '', headers: xml_headers)
68
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/MSATSLimits\/NOTER})
68
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/MSATSLimits/NOTER})
69
69
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
70
70
  .to_return(status: 404, body: '', headers: xml_headers)
71
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/NMIDetail\/NOTER})
71
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/NMIDetail/NOTER})
72
72
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
73
73
  .to_return(status: 404, body: '', headers: xml_headers)
74
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/NMIDiscovery\/NOTER})
74
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/NMIDiscovery/NOTER})
75
75
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
76
76
  .to_return(status: 404, body: '', headers: xml_headers)
77
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/ParticipantSystemStatus\/NOTER})
77
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/ParticipantSystemStatus/NOTER})
78
78
  .with(headers: { 'Accept' => 'text/xml', 'Content-Type' => 'text/xml' })
79
79
  .to_return(status: 404, body: '', headers: xml_headers)
80
80
  # Data errors
81
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/C4\/ER\?.+?NMI=4001234566.+?})
81
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/C4/ER\?.+?NMI=4001234566.+?})
82
82
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
83
83
  .to_return(status: 404, body: '', headers: xml_headers)
84
- stub_request(:get, %r{msats.prod.nemnet.net.au\/msats\/ws\/NMIDetail\/ER\?.+?nmi=4001234566.+?})
84
+ stub_request(:get, %r{msats.prod.nemnet.net.au/msats/ws/NMIDetail/ER\?.+?nmi=4001234566.+?})
85
85
  .with(headers: { 'Accept' => ['text/xml'], 'Content-Type' => 'text/xml' })
86
86
  .to_return(status: 404, body: '', headers: xml_headers)
87
87
  end