rims 0.2.9 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,124 +2,63 @@
2
2
 
3
3
  require 'logger'
4
4
  require 'rims'
5
+ require 'stringio'
5
6
  require 'test/unit'
6
7
 
7
8
  module RIMS::Test
8
9
  class ProtocolRequestReaderTest < Test::Unit::TestCase
10
+ extend AssertUtility
11
+ include AssertUtility
12
+
13
+ LINE_LENGTH_LIMIT = 128
14
+ LITERAL_SIZE_LIMIT = 1024**2
15
+ COMMAND_SIZE_LIMIT = LITERAL_SIZE_LIMIT + LINE_LENGTH_LIMIT
16
+
9
17
  def setup
10
18
  @input = StringIO.new('', 'r')
11
19
  @output = StringIO.new('', 'w')
12
20
  @logger = Logger.new(STDOUT)
13
21
  @logger.level = ($DEBUG) ? Logger::DEBUG : Logger::FATAL
14
- @reader = RIMS::Protocol::RequestReader.new(@input, @output, @logger)
22
+ @reader = RIMS::Protocol::RequestReader.new(@input, @output, @logger,
23
+ line_length_limit: LINE_LENGTH_LIMIT,
24
+ literal_size_limit: LITERAL_SIZE_LIMIT,
25
+ command_size_limit: COMMAND_SIZE_LIMIT)
15
26
  end
16
27
 
