xip 0.0.1 → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +116 -0
  3. data/.gitignore +12 -0
  4. data/CHANGELOG.md +135 -0
  5. data/Gemfile +4 -1
  6. data/Gemfile.lock +65 -15
  7. data/LICENSE +6 -4
  8. data/README.md +51 -1
  9. data/VERSION +1 -0
  10. data/bin/xip +3 -11
  11. data/lib/xip.rb +1 -3
  12. data/lib/xip/base.rb +189 -0
  13. data/lib/xip/cli.rb +273 -0
  14. data/lib/xip/cli_base.rb +24 -0
  15. data/lib/xip/commands/command.rb +13 -0
  16. data/lib/xip/commands/console.rb +74 -0
  17. data/lib/xip/commands/server.rb +63 -0
  18. data/lib/xip/configuration.rb +56 -0
  19. data/lib/xip/controller/callbacks.rb +63 -0
  20. data/lib/xip/controller/catch_all.rb +84 -0
  21. data/lib/xip/controller/controller.rb +274 -0
  22. data/lib/xip/controller/dev_jumps.rb +40 -0
  23. data/lib/xip/controller/dynamic_delay.rb +61 -0
  24. data/lib/xip/controller/helpers.rb +128 -0
  25. data/lib/xip/controller/interrupt_detect.rb +99 -0
  26. data/lib/xip/controller/messages.rb +283 -0
  27. data/lib/xip/controller/nlp.rb +49 -0
  28. data/lib/xip/controller/replies.rb +281 -0
  29. data/lib/xip/controller/unrecognized_message.rb +61 -0
  30. data/lib/xip/core_ext.rb +5 -0
  31. data/lib/xip/core_ext/numeric.rb +10 -0
  32. data/lib/xip/core_ext/string.rb +18 -0
  33. data/lib/xip/dispatcher.rb +68 -0
  34. data/lib/xip/errors.rb +55 -0
  35. data/lib/xip/flow/base.rb +69 -0
  36. data/lib/xip/flow/specification.rb +56 -0
  37. data/lib/xip/flow/state.rb +82 -0
  38. data/lib/xip/generators/builder.rb +41 -0
  39. data/lib/xip/generators/builder/.gitignore +30 -0
  40. data/lib/xip/generators/builder/Gemfile +19 -0
  41. data/lib/xip/generators/builder/Procfile.dev +2 -0
  42. data/lib/xip/generators/builder/README.md +9 -0
  43. data/lib/xip/generators/builder/Rakefile +2 -0
  44. data/lib/xip/generators/builder/bot/controllers/bot_controller.rb +55 -0
  45. data/lib/xip/generators/builder/bot/controllers/catch_alls_controller.rb +21 -0
  46. data/lib/xip/generators/builder/bot/controllers/concerns/.keep +0 -0
  47. data/lib/xip/generators/builder/bot/controllers/goodbyes_controller.rb +9 -0
  48. data/lib/xip/generators/builder/bot/controllers/hellos_controller.rb +9 -0
  49. data/lib/xip/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
  50. data/lib/xip/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
  51. data/lib/xip/generators/builder/bot/helpers/bot_helper.rb +2 -0
  52. data/lib/xip/generators/builder/bot/models/bot_record.rb +3 -0
  53. data/lib/xip/generators/builder/bot/models/concerns/.keep +0 -0
  54. data/lib/xip/generators/builder/bot/replies/catch_alls/level1.yml +2 -0
  55. data/lib/xip/generators/builder/bot/replies/goodbyes/say_goodbye.yml +2 -0
  56. data/lib/xip/generators/builder/bot/replies/hellos/say_hello.yml +2 -0
  57. data/lib/xip/generators/builder/config.ru +4 -0
  58. data/lib/xip/generators/builder/config/boot.rb +6 -0
  59. data/lib/xip/generators/builder/config/database.yml +25 -0
  60. data/lib/xip/generators/builder/config/environment.rb +2 -0
  61. data/lib/xip/generators/builder/config/flow_map.rb +25 -0
  62. data/lib/xip/generators/builder/config/initializers/autoload.rb +8 -0
  63. data/lib/xip/generators/builder/config/initializers/inflections.rb +16 -0
  64. data/lib/xip/generators/builder/config/puma.rb +25 -0
  65. data/lib/xip/generators/builder/config/services.yml +35 -0
  66. data/lib/xip/generators/builder/config/sidekiq.yml +3 -0
  67. data/lib/xip/generators/builder/db/seeds.rb +7 -0
  68. data/lib/xip/generators/generate.rb +39 -0
  69. data/lib/xip/generators/generate/flow/controllers/controller.tt +7 -0
  70. data/lib/xip/generators/generate/flow/helpers/helper.tt +3 -0
  71. data/lib/xip/generators/generate/flow/replies/ask_example.tt +9 -0
  72. data/lib/xip/helpers/redis.rb +40 -0
  73. data/lib/xip/jobs.rb +9 -0
  74. data/lib/xip/lock.rb +82 -0
  75. data/lib/xip/logger.rb +9 -3
  76. data/lib/xip/migrations/configurator.rb +73 -0
  77. data/lib/xip/migrations/generators.rb +16 -0
  78. data/lib/xip/migrations/railtie_config.rb +14 -0
  79. data/lib/xip/migrations/tasks.rb +43 -0
  80. data/lib/xip/nlp/client.rb +21 -0
  81. data/lib/xip/nlp/result.rb +56 -0
  82. data/lib/xip/reloader.rb +89 -0
  83. data/lib/xip/reply.rb +36 -0
  84. data/lib/xip/scheduled_reply.rb +18 -0
  85. data/lib/xip/server.rb +63 -0
  86. data/lib/xip/service_message.rb +17 -0
  87. data/lib/xip/service_reply.rb +44 -0
  88. data/lib/xip/services/base_client.rb +24 -0
  89. data/lib/xip/services/base_message_handler.rb +27 -0
  90. data/lib/xip/services/base_reply_handler.rb +72 -0
  91. data/lib/xip/services/jobs/handle_message_job.rb +21 -0
  92. data/lib/xip/session.rb +203 -0
  93. data/lib/xip/version.rb +7 -1
  94. data/logo.svg +17 -0
  95. data/spec/configuration_spec.rb +93 -0
  96. data/spec/controller/callbacks_spec.rb +217 -0
  97. data/spec/controller/catch_all_spec.rb +154 -0
  98. data/spec/controller/controller_spec.rb +889 -0
  99. data/spec/controller/dynamic_delay_spec.rb +70 -0
  100. data/spec/controller/helpers_spec.rb +119 -0
  101. data/spec/controller/interrupt_detect_spec.rb +171 -0
  102. data/spec/controller/messages_spec.rb +744 -0
  103. data/spec/controller/nlp_spec.rb +93 -0
  104. data/spec/controller/replies_spec.rb +694 -0
  105. data/spec/controller/unrecognized_message_spec.rb +168 -0
  106. data/spec/dispatcher_spec.rb +79 -0
  107. data/spec/flow/flow_spec.rb +82 -0
  108. data/spec/flow/state_spec.rb +109 -0
  109. data/spec/helpers/redis_spec.rb +77 -0
  110. data/spec/lock_spec.rb +100 -0
  111. data/spec/nlp/client_spec.rb +23 -0
  112. data/spec/nlp/result_spec.rb +57 -0
  113. data/spec/replies/hello.yml.erb +15 -0
  114. data/spec/replies/messages/say_hola.yml+facebook.erb +6 -0
  115. data/spec/replies/messages/say_hola.yml+twilio.erb +6 -0
  116. data/spec/replies/messages/say_hola.yml.erb +6 -0
  117. data/spec/replies/messages/say_howdy_with_dynamic.yml +79 -0
  118. data/spec/replies/messages/say_msgs_without_breaks.yml +4 -0
  119. data/spec/replies/messages/say_offer.yml +6 -0
  120. data/spec/replies/messages/say_offer_with_dynamic.yml +6 -0
  121. data/spec/replies/messages/say_oi.yml.erb +15 -0
  122. data/spec/replies/messages/say_randomize_speech.yml +10 -0
  123. data/spec/replies/messages/say_randomize_text.yml +10 -0
  124. data/spec/replies/messages/say_yo.yml +6 -0
  125. data/spec/replies/messages/say_yo.yml+twitter +6 -0
  126. data/spec/replies/messages/sub1/sub2/say_nested.yml +10 -0
  127. data/spec/reply_spec.rb +61 -0
  128. data/spec/scheduled_reply_spec.rb +23 -0
  129. data/spec/service_reply_spec.rb +92 -0
  130. data/spec/session_spec.rb +366 -0
  131. data/spec/spec_helper.rb +22 -66
  132. data/spec/support/alternate_helpers/foo_helper.rb +5 -0
  133. data/spec/support/controllers/vaders_controller.rb +24 -0
  134. data/spec/support/helpers/fun/games_helper.rb +7 -0
  135. data/spec/support/helpers/fun/pdf_helper.rb +7 -0
  136. data/spec/support/helpers/standalone_helper.rb +5 -0
  137. data/spec/support/helpers_typo/users_helper.rb +2 -0
  138. data/spec/support/nlp_clients/dialogflow.rb +9 -0
  139. data/spec/support/nlp_clients/luis.rb +9 -0
  140. data/spec/support/nlp_results/luis_result.rb +163 -0
  141. data/spec/support/sample_messages.rb +66 -0
  142. data/spec/support/services.yml +31 -0
  143. data/spec/support/services_with_erb.yml +31 -0
  144. data/spec/version_spec.rb +16 -0
  145. data/xip.gemspec +25 -14
  146. metadata +320 -18
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Xip::Controller::UnrecognizedMessage" do
6
+
7
+ let(:fb_message) { SampleMessage.new(service: 'facebook') }
8
+ let(:controller) { VadersController.new(service_message: fb_message.message_with_text) }
9
+
10
+ describe 'run_unrecognized_message' do
11
+ let(:e) {
12
+ e = OpenStruct.new
13
+ e.class = RuntimeError
14
+ e.message = 'oops'
15
+ e.backtrace = [
16
+ '/xip/lib/xip/controller/controller.rb',
17
+ '/xip/lib/xip/controller/catch_all.rb',
18
+ ]
19
+ e
20
+ }
21
+
22
+ describe 'when UnrecognizedMessagesController is not defined' do
23
+ before(:each) do
24
+ Object.send(:remove_const, :UnrecognizedMessagesController)
25
+ end
26
+
27
+ it "should log and run catch_all" do
28
+ expect(Xip::Logger).to receive(:l).with(
29
+ topic: 'unrecognized_message',
30
+ message: "The message \"Hello World!\" was not recognized in the original context."
31
+ ).ordered
32
+
33
+ expect(Xip::Logger).to receive(:l).with(
34
+ topic: 'unrecognized_message',
35
+ message: 'Running catch_all; UnrecognizedMessagesController not defined.'
36
+ ).ordered
37
+
38
+ expect(controller).to receive(:run_catch_all).with(err: e)
39
+ controller.run_unrecognized_message(err: e)
40
+ end
41
+ end
42
+
43
+ it "should call handle_unrecognized_message on the UnrecognizedMessagesController" do
44
+ class UnrecognizedMessagesController < Xip::Controller
45
+ def handle_unrecognized_message
46
+ do_nothing
47
+ end
48
+ end
49
+
50
+ expect(Xip::Logger).to receive(:l).with(
51
+ topic: 'unrecognized_message',
52
+ message: "The message \"Hello World!\" was not recognized in the original context."
53
+ ).ordered
54
+
55
+ expect(Xip::Logger).to receive(:l).with(
56
+ topic: 'unrecognized_message',
57
+ message: 'A match was detected. Skipping catch-all.'
58
+ ).ordered
59
+
60
+ controller.run_unrecognized_message(err: e)
61
+ end
62
+
63
+ it "should log if the UnrecognizedMessagesController#handle_unrecognized_message does not progress the session" do
64
+ class UnrecognizedMessagesController < Xip::Controller
65
+ def handle_unrecognized_message
66
+ # Oops
67
+ end
68
+ end
69
+
70
+ expect(Xip::Logger).to receive(:l).with(
71
+ topic: 'unrecognized_message',
72
+ message: "The message \"Hello World!\" was not recognized in the original context."
73
+ ).ordered
74
+
75
+ expect(Xip::Logger).to receive(:l).with(
76
+ topic: 'unrecognized_message',
77
+ message: 'Did not send replies, update session, or step'
78
+ ).ordered
79
+
80
+ expect(controller).to_not receive(:run_catch_all)
81
+
82
+ controller.run_unrecognized_message(err: e)
83
+ end
84
+
85
+ describe 'handoff to catch_all' do
86
+ before(:each) do
87
+ @session = Xip::Session.new(id: controller.current_session_id)
88
+ @session.set_session(new_flow: 'vader', new_state: 'action_with_unrecognized_msg')
89
+
90
+ @error_slug = [
91
+ 'error',
92
+ controller.current_session_id,
93
+ 'vader',
94
+ 'action_with_unrecognized_msg'
95
+ ].join('-')
96
+
97
+ $redis.del(@error_slug)
98
+ end
99
+
100
+ it "should catch StandardError within UnrecognizedMessagesController and run catch_all" do
101
+ $err = Xip::Errors::ReplyNotFound.new('oops')
102
+
103
+ class UnrecognizedMessagesController < Xip::Controller
104
+ def handle_unrecognized_message
105
+ raise $err
106
+ end
107
+ end
108
+
109
+ expect(Xip::Logger).to receive(:l).with(
110
+ topic: 'unrecognized_message',
111
+ message: "The message \"Hello World!\" was not recognized in the original context."
112
+ ).ordered
113
+
114
+ expect(controller).to receive(:run_catch_all).with(err: $err)
115
+
116
+ controller.run_unrecognized_message(err: e)
117
+ end
118
+
119
+ it "should track the catch_all level against the original session during exceptions" do
120
+ class UnrecognizedMessagesController < Xip::Controller
121
+ def handle_unrecognized_message
122
+ raise 'oops'
123
+ end
124
+ end
125
+
126
+ expect($redis.get(@error_slug)).to be_nil
127
+ controller.run_unrecognized_message(err: e)
128
+ expect($redis.get(@error_slug)).to eq '1'
129
+ end
130
+
131
+ it "should track the catch_all level against the original session for UnrecognizedMessage errors" do
132
+ class UnrecognizedMessagesController < Xip::Controller
133
+ def handle_unrecognized_message
134
+ handle_message(
135
+ 'x' => proc { do_nothing },
136
+ 'y' => proc { do_nothing }
137
+ )
138
+ end
139
+ end
140
+
141
+ expect($redis.get(@error_slug)).to be_nil
142
+ controller.action(action: :action_with_unrecognized_msg)
143
+ expect($redis.get(@error_slug)).to eq '1'
144
+ end
145
+
146
+ it "should NOT run catch_all if UnrecognizedMessagesController handles the message" do
147
+ $x = 0
148
+ class UnrecognizedMessagesController < Xip::Controller
149
+ def handle_unrecognized_message
150
+ handle_message(
151
+ 'Hello World!' => proc {
152
+ $x = 1
153
+ do_nothing
154
+ },
155
+ 'y' => proc { do_nothing }
156
+ )
157
+ end
158
+ end
159
+
160
+ expect($redis.get(@error_slug)).to be_nil
161
+ controller.action(action: :action_with_unrecognized_msg)
162
+ expect($redis.get(@error_slug)).to be_nil
163
+ expect($x).to eq 1
164
+ end
165
+ end
166
+ end
167
+
168
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Xip::Dispatcher" do
6
+
7
+ class Xip::Services::Facebook::MessageHandler
8
+
9
+ end
10
+
11
+ describe 'coordinate' do
12
+ let(:dispatcher) {
13
+ Xip::Dispatcher.new(
14
+ service: 'facebook',
15
+ params: {},
16
+ headers: {}
17
+ )
18
+ }
19
+
20
+ it 'should call coordinate on the message handler' do
21
+ message_handler = double
22
+ expect(Xip::Services::Facebook::MessageHandler).to receive(:new).and_return(message_handler)
23
+ expect(message_handler).to receive(:coordinate)
24
+
25
+ dispatcher.coordinate
26
+ end
27
+ end
28
+
29
+ describe 'process' do
30
+ class StubbedBotController < Xip::Controller
31
+ def route
32
+ true
33
+ end
34
+ end
35
+
36
+ let(:dispatcher) {
37
+ Xip::Dispatcher.new(
38
+ service: 'facebook',
39
+ params: {},
40
+ headers: {}
41
+ )
42
+ }
43
+ let(:fb_message) { SampleMessage.new(service: 'facebook') }
44
+ let(:stubbed_controller) {
45
+ StubbedBotController.new(service_message: fb_message.message_with_text)
46
+ }
47
+
48
+ it 'should call process on the message handler' do
49
+ message_handler = double
50
+
51
+ # Stub out the message handler to return a service_message
52
+ expect(Xip::Services::Facebook::MessageHandler).to receive(:new).and_return(message_handler)
53
+ expect(message_handler).to receive(:process).and_return(fb_message.message_with_text)
54
+
55
+ # Stub out BotController and set session
56
+ expect(BotController).to receive(:new).and_return(stubbed_controller)
57
+ stubbed_controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
58
+
59
+ dispatcher.process
60
+ end
61
+
62
+ it 'should log the incoming message if transcript_logging is enabled' do
63
+ message_handler = double
64
+
65
+ # Stub out the message handler to return a service_message
66
+ expect(Xip::Services::Facebook::MessageHandler).to receive(:new).and_return(message_handler)
67
+ expect(message_handler).to receive(:process).and_return(fb_message.message_with_text)
68
+
69
+ # Stub out BotController and set session
70
+ expect(BotController).to receive(:new).and_return(stubbed_controller)
71
+ stubbed_controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
72
+
73
+ Xip.config.transcript_logging = true
74
+ expect(dispatcher).to receive(:log_incoming_message).with(fb_message.message_with_text)
75
+ dispatcher.process
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Xip::Flow do
6
+
7
+ class CustomFlowMap
8
+ include Xip::Flow
9
+
10
+ flow :new_todo do
11
+ state :new
12
+ state :get_due_date
13
+ state :created
14
+ state :error
15
+ end
16
+
17
+ flow :hello do
18
+ state :say_hello
19
+ state :say_oi
20
+ end
21
+
22
+ flow "howdy" do
23
+ state :say_howdy
24
+ end
25
+ end
26
+
27
+ let(:flow_map) { CustomFlowMap.new }
28
+
29
+ describe "inititating with states" do
30
+ it "should init a state given a state name" do
31
+ flow_map.init(flow: 'new_todo', state: 'created')
32
+ expect(flow_map.current_state).to eq :created
33
+
34
+ flow_map.init(flow: 'new_todo', state: 'error')
35
+ expect(flow_map.current_state).to eq :error
36
+ end
37
+
38
+ it "should raise an error if an invalid state is specified" do
39
+ expect {
40
+ flow_map.init(flow: 'new_todo', state: 'invalid')
41
+ }.to raise_error(Xip::Errors::InvalidStateTransition)
42
+ end
43
+ end
44
+
45
+ describe "accessing states" do
46
+ it "should default to the first flow and state" do
47
+ expect(flow_map.current_flow).to eq(:new_todo)
48
+ expect(flow_map.current_state).to eq(:new)
49
+ end
50
+
51
+ it "should support comparing states" do
52
+ first_state = CustomFlowMap.flow_spec[:new_todo].states[:new]
53
+ last_state = CustomFlowMap.flow_spec[:new_todo].states[:error]
54
+ expect(first_state < last_state).to be true
55
+ expect(last_state > first_state).to be true
56
+ end
57
+
58
+ it "should allow every state to be fetched for a flow" do
59
+ expect(CustomFlowMap.flow_spec[:new_todo].states.length).to eq 4
60
+ expect(CustomFlowMap.flow_spec[:hello].states.length).to eq 2
61
+ expect(CustomFlowMap.flow_spec[:new_todo].states.keys).to eq([:new, :get_due_date, :created, :error])
62
+ expect(CustomFlowMap.flow_spec[:hello].states.keys).to eq([:say_hello, :say_oi])
63
+ end
64
+
65
+ it "should return the states in an array for a given FlowMap instance" do
66
+ expect(flow_map.states).to eq [:new, :get_due_date, :created, :error]
67
+ flow_map.init(flow: :hello, state: :say_oi)
68
+ expect(flow_map.states).to eq [:say_hello, :say_oi]
69
+ end
70
+
71
+ it "should allow flows to be specified with strings" do
72
+ expect(CustomFlowMap.flow_spec[:howdy].states.length).to eq 1
73
+ expect(CustomFlowMap.flow_spec[:howdy].states.keys).to eq([:say_howdy])
74
+ end
75
+
76
+ it "should allow FlowMaps to be intialized with strings" do
77
+ flow_map.init(flow: "hello", state: "say_oi")
78
+ expect(flow_map.states).to eq [:say_hello, :say_oi]
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Xip::Flow::State do
6
+
7
+ class SuperFlowMap
8
+ include Xip::Flow
9
+
10
+ flow :new_todo do
11
+ state :new
12
+ state :get_due_date
13
+ state :created, fails_to: :new
14
+ state :created2, fails_to: 'new_todo->new'
15
+ state :deprecated, redirects_to: 'new'
16
+ state :deprecated2, redirects_to: 'other_flow->say_hi'
17
+ state :new_opts, opt1: 'hello', opt2: 1
18
+ state :error
19
+ end
20
+ end
21
+
22
+ let(:flow_map) { SuperFlowMap.new }
23
+
24
+ describe "flow states" do
25
+ it "should convert itself to a string" do
26
+ expect(flow_map.current_state.to_s).to be_a(String)
27
+ end
28
+
29
+ it "should convert itself to a symbol" do
30
+ expect(flow_map.current_state.to_sym).to be_a(Symbol)
31
+ end
32
+ end
33
+
34
+ describe "fails_to" do
35
+ it "should be nil for a state that has not specified a fails_to" do
36
+ expect(flow_map.current_state.fails_to).to be_nil
37
+ end
38
+
39
+ it "should return the fail_state if a fails_to was specified" do
40
+ flow_map.init(flow: :new_todo, state: :created)
41
+ expect(flow_map.current_state.fails_to).to be_a(Xip::Session)
42
+ expect(flow_map.current_state.fails_to.state_string).to eq 'new'
43
+ end
44
+
45
+ it "should return the fail_state if a fails_to was specified as a session" do
46
+ flow_map.init(flow: :new_todo, state: :created2)
47
+ expect(flow_map.current_state.fails_to).to be_a(Xip::Session)
48
+ expect(flow_map.current_state.fails_to.state_string).to eq 'new'
49
+ expect(flow_map.current_state.fails_to.flow_string).to eq 'new_todo'
50
+ end
51
+ end
52
+
53
+ describe "redirects_to" do
54
+ it "should be nil for a state that has not specified a fails_to" do
55
+ expect(flow_map.current_state.redirects_to).to be_nil
56
+ end
57
+
58
+ it "should return the redirects_to state if a redirects_to was specified" do
59
+ flow_map.init(flow: :new_todo, state: :deprecated)
60
+ expect(flow_map.current_state.redirects_to).to be_a(Xip::Session)
61
+ expect(flow_map.current_state.redirects_to.state_string).to eq 'new'
62
+ end
63
+
64
+ it "should return the redirects_to state if a redirects_to was specified as a session" do
65
+ flow_map.init(flow: :new_todo, state: :deprecated2)
66
+ expect(flow_map.current_state.redirects_to).to be_a(Xip::Session)
67
+ expect(flow_map.current_state.redirects_to.state_string).to eq 'say_hi'
68
+ expect(flow_map.current_state.redirects_to.flow_string).to eq 'other_flow'
69
+ end
70
+ end
71
+
72
+ describe "opts" do
73
+ it "should return {} for a state that has not specified any opts" do
74
+ expect(flow_map.current_state.opts).to eq({})
75
+ end
76
+
77
+ it "should return the opts if they were specified" do
78
+ flow_map.init(flow: :new_todo, state: :new_opts)
79
+ expect(flow_map.current_state.opts).to eq({ opt1: 'hello', opt2: 1 })
80
+ end
81
+ end
82
+
83
+ describe "state incrementing and decrementing" do
84
+ it "should increment the state" do
85
+ flow_map.init(flow: :new_todo, state: :get_due_date)
86
+ new_state = flow_map.current_state + 1.state
87
+ expect(new_state).to eq(:created)
88
+ end
89
+
90
+ it "should decrement the state" do
91
+ flow_map.init(flow: :new_todo, state: :error)
92
+ new_state = flow_map.current_state - 6.states
93
+ expect(new_state).to eq(:get_due_date)
94
+ end
95
+
96
+ it "should return the first state if the decrement is out of bounds" do
97
+ flow_map.init(flow: :new_todo, state: :get_due_date)
98
+ new_state = flow_map.current_state - 5.states
99
+ expect(new_state).to eq(:new)
100
+ end
101
+
102
+ it "should return the last state if the increment is out of bounds" do
103
+ flow_map.init(flow: :new_todo, state: :created)
104
+ new_state = flow_map.current_state + 10.states
105
+ expect(new_state).to eq(:error)
106
+ end
107
+ end
108
+
109
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Xip::Redis" do
6
+
7
+ class RedisTester
8
+ include Xip::Redis
9
+ end
10
+
11
+ let(:redis_tester) { RedisTester.new }
12
+ let(:key) { 'xyz' }
13
+
14
+ describe "get_key" do
15
+ it "should return the key from Redis if an expiration is not set" do
16
+ $redis.set(key, 'abc')
17
+ expect(redis_tester.send(:get_key, key)).to eq 'abc'
18
+ end
19
+
20
+ it "should call getex if an expiration is set" do
21
+ expect(redis_tester).to receive(:getex).with(key, 30)
22
+ redis_tester.send(:get_key, key, expiration: 30)
23
+ end
24
+ end
25
+
26
+ describe "delete_key" do
27
+ it 'should delete the key from Redis' do
28
+ $redis.set(key, 'abc')
29
+ expect(redis_tester.send(:get_key, key)).to eq 'abc'
30
+ redis_tester.send(:delete_key, key)
31
+ expect(redis_tester.send(:get_key, key)).to be_nil
32
+ end
33
+ end
34
+
35
+ describe "getex" do
36
+ it "should return the key from Redis" do
37
+ Xip.config.session_ttl = 50
38
+ $redis.set(key, 'abc')
39
+ expect(redis_tester.send(:getex, key)).to eq 'abc'
40
+ end
41
+
42
+ it "should set the expiration of a key in Redis" do
43
+ Xip.config.session_ttl = 50
44
+ $redis.set(key, 'abc')
45
+ redis_tester.send(:getex, key)
46
+ expect($redis.ttl(key)).to be_between(0, 50).inclusive
47
+ end
48
+
49
+ it "should update the expiration of a key in Redis" do
50
+ Xip.config.session_ttl = 500
51
+ $redis.setex(key, 50, 'abc')
52
+ redis_tester.send(:getex, key)
53
+ expect($redis.ttl(key)).to be_between(400, 500).inclusive
54
+ end
55
+ end
56
+
57
+ describe "persist_key" do
58
+ it "should set the key in Redis" do
59
+ Xip.config.session_ttl = 50
60
+ redis_tester.send(:persist_key, key: key, value: 'zzz')
61
+ expect($redis.get(key)).to eq 'zzz'
62
+ end
63
+
64
+ it "should set the expiration to session_ttl if none specified" do
65
+ Xip.config.session_ttl = 50
66
+ redis_tester.send(:persist_key, key: key, value: 'zzz')
67
+ expect($redis.ttl(key)).to be_between(0, 50).inclusive
68
+ end
69
+
70
+ it "should set the expiration to the specified value when provided" do
71
+ Xip.config.session_ttl = 50
72
+ redis_tester.send(:persist_key, key: key, value: 'zzz', expiration: 500)
73
+ expect($redis.ttl(key)).to be_between(400, 500).inclusive
74
+ end
75
+ end
76
+
77
+ end