carterdte_smtp_filter 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="ISO-8859-1" ?>
2
+ <RESULTADO_ENVIO>
3
+ <IDENTIFICACION>
4
+ <RUTEMISOR>81137900-K</RUTEMISOR>
5
+ <RUTENVIA>11862736-9</RUTENVIA>
6
+ <TRACKID>1031250770</TRACKID>
7
+ <TMSTRECEPCION>09/05/2015 12:18:50</TMSTRECEPCION>
8
+ <ESTADO>EPR - Envio Procesado</ESTADO>
9
+ </IDENTIFICACION>
10
+ <ESTADISTICA>
11
+ <SUBTOTAL>
12
+ <TIPODOC>33</TIPODOC>
13
+ <INFORMADO>1</INFORMADO>
14
+ <ACEPTA>1</ACEPTA>
15
+ </SUBTOTAL>
16
+ </ESTADISTICA>
17
+ </RESULTADO_ENVIO>
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ class TestApiClient < Minitest::Test
4
+
5
+
6
+ def setup
7
+ CarterdteSmtpFilter::Config.parse("./test/fixtures/config.yml")
8
+ @api_host = CarterdteSmtpFilter::Config::api_host
9
+ @api_url = "http://#{@api_host}"
10
+ stub_request(:any, /#{@api_host}/).to_rack(FakeApi)
11
+ end
12
+
13
+ def test_push_should_return_false_if_message_is_not_json
14
+ response = CarterdteSmtpFilter::ApiClient.push "hola"
15
+ assert(!response, "Failure message.")
16
+ end
17
+
18
+ def test_check_fake_api_response
19
+ user = CarterdteSmtpFilter::Config::api_user.gsub(/@/,"%40")
20
+ response = RestClient.get "http://#{user}:#{CarterdteSmtpFilter::Config::api_password}@#{@api_host}/api/v1/"
21
+ assert_equal("{api_version: 1}", response)
22
+ end
23
+
24
+ def test_post_should_workout_unauthorized
25
+ response = CarterdteSmtpFilter::ApiClient.post({url: "#{@api_url}/api/v1/denied" })
26
+ assert(!response, "Failure message.")
27
+ end
28
+
29
+ def test_post_should_workout_application_error
30
+ response = CarterdteSmtpFilter::ApiClient.post({url: "#{@api_url}/api/v1/app_error" })
31
+ assert(!response, "Failure message.")
32
+ end
33
+
34
+ def test_post_should_return_json
35
+ hash = {dte_type: 33, msg_type: "envio"}
36
+ json = JSON.generate hash
37
+ response = CarterdteSmtpFilter::ApiClient.post({payload: json})
38
+ new_hash = JSON.parse response
39
+ assert_equal("123456", new_hash["id"])
40
+ assert_equal(hash[:dte_type], new_hash["dte_type"])
41
+ end
42
+
43
+ end
@@ -0,0 +1,20 @@
1
+ require 'test_helper'
2
+
3
+ class TestConfig < Minitest::Test
4
+
5
+ def test_should_raise_not_file_if_file_not_exists
6
+ assert_raises(Errno::ENOENT) { CarterdteSmtpFilter::Config.parse("/tmp/file") }
7
+ end
8
+
9
+ def test_should_return_config_values
10
+ CarterdteSmtpFilter::Config.parse("./test/fixtures/config.yml")
11
+ assert_equal("127.0.0.1", CarterdteSmtpFilter::Config::bind_address)
12
+ assert_equal("127.0.0.1", CarterdteSmtpFilter::Config::return_host)
13
+ assert_equal("30025", CarterdteSmtpFilter::Config::return_port)
14
+ assert_equal("pbruna@example.com", CarterdteSmtpFilter::Config::api_user)
15
+ assert_equal("123456", CarterdteSmtpFilter::Config::api_password)
16
+ assert_equal("api.dte.zboxapp.com", CarterdteSmtpFilter::Config::api_host)
17
+ assert_equal("./test/tmp/carterdte_smtp_filter.log", CarterdteSmtpFilter::Config::log_file)
18
+ end
19
+
20
+ end
data/test/test_dte.rb ADDED
@@ -0,0 +1,79 @@
1
+ require 'test_helper'
2
+
3
+ class TestDte < Minitest::Test
4
+
5
+ def setup
6
+ @resultado = File.open("./test/fixtures/acuse.xml", "rb")
7
+ @envio = File.open("./test/fixtures/envio_dte_33.xml", "rb")
8
+ @dte_resultado = CarterdteSmtpFilter::Dte.new @resultado
9
+ @dte_envio = CarterdteSmtpFilter::Dte.new @envio
10
+ end
11
+
12
+ def teardown
13
+
14
+ end
15
+
16
+ def test_should_only_parse_valid_dtes
17
+ file = File.open("./test/fixtures/invalid_dte.xml", "rb")
18
+ dte = CarterdteSmtpFilter::Dte.new file
19
+ assert(!dte.valid?, "Should be false")
20
+ assert(@dte_resultado.valid?, "Failure message.")
21
+ assert(@dte_envio.valid?, "Failure message.")
22
+ end
23
+
24
+ def test_root_should_return_the_root_element
25
+ assert_equal("Resultado", @dte_resultado.root_name)
26
+ assert_equal("SetDTE", @dte_envio.root_name)
27
+ end
28
+
29
+ def test_return_msg_type
30
+ assert_equal("respuesta", @dte_resultado.msg_type)
31
+ assert_equal("envio", @dte_envio.msg_type)
32
+ end
33
+
34
+ def test_return_setdte_id
35
+ assert_equal("SETDTE94528000X33X7597817X94141763", @dte_resultado.setdte_id)
36
+ assert_equal("SETDTE96529310X33X3152118X94141243", @dte_envio.setdte_id)
37
+ end
38
+
39
+ def test_rut_emisor
40
+ assert_equal("94528000-K", @dte_resultado.rut_emisor)
41
+ assert_equal("96529310-8", @dte_envio.rut_emisor)
42
+ end
43
+
44
+ def test_rut_receptor
45
+ assert_equal("81537600-5", @dte_resultado.rut_receptor)
46
+ assert_equal("81201000-K", @dte_envio.rut_receptor)
47
+ end
48
+
49
+ def test_dte_type
50
+ assert_equal("33", @dte_resultado.dte_type)
51
+ assert_equal("33", @dte_envio.dte_type)
52
+ end
53
+
54
+ def test_dte_fecha_emision
55
+ date_envio = Time.parse("2015-05-09T11:10:10").to_date
56
+ date_resultado = Time.parse("2015-05-09T12:30:11").to_date
57
+ assert_equal(date_resultado, @dte_resultado.fecha_emision)
58
+ assert_equal(date_envio, @dte_envio.fecha_emision)
59
+ end
60
+
61
+ def test_dte_fecha_recepcion
62
+ date_resultado = Time.parse("2015-05-09T12:30:11").to_date
63
+ assert_equal(date_resultado, @dte_resultado.fecha_recepcion)
64
+ assert_equal(nil, @dte_envio.fecha_recepcion)
65
+ end
66
+
67
+ def test_return_empty_json_if_no_valid
68
+ file = File.open("./test/fixtures/invalid_dte.xml", "rb")
69
+ dte = CarterdteSmtpFilter::Dte.new file
70
+ json = JSON.parse dte.to_json
71
+ assert_equal({}, json)
72
+ end
73
+
74
+ def test_return_json_object
75
+ json = JSON.parse @dte_resultado.to_json
76
+ assert_equal("81537600-5", json["rut_receptor"])
77
+ end
78
+
79
+ end
@@ -0,0 +1,277 @@
1
+ require "carterdte_smtp_filter"
2
+ require 'pp'
3
+ require 'time'
4
+ require 'date'
5
+
6
+ require 'minitest/autorun'
7
+ require 'minitest/reporters' # requires the gem
8
+ require 'webmock/minitest'
9
+
10
+ require 'fake_api'
11
+
12
+ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # spec-like progress
13
+
14
+ # Only log Celluloid errors
15
+ #Celluloid.logger.level = Logger::ERROR
16
+
17
+
18
+ #CarterDteSmtp::Config.parse("./test/fixtures/config.yml")
19
+
20
+ def check_imap()
21
+ require "mail"
22
+ Mail.defaults do
23
+ retriever_method :imap, address: "localhost", port: 143, user_name: "carterdte", password: "123456"
24
+ end
25
+
26
+ emails = Mail.find keys: ['NOT', 'SEEN']
27
+ emails.first
28
+
29
+ end
30
+
31
+ class Enviacorreo
32
+ attr_accessor :to, :from, :body, :subject, :attachment_path, :server, :port
33
+
34
+ def initialize(to: "carterdte@example.com", from: "dte@itlinux.cl", body: "Cuerpo correo", subject: "DTE", attachment_path: false, server: "localhost", port: 2025)
35
+ @to = to
36
+ @from = from
37
+ @body = body
38
+ @subject = subject
39
+ @attachment_path = attachment_path
40
+ @server = server
41
+ @port = port
42
+ end
43
+
44
+ def send
45
+ message.deliver!
46
+ end
47
+
48
+ def message
49
+ smtp_conn = smtp_options
50
+ Mail.defaults do
51
+ delivery_method :smtp_connection, { :connection => smtp_conn.start }
52
+ end
53
+ mail = Mail.new(
54
+ from: @from,
55
+ to: @to,
56
+ subject: @subject,
57
+ body: @body
58
+ )
59
+ mail.add_file @attachment_path if @attachment_path
60
+ mail
61
+ end
62
+
63
+ def smtp_options
64
+ smtp = Net::SMTP.new(@server, @port)
65
+ smtp.read_timeout = 2
66
+ smtp.open_timeout = 2
67
+ smtp
68
+ end
69
+
70
+ end
71
+
72
+ class ReturnSmtp < MidiSmtpServer::Smtpd
73
+
74
+ attr_accessor :mail
75
+
76
+ def start
77
+ @logger = Logger.new('/dev/null')
78
+ @port = CarterdteSmtpFilter::Config::return_port
79
+ @host = CarterdteSmtpFilter::Config::return_host
80
+ super
81
+ end
82
+
83
+ def on_message_data_event(ctx)
84
+ File.open("./test/tmp/returnmail", "w") {|file| file.write ctx[:message][:data]}
85
+ end
86
+
87
+ def process_line(line)
88
+ # check wether in data or command mode
89
+ if Thread.current[:cmd_sequence] != :CMD_DATA
90
+
91
+ # Handle specific messages from the client
92
+ case line
93
+
94
+ when (/^(HELO|EHLO)(\s+.*)?$/i)
95
+ # HELO/EHLO
96
+ # 250 Requested mail action okay, completed
97
+ # 421 <domain> Service not available, closing transmission channel
98
+ # 500 Syntax error, command unrecognised
99
+ # 501 Syntax error in parameters or arguments
100
+ # 504 Command parameter not implemented
101
+ # 521 <domain> does not accept mail [rfc1846]
102
+ # ---------
103
+ # check valid command sequence
104
+ raise Smtpd503Exception if Thread.current[:cmd_sequence] != :CMD_HELO
105
+ # handle command
106
+ @cmd_data = line.gsub(/^(HELO|EHLO)\ /i, '').strip
107
+ # call event to handle data
108
+ on_helo_event(Thread.current[:ctx], @cmd_data)
109
+ # if no error raised, append to message hash
110
+ Thread.current[:ctx][:server][:helo] = @cmd_data
111
+ # set sequence state as RSET
112
+ Thread.current[:cmd_sequence] = :CMD_RSET
113
+ # reply ok
114
+ return "250 OK"
115
+
116
+ when (/^NOOP\s*$/i)
117
+ # NOOP
118
+ # 250 Requested mail action okay, completed
119
+ # 421 <domain> Service not available, closing transmission channel
120
+ # 500 Syntax error, command unrecognised
121
+ return "250 OK"
122
+
123
+ when (/^RSET\s*$/i)
124
+ # RSET
125
+ # 250 Requested mail action okay, completed
126
+ # 421 <domain> Service not available, closing transmission channel
127
+ # 500 Syntax error, command unrecognised
128
+ # 501 Syntax error in parameters or arguments
129
+ # ---------
130
+ # check valid command sequence
131
+ raise Smtpd503Exception if Thread.current[:cmd_sequence] == :CMD_HELO
132
+ # handle command
133
+ reset_ctx
134
+ return "250 OK"
135
+
136
+ when (/^QUIT\s*$/i)
137
+ # QUIT
138
+ # 221 <domain> Service closing transmission channel
139
+ # 500 Syntax error, command unrecognised
140
+ Thread.current[:cmd_sequence] = :CMD_QUIT
141
+ return ""
142
+
143
+ when (/^MAIL FROM\:/i)
144
+ # MAIL
145
+ # 250 Requested mail action okay, completed
146
+ # 421 <domain> Service not available, closing transmission channel
147
+ # 451 Requested action aborted: local error in processing
148
+ # 452 Requested action not taken: insufficient system storage
149
+ # 500 Syntax error, command unrecognised
150
+ # 501 Syntax error in parameters or arguments
151
+ # 552 Requested mail action aborted: exceeded storage allocation
152
+ # ---------
153
+ # check valid command sequence
154
+ raise Smtpd503Exception if Thread.current[:cmd_sequence] != :CMD_RSET
155
+ # handle command
156
+ @cmd_data = line.gsub(/^MAIL FROM\:/i, '').strip
157
+ # call event to handle data
158
+ if return_value = on_mail_from_event(Thread.current[:ctx], @cmd_data)
159
+ # overwrite data with returned value
160
+ @cmd_data = return_value
161
+ end
162
+ # if no error raised, append to message hash
163
+ Thread.current[:ctx][:envelope][:from] = @cmd_data
164
+ # set sequence state
165
+ Thread.current[:cmd_sequence] = :CMD_MAIL
166
+ # reply ok
167
+ return "250 OK"
168
+
169
+ when (/^RCPT TO\:/i)
170
+ # RCPT
171
+ # 250 Requested mail action okay, completed
172
+ # 251 User not local; will forward to <forward-path>
173
+ # 421 <domain> Service not available, closing transmission channel
174
+ # 450 Requested mail action not taken: mailbox unavailable
175
+ # 451 Requested action aborted: local error in processing
176
+ # 452 Requested action not taken: insufficient system storage
177
+ # 500 Syntax error, command unrecognised
178
+ # 501 Syntax error in parameters or arguments
179
+ # 503 Bad sequence of commands
180
+ # 521 <domain> does not accept mail [rfc1846]
181
+ # 550 Requested action not taken: mailbox unavailable
182
+ # 551 User not local; please try <forward-path>
183
+ # 552 Requested mail action aborted: exceeded storage allocation
184
+ # 553 Requested action not taken: mailbox name not allowed
185
+ # ---------
186
+ # check valid command sequence
187
+ raise Smtpd503Exception if ![ :CMD_MAIL, :CMD_RCPT ].include?(Thread.current[:cmd_sequence])
188
+ # handle command
189
+ @cmd_data = line.gsub(/^RCPT TO\:/i, '').strip
190
+ # call event to handle data
191
+ if return_value = on_rcpt_to_event(Thread.current[:ctx], @cmd_data)
192
+ # overwrite data with returned value
193
+ @cmd_data = return_value
194
+ end
195
+ # if no error raised, append to message hash
196
+ Thread.current[:ctx][:envelope][:to] << @cmd_data
197
+ # set sequence state
198
+ Thread.current[:cmd_sequence] = :CMD_RCPT
199
+ # reply ok
200
+ return "250 OK"
201
+
202
+ when (/^DATA\s*$/i)
203
+ # DATA
204
+ # 354 Start mail input; end with <CRLF>.<CRLF>
205
+ # 250 Requested mail action okay, completed
206
+ # 421 <domain> Service not available, closing transmission channel received data
207
+ # 451 Requested action aborted: local error in processing
208
+ # 452 Requested action not taken: insufficient system storage
209
+ # 500 Syntax error, command unrecognised
210
+ # 501 Syntax error in parameters or arguments
211
+ # 503 Bad sequence of commands
212
+ # 552 Requested mail action aborted: exceeded storage allocation
213
+ # 554 Transaction failed
214
+ # ---------
215
+ # check valid command sequence
216
+ raise Smtpd503Exception if Thread.current[:cmd_sequence] != :CMD_RCPT
217
+ # handle command
218
+ # set sequence state
219
+ Thread.current[:cmd_sequence] = :CMD_DATA
220
+ # reply ok / proceed with message data
221
+ return "354 Enter message, ending with \".\" on a line by itself"
222
+
223
+ else
224
+ # If we somehow get to this point then
225
+ # we have encountered an error
226
+ raise Smtpd500Exception
227
+
228
+ end
229
+
230
+ else
231
+ # If we are in data mode and the entire message consists
232
+ # solely of a period on a line by itself then we
233
+ # are being told to exit data mode
234
+ if (line.chomp =~ /^\.$/)
235
+ # append last chars to message data
236
+ Thread.current[:ctx][:message][:data] += line
237
+ # remove ending line .
238
+ Thread.current[:ctx][:message][:data].gsub!(/\r\n\Z/, '').gsub!(/\.\Z/, '')
239
+ # save delivered time
240
+ Thread.current[:ctx][:message][:delivered] = Time.now.utc
241
+ # save bytesize of message data
242
+ Thread.current[:ctx][:message][:bytesize] = Thread.current[:ctx][:message][:data].bytesize
243
+ # call event
244
+ begin
245
+ on_message_data_event(Thread.current[:ctx])
246
+ array = (0..9).to_a + ("A".."F").to_a
247
+ return "250 2.0.0 Ok: queued as #{array.sample(11).join("")}"
248
+
249
+ # test for SmtpdException
250
+ rescue SmtpdException
251
+ # just re-raise exception set by app
252
+ raise
253
+
254
+ # test all other Exceptions
255
+ rescue Exception => e
256
+ # send correct aborted message to smtp dialog
257
+ raise Smtpd451Exception.new("#{e}")
258
+
259
+ ensure
260
+ # always start with empty values after finishing incoming message
261
+ # and rset command sequence
262
+ reset_ctx
263
+ end
264
+
265
+ else
266
+ # If we are in date mode then we need to add
267
+ # the new data to the message
268
+ Thread.current[:ctx][:message][:data] += line
269
+ return ""
270
+ # command sequence state will stay on :CMD_DATA
271
+
272
+ end
273
+
274
+ end
275
+ end
276
+
277
+ end
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ class TestMessage < Minitest::Test
4
+
5
+ def setup
6
+ CarterdteSmtpFilter::Config.parse("./test/fixtures/config.yml")
7
+ @server = CarterdteSmtpFilter::SmtpServer.new()
8
+ @server.start
9
+ @return_stmp = ReturnSmtp.new()
10
+ @return_stmp.start
11
+ end
12
+
13
+ def teardown
14
+ @server.shutdown
15
+ sleep 2 unless @server.connections == 0
16
+ @server.stop
17
+ @return_stmp.shutdown
18
+ @return_stmp.stop
19
+ end
20
+
21
+ def test_message_should_save_server_response_when_returning_email
22
+ raw_mail = File.open("./test/fixtures/mail.tmp", "rb").read
23
+ message = CarterdteSmtpFilter::Message.new raw_mail
24
+ response = message.return_email
25
+ assert(Net::SMTP::Response == message.response.class, "Response should be a SMTP Response")
26
+ end
27
+
28
+ def test_message_should_save_server_queueid_response_if_any
29
+ raw_mail = File.open("./test/fixtures/mail_with_dte.eml", "rb").read
30
+ message = CarterdteSmtpFilter::Message.new raw_mail
31
+ message.process
32
+ assert_equal(message.response.string.split(/\s+/).last, message.qid)
33
+ end
34
+
35
+ def test_message_to_json_should_return_json_object_with_message_metada
36
+ message = CarterdteSmtpFilter::Message.new File.open("./test/fixtures/mail_with_dte.eml", "rb").read
37
+ assert(JSON.parse(message.to_json), "No JSON")
38
+ json = JSON.parse(message.to_json)
39
+ assert_equal(message.email.to, json["to"])
40
+ assert_equal(message.email.from, json["from"])
41
+ assert_equal(message.email.date.to_s, json["date"])
42
+ assert_equal("96529310-8", json["dte"]["rut_emisor"])
43
+ end
44
+
45
+ def test_extract_dte_should_return_false_if_no_attachments
46
+ message = CarterdteSmtpFilter::Message.new File.open("./test/fixtures/mail.tmp", "rb").read
47
+ assert(!message.extract_dte, "Failure message.")
48
+ end
49
+
50
+ def test_extract_dte_should_return_false_if_no_dte_attachments
51
+ message = CarterdteSmtpFilter::Message.new File.open("./test/fixtures/email_with_attachment_no_dte.eml", "rb").read
52
+ assert(!message.extract_dte, "Failure message.")
53
+ end
54
+
55
+ def test_extract_dte_should_return_mail_part_with_xml_file
56
+ message = CarterdteSmtpFilter::Message.new File.open("./test/fixtures/mail_with_multiple_attachments.eml", "rb").read
57
+ assert(message.dte, "Failure message.")
58
+ end
59
+
60
+
61
+ def test_message_dte_should_be_a_dte_object
62
+ message = CarterdteSmtpFilter::Message.new File.open("./test/fixtures/mail_with_dte.eml", "rb").read
63
+ assert_equal(CarterdteSmtpFilter::Dte, message.dte.class)
64
+ end
65
+
66
+
67
+ end