wamp-worker 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +204 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/bin/wamp-worker +46 -0
  13. data/lib/wamp/worker.rb +132 -0
  14. data/lib/wamp/worker/config.rb +184 -0
  15. data/lib/wamp/worker/handler.rb +196 -0
  16. data/lib/wamp/worker/proxy/backgrounder.rb +38 -0
  17. data/lib/wamp/worker/proxy/base.rb +101 -0
  18. data/lib/wamp/worker/proxy/dispatcher.rb +115 -0
  19. data/lib/wamp/worker/proxy/requestor.rb +91 -0
  20. data/lib/wamp/worker/queue.rb +135 -0
  21. data/lib/wamp/worker/rails.rb +28 -0
  22. data/lib/wamp/worker/runner.rb +240 -0
  23. data/lib/wamp/worker/ticker.rb +30 -0
  24. data/lib/wamp/worker/version.rb +5 -0
  25. data/spec/spec_helper.rb +29 -0
  26. data/spec/support/client_stub.rb +47 -0
  27. data/spec/support/handler_stub.rb +105 -0
  28. data/spec/support/redis_stub.rb +89 -0
  29. data/spec/support/session_stub.rb +101 -0
  30. data/spec/wamp/worker/config_spec.rb +90 -0
  31. data/spec/wamp/worker/handler_spec.rb +162 -0
  32. data/spec/wamp/worker/proxy_spec.rb +153 -0
  33. data/spec/wamp/worker/queue_spec.rb +49 -0
  34. data/spec/wamp/worker/runner_spec.rb +108 -0
  35. data/spec/wamp/worker_spec.rb +8 -0
  36. data/test/app_test.rb +124 -0
  37. data/test/hello.py +50 -0
  38. data/test/sidekiq.yml +5 -0
  39. data/test/wamp_test/.generators +8 -0
  40. data/test/wamp_test/.ruby-version +1 -0
  41. data/test/wamp_test/Gemfile +65 -0
  42. data/test/wamp_test/Gemfile.lock +246 -0
  43. data/test/wamp_test/README.md +24 -0
  44. data/test/wamp_test/Rakefile +6 -0
  45. data/test/wamp_test/app/assets/config/manifest.js +3 -0
  46. data/test/wamp_test/app/assets/images/.keep +0 -0
  47. data/test/wamp_test/app/assets/javascripts/application.js +16 -0
  48. data/test/wamp_test/app/assets/javascripts/cable.js +13 -0
  49. data/test/wamp_test/app/assets/javascripts/channels/.keep +0 -0
  50. data/test/wamp_test/app/assets/stylesheets/application.css +15 -0
  51. data/test/wamp_test/app/channels/application_cable/channel.rb +4 -0
  52. data/test/wamp_test/app/channels/application_cable/connection.rb +4 -0
  53. data/test/wamp_test/app/controllers/add_controller.rb +11 -0
  54. data/test/wamp_test/app/controllers/application_controller.rb +3 -0
  55. data/test/wamp_test/app/controllers/concerns/.keep +0 -0
  56. data/test/wamp_test/app/controllers/ping_controller.rb +7 -0
  57. data/test/wamp_test/app/handlers/add_handler.rb +9 -0
  58. data/test/wamp_test/app/handlers/back_add_handler.rb +26 -0
  59. data/test/wamp_test/app/handlers/back_ping_handler.rb +10 -0
  60. data/test/wamp_test/app/handlers/ping_handler.rb +10 -0
  61. data/test/wamp_test/app/helpers/application_helper.rb +2 -0
  62. data/test/wamp_test/app/jobs/application_job.rb +2 -0
  63. data/test/wamp_test/app/mailers/application_mailer.rb +4 -0
  64. data/test/wamp_test/app/models/application_record.rb +3 -0
  65. data/test/wamp_test/app/models/concerns/.keep +0 -0
  66. data/test/wamp_test/app/views/layouts/application.html.erb +15 -0
  67. data/test/wamp_test/app/views/layouts/mailer.html.erb +13 -0
  68. data/test/wamp_test/app/views/layouts/mailer.text.erb +1 -0
  69. data/test/wamp_test/bin/bundle +3 -0
  70. data/test/wamp_test/bin/rails +9 -0
  71. data/test/wamp_test/bin/rake +9 -0
  72. data/test/wamp_test/bin/setup +36 -0
  73. data/test/wamp_test/bin/spring +17 -0
  74. data/test/wamp_test/bin/update +31 -0
  75. data/test/wamp_test/bin/yarn +11 -0
  76. data/test/wamp_test/config.ru +5 -0
  77. data/test/wamp_test/config/application.rb +19 -0
  78. data/test/wamp_test/config/boot.rb +4 -0
  79. data/test/wamp_test/config/cable.yml +10 -0
  80. data/test/wamp_test/config/credentials.yml.enc +1 -0
  81. data/test/wamp_test/config/database.yml +25 -0
  82. data/test/wamp_test/config/environment.rb +5 -0
  83. data/test/wamp_test/config/environments/development.rb +61 -0
  84. data/test/wamp_test/config/environments/production.rb +94 -0
  85. data/test/wamp_test/config/environments/test.rb +46 -0
  86. data/test/wamp_test/config/initializers/application_controller_renderer.rb +8 -0
  87. data/test/wamp_test/config/initializers/assets.rb +14 -0
  88. data/test/wamp_test/config/initializers/backtrace_silencers.rb +7 -0
  89. data/test/wamp_test/config/initializers/content_security_policy.rb +25 -0
  90. data/test/wamp_test/config/initializers/cookies_serializer.rb +5 -0
  91. data/test/wamp_test/config/initializers/filter_parameter_logging.rb +4 -0
  92. data/test/wamp_test/config/initializers/inflections.rb +16 -0
  93. data/test/wamp_test/config/initializers/mime_types.rb +4 -0
  94. data/test/wamp_test/config/initializers/wamp-worker.rb +8 -0
  95. data/test/wamp_test/config/initializers/wrap_parameters.rb +14 -0
  96. data/test/wamp_test/config/locales/en.yml +33 -0
  97. data/test/wamp_test/config/master.key +1 -0
  98. data/test/wamp_test/config/puma.rb +34 -0
  99. data/test/wamp_test/config/routes.rb +4 -0
  100. data/test/wamp_test/config/sidekiq.yml +6 -0
  101. data/test/wamp_test/config/spring.rb +6 -0
  102. data/test/wamp_test/config/storage.yml +34 -0
  103. data/test/wamp_test/db/development.sqlite3 +0 -0
  104. data/test/wamp_test/db/seeds.rb +7 -0
  105. data/test/wamp_test/lib/assets/.keep +0 -0
  106. data/test/wamp_test/lib/tasks/.keep +0 -0
  107. data/test/wamp_test/package.json +5 -0
  108. data/test/wamp_test/public/404.html +67 -0
  109. data/test/wamp_test/public/422.html +67 -0
  110. data/test/wamp_test/public/500.html +66 -0
  111. data/test/wamp_test/public/apple-touch-icon-precomposed.png +0 -0
  112. data/test/wamp_test/public/apple-touch-icon.png +0 -0
  113. data/test/wamp_test/public/favicon.ico +0 -0
  114. data/test/wamp_test/public/robots.txt +1 -0
  115. data/test/wamp_test/storage/.keep +0 -0
  116. data/test/wamp_test/test/application_system_test_case.rb +5 -0
  117. data/test/wamp_test/test/controllers/.keep +0 -0
  118. data/test/wamp_test/test/fixtures/.keep +0 -0
  119. data/test/wamp_test/test/fixtures/files/.keep +0 -0
  120. data/test/wamp_test/test/helpers/.keep +0 -0
  121. data/test/wamp_test/test/integration/.keep +0 -0
  122. data/test/wamp_test/test/mailers/.keep +0 -0
  123. data/test/wamp_test/test/models/.keep +0 -0
  124. data/test/wamp_test/test/system/.keep +0 -0
  125. data/test/wamp_test/test/test_helper.rb +10 -0
  126. data/test/wamp_test/vendor/.keep +0 -0
  127. data/test/web/index.html +101 -0
  128. data/wamp-worker.gemspec +32 -0
  129. metadata +395 -0
