xmlconv2 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/History.txt +6 -0
  2. data/LICENCE.txt +339 -0
  3. data/Manifest.txt +107 -0
  4. data/README.txt +25 -0
  5. data/Rakefile +28 -0
  6. data/bin/admin +65 -0
  7. data/bin/xmlconvd +19 -0
  8. data/data/grammar/i2.grammar +73 -0
  9. data/src/conversion/bdd_geh.rb +281 -0
  10. data/src/conversion/bdd_i2.rb +102 -0
  11. data/src/conversion/bdd_xml.rb +242 -0
  12. data/src/conversion/geh_bdd.rb +165 -0
  13. data/src/conversion/i2_bdd.rb +271 -0
  14. data/src/conversion/xml_bdd.rb +125 -0
  15. data/src/custom/lookandfeel.rb +47 -0
  16. data/src/i2/address.rb +33 -0
  17. data/src/i2/date.rb +37 -0
  18. data/src/i2/document.rb +26 -0
  19. data/src/i2/header.rb +24 -0
  20. data/src/i2/order.rb +44 -0
  21. data/src/i2/parser.rb +25 -0
  22. data/src/i2/position.rb +37 -0
  23. data/src/i2/record.rb +11 -0
  24. data/src/model/address.rb +20 -0
  25. data/src/model/agreement.rb +10 -0
  26. data/src/model/bdd.rb +26 -0
  27. data/src/model/bsr.rb +16 -0
  28. data/src/model/delivery.rb +15 -0
  29. data/src/model/delivery_item.rb +18 -0
  30. data/src/model/freetext_container.rb +26 -0
  31. data/src/model/id_container.rb +15 -0
  32. data/src/model/invoice.rb +18 -0
  33. data/src/model/invoice_item.rb +11 -0
  34. data/src/model/item.rb +19 -0
  35. data/src/model/item_container.rb +15 -0
  36. data/src/model/name.rb +27 -0
  37. data/src/model/part_info.rb +10 -0
  38. data/src/model/part_info_container.rb +15 -0
  39. data/src/model/party.rb +23 -0
  40. data/src/model/party_container.rb +20 -0
  41. data/src/model/price.rb +10 -0
  42. data/src/model/price_container.rb +18 -0
  43. data/src/model/transaction.rb +28 -0
  44. data/src/state/global.rb +28 -0
  45. data/src/state/global_predefine.rb +11 -0
  46. data/src/state/login.rb +38 -0
  47. data/src/state/transaction.rb +13 -0
  48. data/src/state/transactions.rb +130 -0
  49. data/src/util/application.rb +143 -0
  50. data/src/util/destination.rb +130 -0
  51. data/src/util/invoicer.rb +72 -0
  52. data/src/util/known_user.rb +16 -0
  53. data/src/util/polling_manager.rb +78 -0
  54. data/src/util/session.rb +23 -0
  55. data/src/util/transaction.rb +105 -0
  56. data/src/util/validator.rb +20 -0
  57. data/src/view/foot.rb +27 -0
  58. data/src/view/head.rb +13 -0
  59. data/src/view/login.rb +36 -0
  60. data/src/view/navigation.rb +30 -0
  61. data/src/view/navigationlink.rb +21 -0
  62. data/src/view/pager.rb +73 -0
  63. data/src/view/preformatted.rb +39 -0
  64. data/src/view/template.rb +17 -0
  65. data/src/view/transaction.rb +42 -0
  66. data/src/view/transactions.rb +90 -0
  67. data/test/mock.rb +149 -0
  68. data/test/suite.rb +16 -0
  69. data/test/test_conversion/bdd_geh.rb +533 -0
  70. data/test/test_conversion/bdd_i2.rb +314 -0
  71. data/test/test_conversion/bdd_xml.rb +556 -0
  72. data/test/test_conversion/geh_bdd.rb +372 -0
  73. data/test/test_conversion/i2_bdd.rb +804 -0
  74. data/test/test_conversion/xml_bdd.rb +203 -0
  75. data/test/test_i2/address.rb +88 -0
  76. data/test/test_i2/date.rb +49 -0
  77. data/test/test_i2/document.rb +62 -0
  78. data/test/test_i2/header.rb +39 -0
  79. data/test/test_i2/order.rb +94 -0
  80. data/test/test_i2/parser.rb +309 -0
  81. data/test/test_i2/position.rb +65 -0
  82. data/test/test_integration/geh_i2.rb +312 -0
  83. data/test/test_integration/i2_geh.rb +449 -0
  84. data/test/test_integration/i2_xml.rb +162 -0
  85. data/test/test_integration/xml_i2.rb +254 -0
  86. data/test/test_model/address.rb +35 -0
  87. data/test/test_model/agreement.rb +22 -0
  88. data/test/test_model/bdd.rb +55 -0
  89. data/test/test_model/bsr.rb +38 -0
  90. data/test/test_model/delivery.rb +79 -0
  91. data/test/test_model/delivery_item.rb +52 -0
  92. data/test/test_model/freetext_container.rb +45 -0
  93. data/test/test_model/invoice.rb +65 -0
  94. data/test/test_model/invoice_item.rb +41 -0
  95. data/test/test_model/name.rb +57 -0
  96. data/test/test_model/part_info.rb +24 -0
  97. data/test/test_model/party.rb +96 -0
  98. data/test/test_model/price.rb +24 -0
  99. data/test/test_util/application.rb +168 -0
  100. data/test/test_util/destination.rb +197 -0
  101. data/test/test_util/invoicer.rb +36 -0
  102. data/test/test_util/polling_manager.rb +111 -0
  103. data/test/test_util/transaction.rb +129 -0
  104. data/user-stories/UserStories_XmlConv2.txt +381 -0
  105. data/user-stories/doku/ABB.incoming.HTTP.invoice.xml +315 -0
  106. data/user-stories/doku/ABB.received.xml +259 -0
  107. data/user-stories/doku/BO_OrderProcessing.sxw +0 -0
  108. metadata +195 -0
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env ruby
2
+ # TestDestination -- xmlconv2 -- 08.06.2004 -- hwyss@ywesee.com
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $: << File.expand_path('..', File.dirname(__FILE__))
6
+ $: << File.expand_path('../../src', File.dirname(__FILE__))
7
+
8
+ require 'test/unit'
9
+ require 'util/destination'
10
+ require 'mock'
11
+
12
+ module XmlConv
13
+ module Util
14
+ class DestinationHttp < Destination
15
+ HTTP_CLASS = Mock.new('DestinationHttp::HTTP_CLASS')
16
+ end
17
+ class TestDestination < Test::Unit::TestCase
18
+ def setup
19
+ @destination = Destination.new
20
+ end
21
+ def test_attr_accessors
22
+ assert_respond_to(@destination, :path)
23
+ assert_respond_to(@destination, :path=)
24
+ end
25
+ def test_attr_readers
26
+ assert_respond_to(@destination, :uri)
27
+ assert_respond_to(@destination, :status)
28
+ assert_respond_to(@destination, :update_status)
29
+ end
30
+ def test_deliver
31
+ delivery = Mock.new('Delivery')
32
+ assert_raises(RuntimeError) {
33
+ @destination.deliver(delivery)
34
+ }
35
+ end
36
+ def test_forget_credentials
37
+ assert_respond_to(@destination, :forget_credentials!)
38
+ end
39
+ end
40
+ class TestDestinationDir < Test::Unit::TestCase
41
+ def setup
42
+ @destination = DestinationDir.new
43
+ @target_dir = File.expand_path('data/destination',
44
+ File.dirname(__FILE__))
45
+ end
46
+ def teardown
47
+ if(File.exist?(@target_dir))
48
+ FileUtils.rm_r(@target_dir)
49
+ end
50
+ end
51
+ def test_attr_readers
52
+ assert_respond_to(@destination, :filename)
53
+ end
54
+ def test_deliver
55
+ storage = Mock.new('Storage')
56
+ storage.__next(:transaction) { |block|
57
+ block.call
58
+ }
59
+ ODBA.storage = storage
60
+ cache = Mock.new('Cache')
61
+ ODBA.cache = cache
62
+ cache.__next(:store) { |obj|
63
+ assert_equal(@destination, obj)
64
+ }
65
+ delivery = Mock.new('Delivery')
66
+ delivery.__next(:filename) { 'test_file.dat' }
67
+ if(File.exist?(@target_dir))
68
+ FileUtils.rm_r(@target_dir)
69
+ end
70
+ @destination.path = @target_dir
71
+ @destination.deliver(delivery)
72
+ target_file = File.expand_path('test_file.dat', @target_dir)
73
+ assert(File.exist?(@target_dir), "Target Directory was not created")
74
+ assert(File.exist?(target_file), "Target File was not written")
75
+ assert_equal(delivery.to_s, File.read(target_file))
76
+ assert_equal('test_file.dat', @destination.filename)
77
+ assert_equal(:pending_pickup, @destination.status)
78
+ cache.__verify
79
+ delivery.__verify
80
+ ensure
81
+ ODBA.storage = nil
82
+ ODBA.cache = nil
83
+ end
84
+ def test_uri
85
+ @destination.path = '/foo/bar/baz'
86
+ assert_instance_of(URI::Generic, @destination.uri)
87
+ assert_equal("file:/foo/bar/baz", @destination.uri.to_s)
88
+ @destination.instance_variable_set('@filename', 'test_file.dat')
89
+ assert_equal("file:/foo/bar/baz/test_file.dat", @destination.uri.to_s)
90
+ end
91
+ def test_status
92
+ storage = Mock.new('Storage')
93
+ storage.__next(:transaction) { |block|
94
+ block.call
95
+ }
96
+ ODBA.storage = storage
97
+ cache = Mock.new('Cache')
98
+ ODBA.cache = cache
99
+ cache.__next(:store) { |obj|
100
+ assert_equal(@destination, obj)
101
+ }
102
+ assert_equal(:open, @destination.status)
103
+ @destination.update_status
104
+ assert_equal(:open, @destination.status)
105
+ @destination.instance_variable_set('@status', :pending_pickup)
106
+ @destination.instance_variable_set('@filename', 'no_such.file')
107
+ assert_equal(:pending_pickup, @destination.status)
108
+ @destination.update_status
109
+ assert_equal(:picked_up, @destination.status)
110
+ @destination.update_status
111
+ assert_equal(:picked_up, @destination.status)
112
+ cache.__verify
113
+ ensure
114
+ ODBA.storage = nil
115
+ ODBA.cache = nil
116
+ end
117
+ def test_status_comparable
118
+ assert_equal(0, @destination.status_comparable)
119
+ @destination.instance_variable_set('@status', :pending_pickup)
120
+ assert_equal(10, @destination.status_comparable)
121
+ @destination.instance_variable_set('@status', :picked_up)
122
+ assert_equal(20, @destination.status_comparable)
123
+ end
124
+ end
125
+ class TestDestinationHttp < Test::Unit::TestCase
126
+ class ToSMock < Mock
127
+ def to_s
128
+ true
129
+ end
130
+ end
131
+ def setup
132
+ @destination = DestinationHttp.new
133
+ end
134
+ def test_attr_accessors
135
+ assert_respond_to(@destination, :path)
136
+ assert_respond_to(@destination, :path=)
137
+ assert_respond_to(@destination, :uri)
138
+ assert_respond_to(@destination, :uri=)
139
+ assert_respond_to(@destination, :host)
140
+ assert_respond_to(@destination, :host=)
141
+ end
142
+ def test_path_writer
143
+ assert_equal('http:/', @destination.uri.to_s)
144
+ @destination.path = '/foo/bar'
145
+ assert_equal('http:/foo/bar', @destination.uri.to_s)
146
+ end
147
+ def test_host_writer
148
+ assert_equal('http:/', @destination.uri.to_s)
149
+ @destination.host = 'www.example.org'
150
+ assert_equal('http://www.example.org/', @destination.uri.to_s)
151
+ end
152
+ def test_uri_writer
153
+ uri = URI.parse('http://www.example.org/foo/bar')
154
+ assert_instance_of(URI::HTTP, @destination.uri)
155
+ assert_equal('http:/', @destination.uri.to_s)
156
+ @destination.uri = uri
157
+ assert_instance_of(URI::HTTP, @destination.uri)
158
+ assert_equal('http://www.example.org/foo/bar', @destination.uri.to_s)
159
+ @destination.uri = 'http://www.example.com/foo/bar'
160
+ assert_instance_of(URI::HTTP, @destination.uri)
161
+ assert_equal('http://www.example.com/foo/bar', @destination.uri.to_s)
162
+ end
163
+ def test_deliver
164
+ @destination.uri = 'http://testaccount:password@janico.ywesee.com:12345/test.rbx'
165
+ http_session = Mock.new('HttpSession')
166
+ delivery = ToSMock.new('Delivery')
167
+ response = Mock.new('Response')
168
+ response.__next(:message) { 'Status' }
169
+ delivery.__next(:to_s) { 'The Delivery' }
170
+ http_session.__next(:request) { |post_request, body|
171
+ assert_instance_of(Net::HTTP::Post, post_request)
172
+ header = post_request.instance_variable_get('@header')
173
+ assert_equal(['text/xml'], header['content-type'])
174
+ assert(header.include?('authorization'), "Authorization-Headers not sent")
175
+ assert_equal('The Delivery', body)
176
+ response
177
+ }
178
+ DestinationHttp::HTTP_CLASS.__next(:start) { |block, host, port|
179
+ assert_equal('janico.ywesee.com', host)
180
+ assert_equal(12345, port)
181
+ block.call(http_session)
182
+ }
183
+ @destination.deliver(delivery)
184
+ assert_equal(:http_status, @destination.status)
185
+ # When the delivery is delivered, forget username and Password
186
+ uri = @destination.uri
187
+ assert_nil(uri.user)
188
+ assert_nil(uri.password)
189
+ assert_equal('http://janico.ywesee.com:12345/test.rbx', uri.to_s)
190
+ DestinationHttp::HTTP_CLASS.__verify
191
+ http_session.__verify
192
+ delivery.__verify
193
+ response.__verify
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ # Util::TestInvoicer -- xmlconv2 -- 03.08.2006 -- hwyss@ywesee.com
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $: << File.expand_path('../../src', File.dirname(__FILE__))
6
+
7
+ require 'test/unit'
8
+ require 'util/invoicer'
9
+ require 'flexmock'
10
+
11
+ module XmlConv
12
+ module Util
13
+ class TestInvoicer < Test::Unit::TestCase
14
+ def setup
15
+ @invoicer = Invoicer.new
16
+ end
17
+ def test_group_by_partner
18
+ trans1 = FlexMock.new
19
+ trans1.mock_handle(:partner) { 'Group1' }
20
+ trans1.mock_handle(:model) { 'Model1' }
21
+ trans2 = FlexMock.new
22
+ trans2.mock_handle(:partner) { 'Group2' }
23
+ trans2.mock_handle(:model) { 'Model2' }
24
+ trans3 = FlexMock.new
25
+ trans3.mock_handle(:partner) { 'Group1' }
26
+ trans3.mock_handle(:model) { 'Model3' }
27
+ transactions = [trans1, trans2, trans3]
28
+ expected = {
29
+ 'Group1' => ['Model1', 'Model3'],
30
+ 'Group2' => ['Model2']
31
+ }
32
+ assert_equal(expected, @invoicer.group_by_partner(transactions))
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env ruby
2
+ # Util::TestPollingManager -- xmlconv2 -- 29.06.2004 -- hwyss@ywesee.com
3
+
4
+ $: << File.expand_path('..', File.dirname(__FILE__))
5
+ $: << File.expand_path('../../src', File.dirname(__FILE__))
6
+
7
+ require 'test/unit'
8
+ require 'util/polling_manager'
9
+ require 'mock'
10
+
11
+ module XmlConv
12
+ module Util
13
+ class PollingManager
14
+ CONFIG_PATH = File.expand_path('data/polling.yaml',
15
+ File.dirname(__FILE__))
16
+ end
17
+ class TestPollingManager < Test::Unit::TestCase
18
+ def setup
19
+ @sys = Mock.new('System')
20
+ @polling = PollingManager.new(@sys)
21
+ @dir = File.expand_path('data/i2',
22
+ File.dirname(__FILE__))
23
+ FileUtils.mkdir_p(@dir)
24
+ @file1 = File.expand_path('file1.txt', @dir)
25
+ File.open(@file1, 'w') { |fh| fh << "File 1\n" }
26
+ end
27
+ def teardown
28
+ FileUtils.rm_rf(@dir)
29
+ @sys.__verify
30
+ end
31
+ def test_load_sources
32
+ File.open(PollingManager::CONFIG_PATH, 'w') { |fh|
33
+ fh << <<-EOS
34
+ --- !ruby/object:XmlConv::Util::PollingMission
35
+ reader: I2Bdd
36
+ directory: "./test/test_util/data/i2"
37
+ writer: BddXml
38
+ destination: "http://example.com:12345"
39
+ --- !ruby/object:XmlConv::Util::PollingMission
40
+ directory: "./test/test_util/data/xml"
41
+ destination: "http://user:pass@foo.bar.baz"
42
+ writer: BddI2
43
+ reader: XmlBdd
44
+ EOS
45
+ }
46
+ block = nil
47
+ block3 = Proc.new { |source|
48
+ flunk "too many sources"
49
+ }
50
+ block2 = Proc.new { |source|
51
+ assert_instance_of(PollingMission, source)
52
+ assert_equal('XmlBdd', source.reader)
53
+ expected = File.expand_path('data/xml', File.dirname(__FILE__))
54
+ assert_equal(expected, source.directory)
55
+ assert_equal('BddI2', source.writer)
56
+ assert_equal('http://user:pass@foo.bar.baz', source.destination)
57
+ block = block3
58
+ }
59
+ block1 = Proc.new { |source|
60
+ assert_instance_of(PollingMission, source)
61
+ assert_equal('I2Bdd', source.reader)
62
+ expected = File.expand_path('data/i2', File.dirname(__FILE__))
63
+ assert_equal(expected, source.directory)
64
+ assert_equal('BddXml', source.writer)
65
+ assert_equal('http://example.com:12345', source.destination)
66
+ block = block2
67
+ }
68
+ block = block1
69
+ @polling.load_sources { |source|
70
+ block.call(source)
71
+ }
72
+ end
73
+ def test_file_paths
74
+ file2 = File.expand_path('file2.txt', @dir)
75
+ File.open(file2, 'w') { |fh| fh << "File 2\n" }
76
+ assert_equal([@file1, file2], @polling.file_paths(@dir).sort)
77
+ end
78
+ def test_poll
79
+ backup_dir = File.expand_path('../data', File.dirname(__FILE__))
80
+ source = Mock.new('Source')
81
+ source.__next(:directory) { @dir }
82
+ source.__next(:glob_pattern) { '*' }
83
+ source.__next(:reader) { 'Reader' }
84
+ source.__next(:writer) { 'Writer' }
85
+ source.__next(:destination) { 'http://foo.bar.baz:2345' }
86
+ source.__next(:debug_recipients) { }
87
+ source.__next(:error_recipients) { }
88
+ source.__next(:backup_dir) { backup_dir }
89
+ @sys.__next(:execute) { |transaction|
90
+ assert_instance_of(Util::Transaction, transaction)
91
+ assert_equal("File 1\n", transaction.input)
92
+ assert_equal('file:' << @file1, transaction.origin)
93
+ assert_equal('Reader', transaction.reader)
94
+ assert_equal('Writer', transaction.writer)
95
+ dest = transaction.destination
96
+ assert_instance_of(Util::DestinationHttp, dest)
97
+ assert_equal('http://foo.bar.baz:2345', dest.uri.to_s)
98
+ }
99
+ @polling.poll(source)
100
+ source.__verify
101
+ end
102
+ def test_destination
103
+ dest = @polling.destination('http://foo:bar@baz.com')
104
+ assert_instance_of(Util::DestinationHttp, dest)
105
+ assert_equal('baz.com', dest.host)
106
+ assert_instance_of(URI::HTTP, dest.uri)
107
+ assert_equal('http://foo:bar@baz.com', dest.uri.to_s)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+ # Util::TestTransaction -- xmlconv2 -- 04.06.2004 -- hwyss@ywesee.com
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $: << File.expand_path('../../src', File.dirname(__FILE__))
6
+
7
+ require 'test/unit'
8
+ require 'util/transaction'
9
+ require 'mock'
10
+
11
+ module XmlConv
12
+ module Conversion
13
+ def const_get(symbol)
14
+ if(symbol.is_a?(Mock))
15
+ symbol
16
+ else
17
+ super
18
+ end
19
+ end
20
+ module_function :const_get
21
+ end
22
+ module Util
23
+ class Transaction
24
+ SMTP_HANDLER = Mock.new('SMTP-Handler')
25
+ end
26
+ class TestTransaction < Test::Unit::TestCase
27
+ def setup
28
+ @transaction = Util::Transaction.new
29
+ end
30
+ def test_attr_accessors
31
+ assert_respond_to(@transaction, :input)
32
+ assert_respond_to(@transaction, :input=)
33
+ assert_respond_to(@transaction, :reader)
34
+ assert_respond_to(@transaction, :reader=)
35
+ assert_respond_to(@transaction, :writer)
36
+ assert_respond_to(@transaction, :writer=)
37
+ assert_respond_to(@transaction, :transaction_id)
38
+ assert_respond_to(@transaction, :transaction_id=)
39
+ assert_respond_to(@transaction, :error)
40
+ assert_respond_to(@transaction, :error=)
41
+ end
42
+ def test_attr_readers
43
+ assert_respond_to(@transaction, :output)
44
+ assert_respond_to(@transaction, :model)
45
+ assert_respond_to(@transaction, :start_time)
46
+ assert_respond_to(@transaction, :commit_time)
47
+ end
48
+ def test_execute
49
+ src = Mock.new('source')
50
+ input = Mock.new('input')
51
+ reader = Mock.new('reader')
52
+ model = Mock.new('model')
53
+ writer = Mock.new('writer')
54
+ output = Mock.new('output')
55
+ destination = Mock.new('destination')
56
+ @transaction.input = src
57
+ @transaction.reader = reader
58
+ @transaction.writer = writer
59
+ @transaction.destination = destination
60
+ reader.__next(:parse) { |read_src|
61
+ assert_equal(src, read_src)
62
+ input
63
+ }
64
+ reader.__next(:convert) { |read_input|
65
+ assert_equal(input, read_input)
66
+ model
67
+ }
68
+ writer.__next(:convert) { |write_input|
69
+ assert_equal(model, write_input)
70
+ output
71
+ }
72
+ destination.__next(:deliver) { |delivery|
73
+ assert_equal(output, delivery)
74
+ }
75
+ destination.__next(:forget_credentials!) { }
76
+ time1 = Time.now
77
+ result = @transaction.execute
78
+ time2 = Time.now
79
+ assert_equal(src, @transaction.input)
80
+ assert_equal(reader, @transaction.reader)
81
+ assert_equal(model, @transaction.model)
82
+ assert_equal(writer, @transaction.writer)
83
+ assert_equal(output.to_s, @transaction.output)
84
+ assert_equal(output.to_s, result)
85
+ assert_in_delta(time1, @transaction.start_time, 0.001)
86
+ assert_in_delta(time2, @transaction.commit_time, 0.001)
87
+ input.__verify
88
+ reader.__verify
89
+ model.__verify
90
+ writer.__verify
91
+ output.__verify
92
+ destination.__verify
93
+ end
94
+ def test_persistable
95
+ assert_kind_of(ODBA::Persistable, @transaction)
96
+ end
97
+ def test_dumpable
98
+ assert_nothing_raised { Marshal.dump(@transaction) }
99
+ end
100
+ def test_notify
101
+ @transaction.instance_variable_set('@start_time', Time.now)
102
+ @transaction.error_recipients = ['bar']
103
+ smtp = Transaction::SMTP_HANDLER
104
+ @transaction.notify
105
+ @transaction.debug_recipients = ['foo']
106
+ mail = Mock.new('MailSession')
107
+ mail.__next(:sendmail) { |encoded, from, recipients|
108
+ assert_equal(['foo'], recipients)
109
+
110
+ }
111
+ smtp.__next(:start) { |block, server|
112
+ block.call(mail)
113
+ }
114
+ @transaction.notify
115
+ @transaction.error = 'error!'
116
+ mail.__next(:sendmail) { |encoded, from, recipients|
117
+ assert_equal(['foo', 'bar'], recipients)
118
+
119
+ }
120
+ smtp.__next(:start) { |block, server|
121
+ block.call(mail)
122
+ }
123
+ @transaction.notify
124
+ smtp.__verify
125
+ mail.__verify
126
+ end
127
+ end
128
+ end
129
+ end