17
- def test_scan_line
18
- assert_equal([], @reader.scan_line(''))
19
- assert_equal(%w[ abcd CAPABILITY ], @reader.scan_line('abcd CAPABILITY'))
20
- assert_equal(%w[ abcd OK CAPABILITY completed ], @reader.scan_line('abcd OK CAPABILITY completed'))
21
- assert_equal(%w[ * CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4 ], @reader.scan_line('* CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4'))
22
- assert_equal(%w[ * 172 EXISTS ], @reader.scan_line('* 172 EXISTS'))
23
- assert_equal([ '*', 'OK', '['.intern, 'UNSEEN', '12', ']'.intern, 'Message', '12', 'is', 'first', 'unseen' ],
24
- @reader.scan_line('* OK [UNSEEN 12] Message 12 is first unseen'))
25
- assert_equal([ '*', 'FLAGS', '('.intern, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft', ')'.intern ],
26
- @reader.scan_line('* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)'))
27
- assert_equal([ '*', 'OK', '['.intern, 'PERMANENTFLAGS', '('.intern, '\Deleted', '\Seen', '\*', ')'.intern, ']'.intern, 'Limited' ],
28
- @reader.scan_line('* OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited'))
29
- assert_equal([ 'A82', 'LIST', '', '*' ], @reader.scan_line('A82 LIST "" *'))
30
- assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo' ], @reader.scan_line('* LIST (\Noselect) "/" foo',))
31
- assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo [bar] (baz)' ],
32
- @reader.scan_line('* LIST (\Noselect) "/" "foo [bar] (baz)"'))
33
- assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ], @reader.scan_line('* LIST (\Noselect) NIL ""'))
34
- assert_equal([ 'A654', 'FETCH', '2:4',
35
- '('.intern,
36
- [ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
37
- ')'.intern
38
- ],
39
- @reader.scan_line('A654 FETCH 2:4 (BODY[])'))
40
-
41
- assert_equal('', @output.string)
28
+ data('EOF' => [
29
+ nil,
30
+ ''
31
+ ],
32
+ 'short_line_length' => [
33
+ "foo\r\n",
34
+ "foo\r\n"
35
+ ],
36
+ 'upper_bound_line_length' => [
37
+ 'x' * (LINE_LENGTH_LIMIT - 2) + "\r\n",
38
+ 'x' * (LINE_LENGTH_LIMIT - 2) + "\r\n"
39
+ ])
40
+ def test_gets(data)
41
+ expected_line, input_line = data
42
+ @input.string = input_line
43
+ assert_equal(expected_line, @reader.gets)
42
44
  end
43
45
 
44
- def test_scan_line_string_literal
45
- literal = <<-'EOF'
46
- Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
47
- From: Fred Foobar <foobar@Blurdybloop.COM>
48
- Subject: afternoon meeting
49
- To: mooch@owatagu.siam.edu
50
- Message-Id: <B27397-0100000@Blurdybloop.COM>
51
- MIME-Version: 1.0
52
- Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
53
-
54
- Hello Joe, do you think we can meet at 3:30 tomorrow?
55
- EOF
56
-
57
- line = 'A003 APPEND saved-messages (\Seen) ' + "{#{literal.bytesize}}"
58
- @input.string = literal + "\r\n"
59
-
60
- assert_equal([ 'A003', 'APPEND', 'saved-messages', '('.intern, '\Seen', ')'.intern, literal ], @reader.scan_line(line))
61
- assert_equal('', @input.read)
62
-
63
- cmd_cont_req = @output.string.each_line
64
- assert_match(/^\+ /, cmd_cont_req.next)
65
- assert_raise(StopIteration) { cmd_cont_req.next }
46
+ data('too_long_line_length' => [
47
+ 'x' * LINE_LENGTH_LIMIT,
48
+ 'x' * LINE_LENGTH_LIMIT + "yyy\r\n"
49
+ ],
50
+ 'upper_bound_line_length' => [
51
+ 'x' * LINE_LENGTH_LIMIT,
52
+ 'x' * LINE_LENGTH_LIMIT
53
+ ])
54
+ def test_gets_line_too_long_error
55
+ expected_line_fragment, input_line = data
56
+ @input.string = input_line
57
+ error = assert_raise(RIMS::LineTooLongError) { @reader.gets }
58
+ assert_equal(expected_line_fragment, error.optional_data[:line_fragment])
66
59
  end
67
60
 
68
- def test_read_line
69
- assert_nil(@reader.read_line)
70
-
71
- @input.string = "\n"
72
- assert_equal([], @reader.read_line)
73
-
74
- @input.string = "abcd CAPABILITY\n"
75
- assert_equal(%w[ abcd CAPABILITY ], @reader.read_line)
76
-
77
- @input.string = "abcd OK CAPABILITY completed\n"
78
- assert_equal(%w[ abcd OK CAPABILITY completed ], @reader.read_line)
79
-
80
- @input.string = "* CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4\n"
81
- assert_equal(%w[ * CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4 ], @reader.read_line)
82
-
83
- @input.string = "* 172 EXISTS\n"
84
- assert_equal(%w[ * 172 EXISTS ], @reader.read_line)
85
-
86
- @input.string = "* OK [UNSEEN 12] Message 12 is first unseen\n"
87
- assert_equal([ '*', 'OK', '['.intern, 'UNSEEN', '12', ']'.intern, 'Message', '12', 'is', 'first', 'unseen', ], @reader.read_line)
88
-
89
- @input.string = "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\n"
90
- assert_equal([ '*', 'FLAGS', '('.intern, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft', ')'.intern ], @reader.read_line)
91
-
92
- @input.string = "* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited\n"
93
- assert_equal([ '*', 'OK',
94
- '['.intern, 'PERMANENTFLAGS', '('.intern, '\Deleted', '\Seen', '\*', ')'.intern, ']'.intern,
95
- 'Limited'
96
- ], @reader.read_line)
97
-
98
- @input.string = "A82 LIST \"\" *\n"
99
- assert_equal([ 'A82', 'LIST', '', '*' ], @reader.read_line)
100
-
101
- @input.string = "* LIST (\\Noselect) \"/\" foo\n"
102
- assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo' ], @reader.read_line)
103
-
104
- @input.string = "* LIST (\\Noselect) \"/\" \"foo [bar] (baz)\""
105
- assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo [bar] (baz)' ], @reader.read_line)
106
-
107
- @input.string = '* LIST (\Noselect) NIL ""'
108
- assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ], @reader.read_line)
109
-
110
- @input.string = "A654 FETCH 2:4 (BODY[])\n"
111
- assert_equal([ 'A654', 'FETCH', '2:4',
112
- '('.intern,
113
- [ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
114
- ')'.intern
115
- ],
116
- @reader.read_line)
117
-
118
- assert_equal('', @output.string)
119
- end
120
-
121
- def test_read_line_string_literal
122
- literal = <<-'EOF'
61
+ LITERAL_1 = <<-'EOF'
123
62
  Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
124
63
  From: Fred Foobar <foobar@Blurdybloop.COM>
125
64
  Subject: afternoon meeting
@@ -129,34 +68,133 @@ MIME-Version: 1.0
129
68
  Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
130
69
 
131
70
  Hello Joe, do you think we can meet at 3:30 tomorrow?
132
- EOF
71
+ EOF
133
72
 
134
- @input.string = "A003 APPEND saved-messages (\\Seen) {#{literal.bytesize}}\n" + literal + "\n"
135
- assert_equal([ 'A003', 'APPEND', 'saved-messages', '('.intern, '\Seen', ')'.intern, literal ], @reader.read_line)
136
- assert_equal('', @input.read)
73
+ LITERAL_2 = <<-'EOF'
74
+ Subject: parse test
137
75
 
138
- cmd_cont_req = @output.string.each_line
139
- assert_match(/^\+ /, cmd_cont_req.next)
140
- assert_raise(StopIteration) { cmd_cont_req.next }
76
+ body[]
77
+ EOF
78
+
79
+ LITERAL_3 = 'body[]'
80
+
81
+ data('empty' => [ nil, nil ],
82
+ 'newline' => [ nil, "\n" ],
83
+ 'whitespaces' => [ nil, " \t\n" ],
84
+ 'tagged_command' => [ %w[ abcd CAPABILITY ], "abcd CAPABILITY\n" ],
85
+ 'tagged_command_with_whitespaces' => [ %w[ abcd CAPABILITY ], "\n \n\t\nabcd CAPABILITY\n" ],
86
+ 'tagged_response' => [ %w[ abcd OK CAPABILITY completed ], "abcd OK CAPABILITY completed\n" ],
87
+ 'group' => [
88
+ [ 'A003', 'STORE', '2:4', '+FLAGS', [ :group, '\Deleted' ] ],
89
+ "A003 STORE 2:4 +FLAGS (\\Deleted)\n"
90
+ ],
91
+ 'nested_group' => [
92
+ [ 'abcd', 'SEARCH',
93
+ [ :group,
94
+ 'OR',
95
+ [ :group, 'FROM', 'foo' ],
96
+ [ :group, 'FROM', 'bar' ]
97
+ ]
98
+ ],
99
+ "abcd SEARCH (OR (FROM foo) (FROM bar))\n"
100
+ ],
101
+ 'quoted_special_nil' => [
102
+ [ 'abcd', 'SEARCH', 'SUBJECT', 'NIL' ],
103
+ "abcd SEARCH SUBJECT \"NIL\"\n"
104
+ ],
105
+ 'quoted_special_parenthesis_begin' => [
106
+ [ 'abcd', 'SEARCH', 'SUBJECT', '(' ],
107
+ "abcd SEARCH SUBJECT \"(\"\n"
108
+ ],
109
+ 'quoted_special_parenthesis_end' => [
110
+ [ 'abcd', 'SEARCH', 'SUBJECT', ')' ],
111
+ "abcd SEARCH SUBJECT \")\"\n"
112
+ ],
113
+ 'quoted_special_square_bracket_begin' => [
114
+ [ 'abcd', 'SEARCH', 'SUBJECT', '[' ],
115
+ "abcd SEARCH SUBJECT \"[\"\n"
116
+ ],
117
+ 'quoted_special_square_bracket_end' => [
118
+ [ 'abcd', 'SEARCH', 'SUBJECT', ']' ],
119
+ "abcd SEARCH SUBJECT \"]\"\n"
120
+ ],
121
+ 'fetch_non_extensible_bodystructure' => [
122
+ [ 'A654', 'FETCH', '2:4', [ :group, 'BODY' ] ],
123
+ "A654 FETCH 2:4 (BODY)\n"
124
+ ],
125
+ 'fetch_body_section' => [
126
+ [ 'A654', 'FETCH', '2:4', [
127
+ :group, [
128
+ :body,
129
+ RIMS::Protocol.body(symbol: 'BODY',
130
+ section: '',
131
+ section_list: [])
132
+ ]
133
+ ]
134
+ ],
135
+ "A654 FETCH 2:4 (BODY[])\n"
136
+ ],
137
+ 'fetch_multiple_items' => [
138
+ [ 'A654', 'FETCH', '2:4',
139
+ [ :group,
140
+ 'FLAGS',
141
+ [ :body,
142
+ RIMS::Protocol.body(symbol: 'BODY',
143
+ option: 'PEEK',
144
+ section: 'HEADER.FIELDS (DATE FROM)',
145
+ section_list: [ 'HEADER.FIELDS', [ :group, 'DATE', 'FROM' ] ],
146
+ partial_origin: 0,
147
+ partial_size: 1500)
148
+ ]
149
+ ]
150
+ ],
151
+ "A654 FETCH 2:4 (FLAGS BODY.PEEK[HEADER.FIELDS (DATE FROM)]<0.1500>)\n"
152
+ ],
153
+ 'append_inline' => [
154
+ [ 'A654', 'APPEND', 'saved-messages', 'body[]' ],
155
+ "A654 APPEND saved-messages \"body[]\"\n"
156
+ ],
157
+ 'append_literal_1' => [
158
+ [ 'A003', 'APPEND', 'saved-messages', [ :group, '\Seen' ], LITERAL_1 ],
159
+ "A003 APPEND saved-messages (\\Seen) #{literal(LITERAL_1)}\n",
160
+ true
161
+ ],
162
+ 'append_literal_2' => [
163
+ [ 'A004', 'APPEND', 'saved-messages', LITERAL_2 ],
164
+ "A004 APPEND saved-messages #{literal(LITERAL_2)}\n",
165
+ true
166
+ ],
167
+ 'append_literal_3' => [
168
+ [ 'A005', 'APPEND', 'saved-messages', LITERAL_3 ],
169
+ "A005 APPEND saved-messages #{literal(LITERAL_3)}\n",
170
+ true
171
+ ],
172
+ 'append_literal_size_limit' => [
173
+ [ 'A005', 'APPEND', 'saved-messages', 'x' * LITERAL_SIZE_LIMIT ],
174
+ "A005 APPEND saved-messages #{literal('x' * LITERAL_SIZE_LIMIT)}\n",
175
+ true
176
+ ])
177
+ def test_read_command(data)
178
+ expected_atom_list, input_string, is_literal = data
179
+ @input.string = input_string if input_string
180
+ assert_equal(expected_atom_list, @reader.read_command)
181
+ assert_equal(expected_atom_list[0], @reader.command_tag) if expected_atom_list
182
+
183
+ if (is_literal) then
184
+ cmd_cont_req = @output.string.each_line
185
+ assert_match(/^\+ /, cmd_cont_req.next)
186
+ assert_raise(StopIteration) { cmd_cont_req.next }
187
+ else
188
+ assert_equal('', @output.string)
189
+ end
141
190
  end
142
191
 
143
- def test_read_line_string_literal_multi
144
- literal1 = <<-'EOF'
145
- Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
146
- From: Fred Foobar <foobar@Blurdybloop.COM>
147
- Subject: afternoon meeting
148
- To: mooch@owatagu.siam.edu
149
- Message-Id: <B27397-0100000@Blurdybloop.COM>
150
- MIME-Version: 1.0
151
- Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
152
- EOF
153
-
154
- literal2 = <<-'EOF'
155
- Hello Joe, do you think we can meet at 3:30 tomorrow?
156
- EOF
192
+ def test_read_command_string_literal_multi
193
+ literal_pair = RIMS::RFC822::Parse.split_message(LITERAL_1)
157
194
 
158
- @input.string = "* ({#{literal1.bytesize}}\n" + literal1 + " {#{literal2.bytesize}}\n" + literal2 + ")\n"
159
- assert_equal([ '*', '('.intern, literal1, literal2, ')'.intern ], @reader.read_line)
195
+ @input.string = "A284 SEARCH CHARSET UTF-8 TEXT #{literal(literal_pair[0])} TEXT #{literal(literal_pair[1])}\n"
196
+ assert_equal([ 'A284', 'SEARCH', 'CHARSET', 'UTF-8', 'TEXT', literal_pair[0], 'TEXT', literal_pair[1] ], @reader.read_command)
197
+ assert_equal('A284', @reader.command_tag)
160
198
  assert_equal('', @input.read)
161
199
 
162
200
  cmd_cont_req = @output.string.each_line
@@ -165,169 +203,156 @@ Hello Joe, do you think we can meet at 3:30 tomorrow?
165
203
  assert_raise(StopIteration) { cmd_cont_req.next }
166
204
  end
167
205
 
168
- def test_parse
169
- assert_equal([], @reader.parse([]))
170
- assert_equal(%w[ abcd CAPABILITY ], @reader.parse(%w[ abcd CAPABILITY ]))
171
- assert_equal(%w[ abcd OK CAPABILITY completed ], @reader.parse(%w[ abcd OK CAPABILITY completed ]))
172
- assert_equal([ '*', 'OK', [ :block, 'UNSEEN', '12' ], 'Message', '12', 'is', 'first', 'unseen' ],
173
- @reader.parse([ '*', 'OK', '['.intern, 'UNSEEN', '12', ']'.intern, 'Message', '12', 'is', 'first', 'unseen' ]))
174
- assert_equal([ '*', 'FLAGS', [ :group, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft' ] ],
175
- @reader.parse([ '*', 'FLAGS', '('.intern, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft', ')'.intern ]))
176
- assert_equal([ '*', 'OK', [ :block, 'PERMANENTFLAGS', [ :group, '\Deleted', '\Seen', '\*' ] ], 'Limited' ],
177
- @reader.parse([ '*', 'OK', '['.intern, 'PERMANENTFLAGS', '('.intern, '\Deleted', '\Seen', '\*', ')'.intern, ']'.intern, 'Limited' ]))
178
- assert_equal([ '*', 'LIST', [ :group, '\Noselect' ], :NIL, '' ],
179
- @reader.parse([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ]))
180
- assert_equal([ 'A654', 'FETCH', '2:4',
181
- [ :group,
182
- [ :body,
183
- RIMS::Protocol.body(symbol: 'BODY',
184
- section: '',
185
- section_list: [])
186
- ]
187
- ]
188
- ],
189
- @reader.parse([ 'A654', 'FETCH', '2:4',
190
- '('.intern,
191
- [ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
192
- ')'.intern
193
- ]))
194
- assert_equal([ 'A003', 'APPEND', 'saved-messages', "foo\nbody[]\nbar\n" ],
195
- @reader.parse([ 'A003', 'APPEND', 'saved-messages', "foo\nbody[]\nbar\n" ]))
196
-
197
- error = assert_raise(RIMS::SyntaxError) { @reader.parse([ '*', 'OK', '['.intern, 'UNSEEN', '12' ]) }
198
- assert_match(/not found a terminator/, error.message)
199
-
200
- error = assert_raise(RIMS::SyntaxError) { @reader.parse([ '*', 'LIST', '('.intern, '\Noselect' ]) }
201
- assert_match(/not found a terminator/, error.message)
202
-
203
- assert_equal('', @output.string)
206
+ def test_read_command_no_tag_error
207
+ @input.string = "noop\r\n"
208
+ error = assert_raise(RIMS::SyntaxError) { @reader.read_command }
209
+ assert_match(/need for tag/, error.message)
204
210
  end
205
211
 
206
- def test_read_command
207
- assert_nil(@reader.read_command)
208
-
209
- @input.string = "\n"
210
- assert_nil(@reader.read_command)
211
-
212
- @input.string = " \t\n"
213
- assert_nil(@reader.read_command)
214
-
215
- @input.string = "abcd CAPABILITY\n"
216
- assert_equal(%w[ abcd CAPABILITY ], @reader.read_command)
217
-
218
- @input.string = "\n \n\t\nabcd CAPABILITY\n"
219
- assert_equal(%w[ abcd CAPABILITY ], @reader.read_command)
220
-
221
- @input.string = "abcd OK CAPABILITY completed\n"
222
- assert_equal(%w[ abcd OK CAPABILITY completed ], @reader.read_command)
223
-
224
- @input.string = "A003 STORE 2:4 +FLAGS (\\Deleted)\n"
225
- assert_equal([ 'A003', 'STORE', '2:4', '+FLAGS', [ :group, '\Deleted' ] ], @reader.read_command)
226
-
227
- @input.string = "abcd SEARCH (OR (FROM foo) (FROM bar))\n"
228
- assert_equal([ 'abcd', 'SEARCH',
229
- [ :group,
230
- 'OR',
231
- [ :group, 'FROM', 'foo' ],
232
- [ :group, 'FROM', 'bar' ]
233
- ]
234
- ],
235
- @reader.read_command)
236
-
237
- @input.string = "abcd SEARCH SUBJECT \"NIL\"\n"
238
- assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', 'NIL' ], @reader.read_command)
239
-
240
- @input.string = "abcd SEARCH SUBJECT \"(\"\n"
241
- assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', '(' ], @reader.read_command)
242
-
243
- @input.string = "abcd SEARCH SUBJECT \")\"\n"
244
- assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', ')' ], @reader.read_command)
245
-
246
- @input.string = "abcd SEARCH SUBJECT \"[\"\n"
247
- assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', '[' ], @reader.read_command)
248
-
249
- @input.string = "abcd SEARCH SUBJECT \"]\"\n"
250
- assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', ']' ], @reader.read_command)
251
-
252
- @input.string = "A654 FETCH 2:4 (BODY)\n"
253
- assert_equal([ 'A654', 'FETCH', '2:4', [ :group, 'BODY' ] ], @reader.read_command)
254
-
255
- @input.string = "A654 FETCH 2:4 (BODY[])\n"
256
- assert_equal([ 'A654', 'FETCH', '2:4', [
257
- :group, [
258
- :body,
259
- RIMS::Protocol.body(symbol: 'BODY',
260
- section: '',
261
- section_list: [])
262
- ]
263
- ]
264
- ], @reader.read_command)
265
-
266
- @input.string = "A654 FETCH 2:4 (FLAGS BODY.PEEK[HEADER.FIELDS (DATE FROM)]<0.1500>)\n"
267
- assert_equal([ 'A654', 'FETCH', '2:4',
268
- [ :group,
269
- 'FLAGS',
270
- [ :body,
271
- RIMS::Protocol.body(symbol: 'BODY',
272
- option: 'PEEK',
273
- section: 'HEADER.FIELDS (DATE FROM)',
274
- section_list: [ 'HEADER.FIELDS', [ :group, 'DATE', 'FROM' ] ],
275
- partial_origin: 0,
276
- partial_size: 1500)
277
- ]
278
- ]
279
- ],
280
- @reader.read_command)
281
-
282
- @input.string = "A654 APPEND saved-messages \"body[]\"\n"
283
- assert_equal([ 'A654', 'APPEND', 'saved-messages', 'body[]' ], @reader.read_command)
284
-
285
- assert_equal('', @output.string)
286
-
287
- literal = <<-'EOF'
288
- Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
289
- From: Fred Foobar <foobar@Blurdybloop.COM>
290
- Subject: afternoon meeting
291
- To: mooch@owatagu.siam.edu
292
- Message-Id: <B27397-0100000@Blurdybloop.COM>
293
- MIME-Version: 1.0
294
- Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
295
-
296
- Hello Joe, do you think we can meet at 3:30 tomorrow?
297
- EOF
298
-
299
- @input.string = "A003 APPEND saved-messages (\\Seen) {#{literal.bytesize}}\n" + literal + "\n"
300
- @output.string = ''
301
- assert_equal([ 'A003', 'APPEND', 'saved-messages', [ :group, '\Seen' ], literal ], @reader.read_command)
302
-
303
- cmd_cont_req = @output.string.each_line
304
- assert_match(/^\+ /, cmd_cont_req.next)
305
- assert_raise(StopIteration) { cmd_cont_req.next }
306
-
212
+ data('*' => %w[ * * ],
213
+ '+' => %w[ + + ],
214
+ 'not_string' => [ '{123}', [ :literal, 123 ] ])
215
+ def test_read_command_invalid_tag_error(data)
216
+ invalid_tag, parsed_tag = data
217
+ @input.string = invalid_tag
218
+ error = assert_raise(RIMS::SyntaxError) { @reader.read_command }
219
+ assert_match(/invalid command tag/, error.message)
220
+ assert_include(error.message, parsed_tag.to_s)
221
+ end
307
222
 
308
- literal2 = <<-'EOF'
309
- Subject: parse test
223
+ def test_read_command_line_too_long_error
224
+ line = 'X001 x'
225
+ while (line.bytesize < LINE_LENGTH_LIMIT)
226
+ line << 'x'
227
+ end
228
+ line << "x\r\n"
229
+ assert_operator(line.bytesize, :>, LINE_LENGTH_LIMIT)
230
+
231
+ @input.string = line
232
+ assert_raise(RIMS::LineTooLongError) { @reader.read_command }
233
+ assert_equal("x\r\n", @input.read, 'not read end of line')
234
+ end
310
235
 
311
- body[]
312
- EOF
236
+ def test_read_command_literal_size_too_large_error
237
+ @input.string = "A001 APPEND saved-messages #{literal('x' * (LITERAL_SIZE_LIMIT + 1))}\n"
238
+ assert_raise(RIMS::LiteralSizeTooLargeError) { @reader.read_command }
239
+ assert_equal('x' * (LITERAL_SIZE_LIMIT + 1) + "\n", @input.read, 'not read literal')
240
+ end
313
241
 
314
- @input.string = "A004 APPEND saved-messages {#{literal2.bytesize}}\n" + literal2 + "\n"
315
- @output.string = ''
316
- assert_equal([ 'A004', 'APPEND', 'saved-messages', literal2 ], @reader.read_command)
242
+ def test_read_command_command_size_too_large_error_by_line
243
+ @input.string = "A001 APPEND #{literal('x' * LITERAL_SIZE_LIMIT)} #{'y' * (LINE_LENGTH_LIMIT - 2)}\n"
244
+ assert_raise(RIMS::CommandSizeTooLargeError) { @reader.read_command }
245
+ assert_equal('', @input.read, 'read whole a line')
246
+ end
317
247
 
318
- cmd_cont_req = @output.string.each_line
319
- assert_match(/^\+ /, cmd_cont_req.next)
320
- assert_raise(StopIteration) { cmd_cont_req.next }
248
+ def test_read_command_command_size_too_large_error_by_literal
249
+ @input.string = "A001 APPEND #{literal('x' * LITERAL_SIZE_LIMIT)} #{literal('y' * LITERAL_SIZE_LIMIT)}\n"
250
+ assert_raise(RIMS::CommandSizeTooLargeError) { @reader.read_command }
251
+ assert_equal('y' * (LITERAL_SIZE_LIMIT) + "\n", @input.read, 'not read literal')
252
+ end
253
+ end
321
254
 
322
- literal3 = 'body[]'
255
+ class ProtocolRequestReaderClassMethodTest < Test::Unit::TestCase
256
+ data('empty' => [ [], '' ],
257
+ 'tagged_command' => [ %w[ abcd CAPABILITY ], 'abcd CAPABILITY' ],
258
+ 'tagged_response' => [ %w[ abcd OK CAPABILITY completed ], 'abcd OK CAPABILITY completed' ],
259
+ 'untagged_response' => [ %w[ * CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4 ], '* CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4' ],
260
+ 'untagged_exists' => [ %w[ * 172 EXISTS ], '* 172 EXISTS' ],
261
+ 'untagged_unseen' => [
262
+ [ '*', 'OK', '['.intern, 'UNSEEN', '12', ']'.intern, 'Message', '12', 'is', 'first', 'unseen' ],
263
+ '* OK [UNSEEN 12] Message 12 is first unseen'
264
+ ],
265
+ 'untagged_flags' => [
266
+ [ '*', 'FLAGS', '('.intern, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft', ')'.intern ],
267
+ '* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)'
268
+ ],
269
+ 'untagged_permanentflags' => [
270
+ [ '*', 'OK', '['.intern, 'PERMANENTFLAGS', '('.intern, '\Deleted', '\Seen', '\*', ')'.intern, ']'.intern, 'Limited' ],
271
+ '* OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited'
272
+ ],
273
+ 'list_empty_reference' => [
274
+ [ 'A82', 'LIST', '', '*' ],
275
+ 'A82 LIST "" *'
276
+ ],
277
+ 'untagged_list_delimiter' => [
278
+ [ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo' ],
279
+ '* LIST (\Noselect) "/" foo'
280
+ ],
281
+ 'untagged_list_quoted_name' => [
282
+ [ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo [bar] (baz)' ],
283
+ '* LIST (\Noselect) "/" "foo [bar] (baz)"'
284
+ ],
285
+ 'untagged_list_nil_delimiter' => [
286
+ [ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ],
287
+ '* LIST (\Noselect) NIL ""'
288
+ ],
289
+ 'fetch_body_command' => [
290
+ [ 'A654', 'FETCH', '2:4',
291
+ '('.intern,
292
+ [ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
293
+ ')'.intern
294
+ ],
295
+ 'A654 FETCH 2:4 (BODY[])'
296
+ ],
297
+ 'append_literal' => [
298
+ [ 'A003', 'APPEND', 'saved-messages', '('.intern, '\Seen', ')'.intern, [ :literal, 310 ] ],
299
+ 'A003 APPEND saved-messages (\Seen) {310}'
300
+ ])
301
+ def test_scan(data)
302
+ expected_atom_list, line = data
303
+ assert_equal(expected_atom_list, RIMS::Protocol::RequestReader.scan(line))
304
+ end
323
305
 
324
- @input.string = "A005 APPEND saved-messages {#{literal3.bytesize}}\n" + literal3 + "\n"
325
- @output.string = ''
326
- assert_equal([ 'A005', 'APPEND', 'saved-messages', literal3 ], @reader.read_command)
306
+ data('empty' => [ [], [] ],
307
+ 'tagged_command' => [ %w[ abcd CAPABILITY ], %w[ abcd CAPABILITY ] ],
308
+ 'tagged_response' => [ %w[ abcd OK CAPABILITY completed ], %w[ abcd OK CAPABILITY completed ] ],
309
+ 'untagged_unseen' => [
310
+ [ '*', 'OK', [ :block, 'UNSEEN', '12' ], 'Message', '12', 'is', 'first', 'unseen' ],
311
+ [ '*', 'OK', '['.intern, 'UNSEEN', '12', ']'.intern, 'Message', '12', 'is', 'first', 'unseen' ]
312
+ ],
313
+ 'untagged_flags' =>[
314
+ [ '*', 'FLAGS', [ :group, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft' ] ],
315
+ [ '*', 'FLAGS', '('.intern, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft', ')'.intern ]
316
+ ],
317
+ 'untagged_permanentflags' => [
318
+ [ '*', 'OK', [ :block, 'PERMANENTFLAGS', [ :group, '\Deleted', '\Seen', '\*' ] ], 'Limited' ],
319
+ [ '*', 'OK', '['.intern, 'PERMANENTFLAGS', '('.intern, '\Deleted', '\Seen', '\*', ')'.intern, ']'.intern, 'Limited' ]
320
+ ],
321
+ 'untagged_list_nil_delimiter' => [
322
+ [ '*', 'LIST', [ :group, '\Noselect' ], :NIL, '' ],
323
+ [ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ]
324
+ ],
325
+ 'fetch_body_command' => [
326
+ [ 'A654', 'FETCH', '2:4',
327
+ [ :group,
328
+ [ :body,
329
+ RIMS::Protocol.body(symbol: 'BODY',
330
+ section: '',
331
+ section_list: [])
332
+ ]
333
+ ]
334
+ ],
335
+ [ 'A654', 'FETCH', '2:4',
336
+ '('.intern,
337
+ [ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
338
+ ')'.intern
339
+ ]
340
+ ],
341
+ 'append_command' => [
342
+ [ 'A003', 'APPEND', 'saved-messages', "foo\nbody[]\nbar\n" ],
343
+ [ 'A003', 'APPEND', 'saved-messages', "foo\nbody[]\nbar\n" ]
344
+ ])
345
+ def test_parse(data)
346
+ expected_atom_list, input_atom_list = data
347
+ assert_equal(expected_atom_list, RIMS::Protocol::RequestReader.parse(input_atom_list))
348
+ end
327
349
 
328
- cmd_cont_req = @output.string.each_line
329
- assert_match(/^\+ /, cmd_cont_req.next)
330
- assert_raise(StopIteration) { cmd_cont_req.next }
350
+ data('unclosed_square_bracket' => [ '*', 'OK', '['.intern, 'UNSEEN', '12' ],
351
+ 'unclosed_parenthesis' => [ '*', 'LIST', '('.intern, '\Noselect' ])
352
+ def test_parse_not_found_a_terminator_error(data)
353
+ input_atom_list = data
354
+ error = assert_raise(RIMS::SyntaxError) { RIMS::Protocol::RequestReader.parse(input_atom_list) }
355
+ assert_match(/not found a terminator/, error.message)
331
356
  end
332
357
  end
333
358
  end