@@ -0,0 +1,162 @@
1
+ require "spec_helper"
2
+
3
+ describe Wamp::Worker::Handler do
4
+ let(:name) { :default }
5
+ let(:session) { SessionStub.new }
6
+ let(:proxy) { Wamp::Worker::Proxy::Dispatcher.new(name, session) }
7
+
8
+ it "registers the handlers" do
9
+ config = Wamp::Worker.config
10
+
11
+ expect(config.subscriptions.count).to eq(2)
12
+ expect(config.subscriptions(name).count).to eq(2)
13
+ expect(config.subscriptions(:other).count).to eq(2)
14
+
15
+ expect(config.registrations.count).to eq(16)
16
+ expect(config.registrations(name).count).to eq(16)
17
+ expect(config.registrations(:other).count).to eq(2)
18
+ end
19
+
20
+ def setup_handlers
21
+ Wamp::Worker.subscribe_topics(name, proxy, session)
22
+ Wamp::Worker.register_procedures(name, proxy, session)
23
+ end
24
+
25
+ it "executes the normal handlers" do
26
+ setup_handlers
27
+
28
+ expect{
29
+ session.publish("topic", nil, nil)
30
+
31
+ session.call("return_error", nil, nil) do |result, error, details|
32
+ expect(error[:error]).to eq("error")
33
+ end
34
+
35
+ session.call("throw_error", nil, nil) do |result, error, details|
36
+ expect(error[:error]).to eq("error")
37
+ end
38
+
39
+ session.call("throw_exception", nil, nil) do |result, error, details|
40
+ expect(error[:error]).to eq("wamp.error.runtime")
41
+ expect(error[:args][0]).to eq("error")
42
+ end
43
+
44
+ session.call("call_result", [3], nil) do |result, error, details|
45
+ expect(result[:args][0]).to eq(5)
46
+ end
47
+
48
+ session.call("normal_result", [3], nil) do |result, error, details|
49
+ expect(result[:args][0]).to eq(6)
50
+ end
51
+
52
+ session.call("nil_result", [3], nil) do |result, error, details|
53
+ expect(result[:args][0]).to eq(nil)
54
+ end
55
+
56
+ session.call("proxy_result", [3], nil) do |result, error, details|
57
+ expect(result[:args][0]).to eq(6)
58
+ end
59
+ }.to change{ NormalHandler.run_count }.by(9)
60
+
61
+ end
62
+
63
+ it "executes the background handlers" do
64
+ setup_handlers
65
+
66
+ queue_count = 0
67
+ queue_params = nil
68
+
69
+ allow_any_instance_of(Wamp::Worker::Queue).to receive(:push) do |queue, queue_name, command, params, handle|
70
+ queue_params = params[:result]
71
+ queue_count += 1
72
+ expect(command).to eq(:yield)
73
+ end
74
+
75
+ expect{
76
+ session.publish("back.topic", nil, nil)
77
+
78
+ session.call("back.return_error", nil, nil)
79
+ expect(queue_params[:error]).to eq("error")
80
+
81
+ session.call("back.throw_error", nil, nil)
82
+ expect(queue_params[:error]).to eq("error")
83
+
84
+ session.call("back.throw_exception", nil, nil)
85
+ expect(queue_params[:error]).to eq("wamp.error.runtime")
86
+
87
+ session.call("back.call_result", [3], nil)
88
+ expect(queue_params[:args][0]).to eq(5)
89
+
90
+ session.call("back.normal_result", [3], nil)
91
+ expect(queue_params[:args][0]).to eq(6)
92
+
93
+ session.call("back.nil_result", [3], nil)
94
+ expect(queue_params[:args][0]).to eq(nil)
95
+ }.to change{ BackgroundHandler.run_count }.by(7)
96
+
97
+ expect(queue_count).to eq(6)
98
+ end
99
+
100
+ context "progress" do
101
+ before(:each) {
102
+ setup_handlers
103
+ }
104
+
105
+ it "reports the progress in the foreground" do
106
+ count = 0
107
+
108
+ session.call("progress_result", [3], nil, { receive_progress: true }) do |result, error, details|
109
+ case count
110
+ when 0
111
+ expect(result[:args][0]).to eq(0)
112
+ expect(details[:progress]).to eq(true)
113
+ when 1
114
+ expect(result[:args][0]).to eq(0.5)
115
+ expect(details[:progress]).to eq(true)
116
+ when 2
117
+ expect(result[:args][0]).to eq(1.0)
118
+ expect(details[:progress]).to eq(true)
119
+ else
120
+ expect(result[:args][0]).to eq(7)
121
+ expect(details[:progress]).not_to eq(true)
122
+ end
123
+
124
+ count += 1
125
+ end
126
+
127
+ expect(count).to eq(4)
128
+ end
129
+
130
+ it "reports the progress in the background" do
131
+ count = 0
132
+
133
+ allow_any_instance_of(Wamp::Worker::Queue).to receive(:push) do |queue, queue_name, command, params, handle|
134
+ result = params[:result]
135
+ options = params[:options]
136
+
137
+ case count
138
+ when 0
139
+ expect(result[:args][0]).to eq(0)
140
+ expect(options[:progress]).to eq(true)
141
+ when 1
142
+ expect(result[:args][0]).to eq(0.5)
143
+ expect(options[:progress]).to eq(true)
144
+ when 2
145
+ expect(result[:args][0]).to eq(1.0)
146
+ expect(options[:progress]).to eq(true)
147
+ else
148
+ expect(result[:args][0]).to eq(7)
149
+ expect(options[:progress]).not_to eq(true)
150
+ end
151
+
152
+ count += 1
153
+ end
154
+
155
+ session.call("back.progress_result", [3], nil, { receive_progress: true })
156
+
157
+ expect(count).to eq(4)
158
+ end
159
+ end
160
+
161
+ end
162
+
@@ -0,0 +1,153 @@
1
+ require "spec_helper"
2
+ require "thread"
3
+
4
+ class RequestorClass
5
+ include Wamp::Worker::Session.new(method: :temp_session)
6
+ end
7
+
8
+ describe Wamp::Worker::Proxy do
9
+ let(:name) { :default }
10
+ let(:topic) { "topic" }
11
+ let(:procedure) { "procedure" }
12
+ let(:session) { SessionStub.new }
13
+ let(:dispatcher) { described_class::Dispatcher.new(name, session) }
14
+ let(:requestor) { RequestorClass.new.temp_session }
15
+
16
+ before(:each) {
17
+ Wamp::Worker.subscribe_topics(name, dispatcher, session)
18
+ Wamp::Worker.register_procedures(name, dispatcher, session)
19
+ }
20
+
21
+ def check_method(times, method, *args)
22
+ check_result = nil
23
+ check_error = nil
24
+
25
+ # Create a thread to run the dispatcher
26
+ thread = Thread.new do
27
+ times.times do
28
+ descriptor = dispatcher.check_queues
29
+ dispatcher.process(descriptor)
30
+ end
31
+ end
32
+
33
+ requestor.send(method, *args) do |result, error, details|
34
+ check_result = result
35
+ check_error = error
36
+ end
37
+
38
+ # Wait for the thread to complete
39
+ thread.join
40
+
41
+ yield(check_result, check_error)
42
+ end
43
+
44
+ context "command/response flow" do
45
+
46
+ it "executes a call command" do
47
+ expect {
48
+ check_method(1, :call, "normal_result", [7]) do |result, error|
49
+ expect(result[:args][0]).to eq(10)
50
+ expect(error).to be_nil
51
+ end
52
+ }.to change{ NormalHandler.run_count }.by(1)
53
+ end
54
+
55
+ it "executes a call command with invalid procedure" do
56
+ expect {
57
+ check_method(1, :call, "invalid_procedure", [7]) do |result, error|
58
+ expect(result).to be_nil
59
+ expect(error).to eq({ error: "wamp.no_procedure", args:[], kwargs:{} })
60
+ end
61
+ }.to change{ NormalHandler.run_count }.by(0)
62
+ end
63
+
64
+ it "executes a call command with throw error" do
65
+ expect {
66
+ check_method(1, :call, "throw_error", [7]) do |result, error|
67
+ expect(result).to be_nil
68
+ expect(error).to eq({ error: "error", args:[], kwargs:{} })
69
+ end
70
+ }.to change{ NormalHandler.run_count }.by(1)
71
+ end
72
+
73
+ it "executes a publish command w/o acknowledge" do
74
+ expect {
75
+ check_method(1, :publish, "topic", [7]) do |result, error|
76
+ expect(result).to be_nil
77
+ expect(error).to be_nil
78
+ end
79
+ }.to change{ NormalHandler.run_count }.by(1)
80
+ end
81
+
82
+ it "executes a publish command w/ acknowledge" do
83
+ expect {
84
+ check_method(1, :publish, "topic", [7], {}, { acknowledge: true }) do |result, error|
85
+ expect(result).to eq(1234)
86
+ expect(error).to be_nil
87
+ end
88
+ }.to change{ NormalHandler.run_count }.by(1)
89
+ end
90
+
91
+ it "executes a publish command w/ acknowledge and error" do
92
+ expect {
93
+ check_method(1, :publish, "invalid_topic", [7], {}, { acknowledge: true }) do |result, error|
94
+ expect(result).to eq(1234)
95
+ expect(error).to eq({ error: "wamp.no_subscriber", args:[], kwargs:{} })
96
+ end
97
+ }.to change{ NormalHandler.run_count }.by(0)
98
+ end
99
+
100
+ it "errors on unsupported proxy command" do
101
+ requestor.queue.push requestor.command_req_queue, :bad, {}, "handle"
102
+
103
+ descriptor = dispatcher.check_queues
104
+ dispatcher.process(descriptor)
105
+
106
+ descriptor = requestor.queue.pop("handle")
107
+ expect(descriptor.command).to eq(:bad)
108
+ expect(descriptor.params[:error][:error]).to eq("wamp.error.runtime")
109
+ expect(descriptor.params[:error][:args][0]).to eq("unsupported proxy command 'bad'")
110
+ end
111
+ end
112
+
113
+ context "dispatcher/backgrounder flow" do
114
+
115
+ it "executes a 'call' command" do
116
+ expect {
117
+ check_method(2, :call, "back.normal_result", [7]) do |result, error|
118
+ expect(result[:args][0]).to eq(10)
119
+ expect(error).to be_nil
120
+ end
121
+ }.to change{ BackgroundHandler.run_count }.by(1)
122
+ end
123
+
124
+ it "executes a 'call' command with throw error" do
125
+ expect {
126
+ check_method(2, :call, "back.throw_error", [7]) do |result, error|
127
+ expect(result).to be_nil
128
+ expect(error).to eq({ error: "error", args:[], kwargs:{} })
129
+ end
130
+ }.to change{ BackgroundHandler.run_count }.by(1)
131
+ end
132
+
133
+ it "executes a 'publish' command w/o acknowledge" do
134
+ expect {
135
+ check_method(1, :publish, "back.topic", [7]) do |result, error|
136
+ expect(result).to be_nil
137
+ expect(error).to be_nil
138
+ end
139
+ }.to change{ BackgroundHandler.run_count }.by(1)
140
+ end
141
+
142
+ it "executes a 'publish' command w/ acknowledge" do
143
+ expect {
144
+ check_method(1, :publish, "back.topic", [7], {}, { acknowledge: true }) do |result, error|
145
+ expect(result).to eq(1234)
146
+ expect(error).to be_nil
147
+ end
148
+ }.to change{ BackgroundHandler.run_count }.by(1)
149
+ end
150
+ end
151
+
152
+ end
153
+
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ describe Wamp::Worker::Queue do
4
+ let(:name) { :default }
5
+ let(:queue) { described_class.new(name) }
6
+ let(:queue_name) { "test:command" }
7
+ let(:handle) { "test:response" }
8
+ let(:params) { { temp: true } }
9
+
10
+ it "general push/pop" do
11
+ # Push the request
12
+ queue.push queue_name, :call, params, handle
13
+
14
+ # Pop the request
15
+ descriptor = queue.pop(queue_name, wait: true)
16
+ expect(descriptor.command).to eq(:call)
17
+ expect(descriptor.handle).to eq(handle)
18
+ expect(descriptor.params).to eq(params)
19
+
20
+ # Check and make sure the key still exists
21
+ expect(queue.redis.exists(queue_name)).to eq(true)
22
+ end
23
+
24
+ it "deletes on pop" do
25
+ expect(queue.redis.exists(queue_name)).to eq(false)
26
+
27
+ # Push the request
28
+ queue.push queue_name, :call, params, handle
29
+
30
+ expect(queue.redis.exists(queue_name)).to eq(true)
31
+
32
+ # Pop the request
33
+ descriptor = queue.pop(queue_name, delete: true)
34
+ expect(descriptor.command).to eq(:call)
35
+ expect(descriptor.handle).to eq(handle)
36
+ expect(descriptor.params).to eq(params)
37
+
38
+ expect(queue.redis.exists(queue_name)).to eq(false)
39
+ end
40
+
41
+ it "timeout" do
42
+ # Pop the descriptor
43
+ descriptor = queue.pop(queue_name, wait: true, timeout: 1)
44
+
45
+ # It should be nil
46
+ expect(descriptor).to eq(nil)
47
+ end
48
+
49
+ end
@@ -0,0 +1,108 @@
1
+ require "spec_helper"
2
+
3
+ describe Wamp::Worker::Runner do
4
+ let(:name) { :other }
5
+ let(:runner) { described_class::Main.new(name) }
6
+ let(:requestor) { Wamp::Worker.requestor(name) }
7
+
8
+ def execute_runner
9
+
10
+ # Start the runner
11
+ runner.start
12
+
13
+ # Start it and the event machine on a different thread
14
+ thread = Thread.new do
15
+ EM.run {}
16
+ end
17
+
18
+ yield
19
+
20
+ # Stop the event machine
21
+ runner.client.transport_class.stop_event_machine
22
+ thread.join
23
+
24
+ # Stop the runner
25
+ runner.stop
26
+
27
+ end
28
+
29
+ it "registers and subscribes" do
30
+
31
+ # Check attributes
32
+ expect(runner.active?).to eq(false)
33
+ expect(runner.dispatcher.session).to be_nil
34
+
35
+ execute_runner do
36
+
37
+ # Check attributes
38
+ expect(runner.active?).to eq(true)
39
+ expect(runner.dispatcher.session).to be(runner.client.session)
40
+
41
+ # Check that some of the handlers work
42
+ expect{
43
+ requestor.call("other.call_result", [3], nil) do |result, error, details|
44
+ expect(result[:args][0]).to eq(5)
45
+ end
46
+ }.to change{ NormalHandler.run_count }.by(1)
47
+
48
+ expect{
49
+ requestor.call("back.other.call_result", [4], nil) do |result, error, details|
50
+ expect(result[:args][0]).to eq(6)
51
+ end
52
+ }.to change{ BackgroundHandler.run_count }.by(1)
53
+
54
+ end
55
+
56
+ # Check attributes
57
+ expect(runner.active?).to eq(false)
58
+ expect(runner.dispatcher.session).to be_nil
59
+ end
60
+
61
+ it "synchronizes the UUID between all of the runners" do
62
+ uuid = runner.dispatcher.uuid
63
+ expect(runner.queue_monitor.dispatcher.uuid).to eq(uuid)
64
+ end
65
+
66
+ it "increments the ticker" do
67
+ expect(runner.dispatcher.ticker.get).to eq(0)
68
+
69
+ expect {
70
+ execute_runner do
71
+ sleep(2.5)
72
+ end
73
+ }.to change{ runner.dispatcher.ticker.get }.by(3)
74
+ end
75
+
76
+ context "challenge" do
77
+ it "errors if challenge is called but no method was passed in" do
78
+ runner = described_class::Main.new :test, client: ClientStub.new({should_challenge: true})
79
+
80
+ # Errors because the callback wasn't defined
81
+ expect {
82
+ runner.start
83
+ }.to raise_error(ArgumentError)
84
+
85
+ # Ensure that the runner terminated
86
+ expect(runner.active?).to eq(false)
87
+ end
88
+
89
+ it "does not error when the challenge is defined" do
90
+ value = 0
91
+
92
+ # Create the runner passing it the challenge method
93
+ runner = described_class::Main.new :test,
94
+ challenge: -> authmethod, details {value += 1},
95
+ client: ClientStub.new({should_challenge: true})
96
+
97
+ # Start the runner
98
+ runner.start
99
+
100
+ # Expect that the challenge method was called
101
+ expect(value).to eq(1)
102
+
103
+ # Stop the runner
104
+ runner.stop
105
+ end
106
+
107
+ end
108
+ end