rims 0.2.6 → 0.3.1

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.
@@ -516,25 +516,27 @@ module RIMS::Test
516
516
  assert_fetch(2,
517
517
  [
518
518
  'BODY ' +
519
- encode_bodystructure([ 'APPLICATION',
520
- 'OCTET-STREAM',
519
+ encode_bodystructure([ 'TEXT',
520
+ 'PLAIN',
521
521
  nil,
522
522
  nil,
523
523
  nil,
524
524
  nil,
525
- @empty_mail.raw_source.bytesize
525
+ @empty_mail.raw_source.bytesize,
526
+ @empty_mail.raw_source.each_line.count
526
527
  ])
527
528
  ])
528
529
  assert_fetch(3,
529
530
  [
530
531
  'BODY ' +
531
- encode_bodystructure([ 'APPLICATION',
532
- 'OCTET-STREAM',
532
+ encode_bodystructure([ 'TEXT',
533
+ 'PLAIN',
533
534
  nil,
534
535
  nil,
535
536
  nil,
536
537
  nil,
537
- @no_body_mail.raw_source.bytesize
538
+ @no_body_mail.raw_source.bytesize,
539
+ @no_body_mail.raw_source.each_line.count
538
540
  ])
539
541
  ])
540
542
  }
@@ -654,13 +656,14 @@ module RIMS::Test
654
656
  ])
655
657
  assert_fetch(2, [
656
658
  'BODYSTRUCTURE ' +
657
- encode_bodystructure([ 'APPLICATION',
658
- 'OCTET-STREAM',
659
+ encode_bodystructure([ 'TEXT',
660
+ 'PLAIN',
659
661
  nil,
660
662
  nil,
661
663
  nil,
662
664
  nil,
663
665
  @empty_mail.raw_source.bytesize,
666
+ @empty_mail.raw_source.each_line.count,
664
667
  nil,
665
668
  nil,
666
669
  nil,
@@ -669,13 +672,14 @@ module RIMS::Test
669
672
  ])
670
673
  assert_fetch(3, [
671
674
  'BODYSTRUCTURE ' +
672
- encode_bodystructure([ 'APPLICATION',
673
- 'OCTET-STREAM',
675
+ encode_bodystructure([ 'TEXT',
676
+ 'PLAIN',
674
677
  nil,
675
678
  nil,
676
679
  nil,
677
680
  nil,
678
681
  @no_body_mail.raw_source.bytesize,
682
+ @no_body_mail.raw_source.each_line.count,
679
683
  nil,
680
684
  nil,
681
685
  nil,
@@ -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