flow_chat 0.8.2 → 0.9.0
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.
- checksums.yaml +4 -4
- data/.cliff.toml +74 -0
- data/.github/workflows/ci.yml +2 -3
- data/.github/workflows/release.yml +56 -0
- data/.standard.yml +4 -0
- data/CHANGELOG.md +22 -0
- data/CLAUDE.md +327 -0
- data/CONTRIBUTING.md +134 -0
- data/Gemfile +1 -0
- data/README.md +290 -105
- data/Rakefile +5 -1
- data/SECURITY.md +42 -349
- data/docs/architecture.md +510 -0
- data/docs/async-background-processing.md +298 -0
- data/docs/configuration.md +556 -226
- data/docs/factory-pattern.md +355 -0
- data/docs/gateway-context-variables.md +171 -0
- data/docs/gateway-development.md +723 -0
- data/docs/getting-started.md +429 -0
- data/docs/instrumentation.md +264 -153
- data/docs/platforms/telegram.md +1013 -0
- data/docs/platforms/ussd.md +693 -0
- data/docs/platforms/whatsapp.md +1395 -0
- data/docs/testing.md +243 -365
- data/examples/custom_session_id_example.rb +119 -0
- data/examples/http_controller.rb +11 -11
- data/examples/intercom_configuration_example.rb +118 -0
- data/examples/intercom_controller.rb +194 -0
- data/examples/multi_tenant_whatsapp_controller.rb +4 -4
- data/examples/ussd_controller.rb +9 -9
- data/examples/whatsapp_controller.rb +2 -2
- data/flow_chat.gemspec +4 -0
- data/lib/flow_chat/{base_app.rb → app.rb} +16 -9
- data/lib/flow_chat/async_job.rb +176 -0
- data/lib/flow_chat/config.rb +10 -30
- data/lib/flow_chat/{base_executor.rb → executor.rb} +6 -11
- data/lib/flow_chat/factory.rb +94 -0
- data/lib/flow_chat/gateway_async_support.rb +106 -0
- data/lib/flow_chat/generic_async_job.rb +30 -0
- data/lib/flow_chat/http/gateway/simple.rb +81 -33
- data/lib/flow_chat/http/renderer.rb +3 -3
- data/lib/flow_chat/instrumentation/setup.rb +1 -1
- data/lib/flow_chat/instrumentation.rb +23 -0
- data/lib/flow_chat/intercom/client.rb +155 -0
- data/lib/flow_chat/intercom/configuration.rb +149 -0
- data/lib/flow_chat/intercom/gateway/intercom_api.rb +396 -0
- data/lib/flow_chat/intercom/renderer.rb +71 -0
- data/lib/flow_chat/phone_number_util.rb +37 -35
- data/lib/flow_chat/processor.rb +188 -0
- data/lib/flow_chat/renderers/markdown_support.rb +58 -0
- data/lib/flow_chat/session/middleware.rb +25 -9
- data/lib/flow_chat/telegram/client.rb +240 -0
- data/lib/flow_chat/telegram/configuration.rb +118 -0
- data/lib/flow_chat/telegram/gateway/bot_api.rb +300 -0
- data/lib/flow_chat/telegram/middleware/choice_mapper.rb +35 -0
- data/lib/flow_chat/telegram/renderer.rb +125 -0
- data/lib/flow_chat/telegram.rb +7 -0
- data/lib/flow_chat/ussd/gateway/nalo.rb +24 -4
- data/lib/flow_chat/ussd/middleware/pagination.rb +9 -5
- data/lib/flow_chat/ussd/renderer.rb +1 -1
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/client.rb +144 -13
- data/lib/flow_chat/whatsapp/configuration.rb +1 -1
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +132 -96
- data/lib/flow_chat/whatsapp/id_generator.rb +124 -0
- data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +147 -0
- data/lib/flow_chat/whatsapp/renderer.rb +145 -11
- data/lib/flow_chat.rb +11 -1
- data/lib/tasks/release.rake +165 -0
- metadata +84 -25
- data/docs/flows.md +0 -320
- data/docs/http-gateway-protocol.md +0 -432
- data/docs/images/simulator.png +0 -0
- data/docs/media.md +0 -153
- data/docs/sessions.md +0 -433
- data/docs/ussd-setup.md +0 -322
- data/docs/whatsapp-setup.md +0 -162
- data/examples/whatsapp_message_job.rb +0 -113
- data/lib/flow_chat/base_processor.rb +0 -146
- data/lib/flow_chat/http/app.rb +0 -6
- data/lib/flow_chat/http/middleware/executor.rb +0 -24
- data/lib/flow_chat/http/processor.rb +0 -33
- data/lib/flow_chat/session/rails_session_store.rb +0 -68
- data/lib/flow_chat/ussd/app.rb +0 -6
- data/lib/flow_chat/ussd/gateway/nsano.rb +0 -96
- data/lib/flow_chat/ussd/middleware/executor.rb +0 -24
- data/lib/flow_chat/ussd/processor.rb +0 -39
- data/lib/flow_chat/whatsapp/app.rb +0 -29
- data/lib/flow_chat/whatsapp/middleware/executor.rb +0 -24
- data/lib/flow_chat/whatsapp/processor.rb +0 -32
- data/lib/flow_chat/whatsapp/send_job_support.rb +0 -79
data/docs/testing.md
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
# Testing
|
|
1
|
+
# Testing & Simulation
|
|
2
2
|
|
|
3
|
-
FlowChat provides
|
|
3
|
+
FlowChat provides a sophisticated web-based simulator for testing conversational flows across multiple platforms (USSD, WhatsApp, HTTP) without requiring actual gateway integrations.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Built-in Web Simulator
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### Overview
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|----------|----------|------------------|----------------|
|
|
11
|
-
| **Unit Testing** | Individual flow logic | Low | No |
|
|
12
|
-
| **Simulator Mode** | Integration testing, development | Medium | No |
|
|
13
|
-
| **Skip Validation** | Staging environments | Medium | Yes |
|
|
14
|
-
| **Full Integration** | Production-like testing | High | Yes |
|
|
9
|
+
The FlowChat simulator is a browser-based testing interface that provides:
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
- **Multi-Platform Testing**: USSD, WhatsApp, and HTTP with platform-specific UI
|
|
12
|
+
- **Real-Time Request Logging**: Complete HTTP traffic monitoring and debugging
|
|
13
|
+
- **Security**: HMAC-signed cookie authentication
|
|
14
|
+
- **Platform-Specific Rendering**: Accurate simulation of each platform's behavior
|
|
15
|
+
- **Configuration Management**: Test multiple endpoints from a single interface
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
### Setup & Configuration
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
#### 1. Basic Configuration
|
|
21
20
|
|
|
22
21
|
```ruby
|
|
23
|
-
# config/initializers/
|
|
24
|
-
FlowChat::Config.simulator_secret =
|
|
22
|
+
# config/initializers/flow_chat.rb
|
|
23
|
+
FlowChat::Config.simulator_secret = "your_secure_secret_key_here"
|
|
25
24
|
```
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
#### 2. Create Simulator Controller
|
|
28
27
|
|
|
29
28
|
```ruby
|
|
30
29
|
# app/controllers/simulator_controller.rb
|
|
@@ -37,439 +36,318 @@ class SimulatorController < ApplicationController
|
|
|
37
36
|
|
|
38
37
|
protected
|
|
39
38
|
|
|
39
|
+
# Configure available test endpoints
|
|
40
40
|
def configurations
|
|
41
41
|
{
|
|
42
|
-
|
|
43
|
-
name: "USSD
|
|
44
|
-
|
|
42
|
+
ussd_main: {
|
|
43
|
+
name: "USSD (Nalo)",
|
|
44
|
+
description: "USSD integration using Nalo gateway",
|
|
45
45
|
processor_type: "ussd",
|
|
46
46
|
gateway: "nalo",
|
|
47
47
|
endpoint: "/ussd",
|
|
48
|
-
|
|
48
|
+
icon: "📱",
|
|
49
|
+
color: "#28a745",
|
|
50
|
+
settings: {
|
|
51
|
+
phone_number: default_phone_number,
|
|
52
|
+
session_timeout: 300
|
|
53
|
+
}
|
|
49
54
|
},
|
|
50
|
-
|
|
51
|
-
name: "WhatsApp
|
|
52
|
-
|
|
53
|
-
processor_type: "whatsapp",
|
|
54
|
-
gateway: "cloud_api",
|
|
55
|
+
whatsapp_main: {
|
|
56
|
+
name: "WhatsApp (Cloud API)",
|
|
57
|
+
description: "WhatsApp integration using Cloud API",
|
|
58
|
+
processor_type: "whatsapp",
|
|
59
|
+
gateway: "cloud_api",
|
|
55
60
|
endpoint: "/whatsapp/webhook",
|
|
56
|
-
|
|
61
|
+
icon: "💬",
|
|
62
|
+
color: "#25D366",
|
|
63
|
+
settings: {
|
|
64
|
+
phone_number: default_phone_number,
|
|
65
|
+
contact_name: default_contact_name
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
http_api: {
|
|
69
|
+
name: "HTTP API",
|
|
70
|
+
description: "JSON HTTP API endpoint",
|
|
71
|
+
processor_type: "http",
|
|
72
|
+
gateway: "http_simple",
|
|
73
|
+
endpoint: "/http/webhook",
|
|
74
|
+
icon: "🌐",
|
|
75
|
+
color: "#0066cc",
|
|
76
|
+
settings: {
|
|
77
|
+
user_id: default_phone_number
|
|
78
|
+
}
|
|
57
79
|
}
|
|
58
80
|
}
|
|
59
81
|
end
|
|
60
82
|
|
|
61
|
-
def default_config_key
|
|
62
|
-
:whatsapp
|
|
63
|
-
end
|
|
64
|
-
|
|
65
83
|
def default_phone_number
|
|
66
|
-
"+
|
|
84
|
+
"+233244123456"
|
|
67
85
|
end
|
|
68
86
|
|
|
69
87
|
def default_contact_name
|
|
70
|
-
"
|
|
88
|
+
"John Doe"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def default_config_key
|
|
92
|
+
:ussd_main
|
|
71
93
|
end
|
|
72
94
|
end
|
|
73
95
|
```
|
|
74
96
|
|
|
75
|
-
|
|
97
|
+
#### 3. Add Route
|
|
76
98
|
|
|
77
99
|
```ruby
|
|
78
100
|
# config/routes.rb
|
|
79
101
|
Rails.application.routes.draw do
|
|
80
102
|
get '/simulator' => 'simulator#index'
|
|
81
|
-
# ...
|
|
103
|
+
# ... other routes
|
|
82
104
|
end
|
|
83
105
|
```
|
|
84
106
|
|
|
85
|
-
###
|
|
107
|
+
### Security Features
|
|
86
108
|
|
|
87
|
-
|
|
88
|
-
# app/controllers/whatsapp_controller.rb
|
|
89
|
-
class WhatsappController < ApplicationController
|
|
90
|
-
skip_forgery_protection
|
|
109
|
+
#### HMAC-Signed Cookies
|
|
91
110
|
|
|
92
|
-
|
|
93
|
-
enable_simulator = Rails.env.development? # enabled in development by default
|
|
94
|
-
processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator:) do |config|
|
|
95
|
-
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
|
96
|
-
config.use_session_store FlowChat::Session::CacheSessionStore
|
|
97
|
-
end
|
|
111
|
+
The simulator uses secure, timestamped cookies for authentication:
|
|
98
112
|
|
|
99
|
-
|
|
100
|
-
|
|
113
|
+
```ruby
|
|
114
|
+
# Automatic cookie generation in simulator controller
|
|
115
|
+
def set_simulator_cookie
|
|
116
|
+
simulator_secret = FlowChat::Config.simulator_secret
|
|
117
|
+
timestamp = Time.now.to_i
|
|
118
|
+
message = "simulator:#{timestamp}"
|
|
119
|
+
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), simulator_secret, message)
|
|
120
|
+
|
|
121
|
+
cookies[:flowchat_simulator] = {
|
|
122
|
+
value: "#{timestamp}:#{signature}",
|
|
123
|
+
expires: 24.hours.from_now,
|
|
124
|
+
secure: request.ssl?, # HTTPS only in production
|
|
125
|
+
httponly: true, # Prevent XSS
|
|
126
|
+
same_site: :lax # CSRF protection
|
|
127
|
+
}
|
|
101
128
|
end
|
|
102
129
|
```
|
|
103
130
|
|
|
104
|
-
|
|
131
|
+
#### Automatic Enablement
|
|
132
|
+
|
|
133
|
+
Simulator mode is automatically enabled when:
|
|
105
134
|
|
|
106
|
-
|
|
135
|
+
```ruby
|
|
136
|
+
# In processor initialization
|
|
137
|
+
enable_simulator: Rails.env.local? # true for development/test
|
|
138
|
+
```
|
|
107
139
|
|
|
108
|
-
|
|
109
|
-
- 📱 **Visual Interface** - Phone-like display showing actual conversation
|
|
110
|
-
- 🔄 **Platform Switching** - Toggle between USSD and WhatsApp modes
|
|
111
|
-
- 📊 **Request Logging** - See HTTP requests and responses in real-time
|
|
112
|
-
- 🎯 **Interactive Testing** - Character counting, validation, session management
|
|
113
|
-
- 🛠️ **Developer Tools** - Reset sessions, view connection status
|
|
140
|
+
### Platform-Specific Features
|
|
114
141
|
|
|
115
|
-
|
|
142
|
+
#### USSD Testing
|
|
116
143
|
|
|
117
|
-
|
|
144
|
+
**Interface**: Menu-style display with character limits and pagination
|
|
118
145
|
|
|
119
|
-
|
|
146
|
+
**Features**:
|
|
147
|
+
- Network-specific character limits (MTN: 160, Airtel: 140, etc.)
|
|
148
|
+
- Session timeout simulation
|
|
149
|
+
- Menu option selection
|
|
150
|
+
- Automatic pagination for long content
|
|
120
151
|
|
|
152
|
+
**Request Format**:
|
|
121
153
|
```ruby
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
@context.session.init_session("test_session")
|
|
130
|
-
end
|
|
154
|
+
{
|
|
155
|
+
msisdn: "+233244123456",
|
|
156
|
+
text: "user_input",
|
|
157
|
+
session_id: "session_123",
|
|
158
|
+
network: "mtn"
|
|
159
|
+
}
|
|
160
|
+
```
|
|
131
161
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
162
|
+
#### WhatsApp Testing
|
|
163
|
+
|
|
164
|
+
**Interface**: Chat-style interface with contact avatars and message history
|
|
165
|
+
|
|
166
|
+
**Features**:
|
|
167
|
+
- Contact name and avatar display
|
|
168
|
+
- Interactive buttons and lists
|
|
169
|
+
- Media support (images, documents, audio, video)
|
|
170
|
+
- Cloud API webhook simulation
|
|
171
|
+
|
|
172
|
+
**Request Format**:
|
|
173
|
+
```javascript
|
|
174
|
+
{
|
|
175
|
+
simulator_mode: true,
|
|
176
|
+
entry: [{
|
|
177
|
+
changes: [{
|
|
178
|
+
value: {
|
|
179
|
+
messaging_product: "whatsapp",
|
|
180
|
+
metadata: {
|
|
181
|
+
display_phone_number: "233244123456",
|
|
182
|
+
phone_number_id: "phone_number_id_123"
|
|
183
|
+
},
|
|
184
|
+
messages: [{
|
|
185
|
+
text: { body: "user_input" },
|
|
186
|
+
type: 'text'
|
|
187
|
+
}],
|
|
188
|
+
contacts: [{
|
|
189
|
+
profile: { name: "John Doe" },
|
|
190
|
+
wa_id: "233244123456"
|
|
191
|
+
}]
|
|
192
|
+
}
|
|
193
|
+
}]
|
|
194
|
+
}]
|
|
195
|
+
}
|
|
196
|
+
```
|
|
145
197
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
198
|
+
**Response Handling**:
|
|
199
|
+
```javascript
|
|
200
|
+
// Simulator expects JSON response for full simulation
|
|
201
|
+
{
|
|
202
|
+
mode: "simulator",
|
|
203
|
+
webhook_processed: true,
|
|
204
|
+
would_send: {
|
|
205
|
+
to: "233244123456",
|
|
206
|
+
type: "text",
|
|
207
|
+
text: { body: "Response message" }
|
|
208
|
+
},
|
|
209
|
+
message_info: {
|
|
210
|
+
to: "233244123456",
|
|
211
|
+
contact_name: "John Doe",
|
|
212
|
+
timestamp: "2024-01-01T12:00:00Z"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
160
215
|
```
|
|
161
216
|
|
|
162
|
-
|
|
217
|
+
#### HTTP Testing
|
|
163
218
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
# Verify email was stored
|
|
182
|
-
assert_equal "john@example.com", context.session.get(:email)
|
|
183
|
-
|
|
184
|
-
# Step 2: Enter age
|
|
185
|
-
context.input = "25"
|
|
186
|
-
|
|
187
|
-
assert_raises(FlowChat::Interrupt::Input) do
|
|
188
|
-
flow = RegistrationFlow.new(app)
|
|
189
|
-
flow.main_page # Continue from where we left off
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
# Step 3: Confirm
|
|
193
|
-
context.input = "yes"
|
|
194
|
-
|
|
195
|
-
assert_raises(FlowChat::Interrupt::Terminate) do
|
|
196
|
-
flow = RegistrationFlow.new(app)
|
|
197
|
-
flow.main_page
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
end
|
|
219
|
+
**Interface**: API-style interface showing JSON request/response
|
|
220
|
+
|
|
221
|
+
**Features**:
|
|
222
|
+
- JSON payload display
|
|
223
|
+
- Session management
|
|
224
|
+
- Custom headers
|
|
225
|
+
- Error handling
|
|
226
|
+
|
|
227
|
+
**Request Format**:
|
|
228
|
+
```javascript
|
|
229
|
+
{
|
|
230
|
+
session_id: "session_123",
|
|
231
|
+
user_id: "+233244123456",
|
|
232
|
+
input: "user_input",
|
|
233
|
+
simulator_mode: true
|
|
234
|
+
}
|
|
201
235
|
```
|
|
202
236
|
|
|
203
|
-
|
|
237
|
+
### Request Logging & Debugging
|
|
204
238
|
|
|
205
|
-
|
|
239
|
+
#### Real-Time Traffic Monitoring
|
|
206
240
|
|
|
207
|
-
|
|
241
|
+
The simulator provides comprehensive request logging:
|
|
208
242
|
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
when 'development'
|
|
213
|
-
# Use simulator for easy testing
|
|
214
|
-
FlowChat::Config.whatsapp.message_handling_mode = :simulator
|
|
215
|
-
FlowChat::Config.simulator_secret = Rails.application.secret_key_base + "_dev"
|
|
216
|
-
|
|
217
|
-
when 'test'
|
|
218
|
-
# Use simulator for automated tests
|
|
219
|
-
FlowChat::Config.whatsapp.message_handling_mode = :simulator
|
|
220
|
-
FlowChat::Config.simulator_secret = "test_secret_key"
|
|
221
|
-
|
|
222
|
-
when 'staging'
|
|
223
|
-
# Use inline mode with real WhatsApp API but skip validation for testing
|
|
224
|
-
FlowChat::Config.whatsapp.message_handling_mode = :inline
|
|
225
|
-
FlowChat::Config.simulator_secret = ENV['FLOWCHAT_SIMULATOR_SECRET']
|
|
226
|
-
|
|
227
|
-
when 'production'
|
|
228
|
-
# Use background jobs with full security
|
|
229
|
-
FlowChat::Config.whatsapp.message_handling_mode = :background
|
|
230
|
-
FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
|
|
231
|
-
# No simulator secret in production
|
|
232
|
-
end
|
|
243
|
+
```javascript
|
|
244
|
+
// Automatic logging of all HTTP traffic
|
|
245
|
+
addRequestLog('POST', endpoint, requestData, responseData, statusCode, errorMessage)
|
|
233
246
|
```
|
|
234
247
|
|
|
235
|
-
|
|
248
|
+
**Log Information**:
|
|
249
|
+
- Request method and URL
|
|
250
|
+
- Complete request payload
|
|
251
|
+
- Response status and data
|
|
252
|
+
- Timestamps and duration
|
|
253
|
+
- Error messages with helpful debugging hints
|
|
236
254
|
|
|
237
|
-
|
|
255
|
+
#### Error Handling
|
|
238
256
|
|
|
239
|
-
|
|
240
|
-
# test/controllers/whatsapp_controller_test.rb
|
|
241
|
-
class WhatsappControllerTest < ActionDispatch::IntegrationTest
|
|
242
|
-
test "processes whatsapp message in simulator mode" do
|
|
243
|
-
webhook_payload = {
|
|
244
|
-
entry: [{
|
|
245
|
-
changes: [{
|
|
246
|
-
value: {
|
|
247
|
-
messages: [{
|
|
248
|
-
from: "1234567890",
|
|
249
|
-
text: { body: "Hello" },
|
|
250
|
-
type: "text",
|
|
251
|
-
id: "msg_123",
|
|
252
|
-
timestamp: Time.now.to_i
|
|
253
|
-
}]
|
|
254
|
-
}
|
|
255
|
-
}]
|
|
256
|
-
}],
|
|
257
|
-
simulator_mode: true # Enable simulator mode
|
|
258
|
-
}
|
|
257
|
+
The simulator provides detailed error messages for common issues:
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
params: webhook_payload,
|
|
265
|
-
cookies: { flowchat_simulator: valid_cookie }
|
|
266
|
-
|
|
267
|
-
assert_response :success
|
|
268
|
-
|
|
269
|
-
# In simulator mode, response contains message data
|
|
270
|
-
response_data = JSON.parse(response.body)
|
|
271
|
-
assert response_data.key?("text")
|
|
272
|
-
assert_includes response_data["text"], "What's your name?"
|
|
273
|
-
end
|
|
259
|
+
- **Connection Failures**: CORS, SSL, network issues
|
|
260
|
+
- **404 Not Found**: Route configuration problems
|
|
261
|
+
- **500 Server Errors**: Application errors
|
|
262
|
+
- **Authentication Issues**: Invalid simulator cookies
|
|
274
263
|
|
|
275
|
-
|
|
276
|
-
valid_cookie = generate_simulator_cookie
|
|
277
|
-
|
|
278
|
-
# Step 1: Start conversation
|
|
279
|
-
post_simulator_message("start", valid_cookie)
|
|
280
|
-
assert_response :success
|
|
281
|
-
|
|
282
|
-
# Step 2: Enter name
|
|
283
|
-
post_simulator_message("John", valid_cookie)
|
|
284
|
-
assert_response :success
|
|
285
|
-
|
|
286
|
-
response_data = JSON.parse(response.body)
|
|
287
|
-
assert_includes response_data["text"], "Hello John"
|
|
288
|
-
end
|
|
264
|
+
### Enabling Simulator Mode in Your Endpoints
|
|
289
265
|
|
|
290
|
-
|
|
266
|
+
#### For WhatsApp Endpoints
|
|
291
267
|
|
|
292
|
-
|
|
293
|
-
timestamp = Time.now.to_i
|
|
294
|
-
message = "simulator:#{timestamp}"
|
|
295
|
-
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), secret, message)
|
|
296
|
-
"#{timestamp}:#{signature}"
|
|
297
|
-
end
|
|
268
|
+
Your WhatsApp processor automatically handles simulator mode:
|
|
298
269
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
text: { body: text },
|
|
307
|
-
type: "text",
|
|
308
|
-
id: "msg_#{rand(1000)}",
|
|
309
|
-
timestamp: Time.now.to_i
|
|
310
|
-
}]
|
|
311
|
-
}
|
|
312
|
-
}]
|
|
313
|
-
}],
|
|
314
|
-
simulator_mode: true
|
|
315
|
-
}
|
|
270
|
+
```ruby
|
|
271
|
+
class WhatsappController < ApplicationController
|
|
272
|
+
def webhook
|
|
273
|
+
processor = FlowCha::Processor.new(self, enable_simulator: !Rails.env.production?) do |config|
|
|
274
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
|
275
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
|
276
|
+
end
|
|
316
277
|
|
|
317
|
-
|
|
318
|
-
params: webhook_payload,
|
|
319
|
-
cookies: { flowchat_simulator: cookie }
|
|
278
|
+
processor.run WelcomeFlow, :main_page
|
|
320
279
|
end
|
|
321
280
|
end
|
|
322
281
|
```
|
|
323
282
|
|
|
324
|
-
|
|
283
|
+
#### For HTTP Endpoints
|
|
325
284
|
|
|
326
|
-
|
|
285
|
+
HTTP endpoints work automatically with the simulator:
|
|
327
286
|
|
|
328
287
|
```ruby
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
config.skip_signature_validation = true # Disable validation for testing
|
|
336
|
-
|
|
337
|
-
webhook_payload = {
|
|
338
|
-
entry: [{
|
|
339
|
-
changes: [{
|
|
340
|
-
value: {
|
|
341
|
-
messages: [{
|
|
342
|
-
from: "1234567890",
|
|
343
|
-
text: { body: "Hello" },
|
|
344
|
-
type: "text"
|
|
345
|
-
}]
|
|
346
|
-
}
|
|
347
|
-
}]
|
|
348
|
-
}]
|
|
349
|
-
}
|
|
288
|
+
class HttpController < ApplicationController
|
|
289
|
+
def webhook
|
|
290
|
+
processor = FlowChat::Processor.new(self) do |config|
|
|
291
|
+
config.use_gateway FlowChat::Http::Gateway::Simple
|
|
292
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
|
293
|
+
end
|
|
350
294
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
headers: { "Content-Type" => "application/json" }
|
|
354
|
-
|
|
355
|
-
assert_response :success
|
|
295
|
+
processor.run WelcomeFlow, :main_page
|
|
296
|
+
end
|
|
356
297
|
end
|
|
357
298
|
```
|
|
358
299
|
|
|
359
|
-
|
|
300
|
+
### Usage Workflow
|
|
360
301
|
|
|
361
|
-
|
|
302
|
+
#### 1. Start the Simulator
|
|
362
303
|
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
# Send invalid email
|
|
368
|
-
post_simulator_message("invalid-email", valid_cookie)
|
|
369
|
-
|
|
370
|
-
response_data = JSON.parse(response.body)
|
|
371
|
-
assert_includes response_data["text"], "Invalid email format"
|
|
372
|
-
|
|
373
|
-
# Send valid email - should proceed
|
|
374
|
-
post_simulator_message("john@example.com", valid_cookie)
|
|
375
|
-
|
|
376
|
-
response_data = JSON.parse(response.body)
|
|
377
|
-
refute_includes response_data["text"], "Invalid email"
|
|
378
|
-
end
|
|
304
|
+
```bash
|
|
305
|
+
rails server
|
|
306
|
+
# Visit http://localhost:3000/simulator
|
|
379
307
|
```
|
|
380
308
|
|
|
381
|
-
|
|
309
|
+
#### 2. Configure Test Environment
|
|
382
310
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
post_simulator_message("help", valid_cookie)
|
|
388
|
-
|
|
389
|
-
response_data = JSON.parse(response.body)
|
|
390
|
-
|
|
391
|
-
# Check media is included
|
|
392
|
-
assert response_data.key?("media")
|
|
393
|
-
assert_equal "image", response_data["media"]["type"]
|
|
394
|
-
assert response_data["media"]["url"].present?
|
|
395
|
-
end
|
|
396
|
-
```
|
|
311
|
+
- Select endpoint from configuration dropdown
|
|
312
|
+
- Set phone number and contact name
|
|
313
|
+
- Choose platform type (USSD/WhatsApp/HTTP)
|
|
397
314
|
|
|
398
|
-
|
|
315
|
+
#### 3. Test Conversation Flows
|
|
399
316
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
# First request - enter name
|
|
405
|
-
post_simulator_message("John", valid_cookie)
|
|
406
|
-
|
|
407
|
-
# Second request - session should remember name
|
|
408
|
-
post_simulator_message("continue", valid_cookie)
|
|
409
|
-
|
|
410
|
-
response_data = JSON.parse(response.body)
|
|
411
|
-
assert_includes response_data["text"], "John" # Name should be remembered
|
|
412
|
-
end
|
|
413
|
-
```
|
|
317
|
+
- Click "Start Session" to begin
|
|
318
|
+
- Send messages through the platform-specific interface
|
|
319
|
+
- Monitor request/response logs in real-time
|
|
320
|
+
- Reset session to test different scenarios
|
|
414
321
|
|
|
415
|
-
|
|
322
|
+
#### 4. Debug Issues
|
|
416
323
|
|
|
417
|
-
|
|
324
|
+
- Check request logs for HTTP traffic details
|
|
325
|
+
- Verify endpoint responses and status codes
|
|
326
|
+
- Review error messages for configuration issues
|
|
327
|
+
- Test different platforms and configurations
|
|
418
328
|
|
|
419
|
-
|
|
420
|
-
test "handles high message volume with background jobs" do
|
|
421
|
-
# Switch to background mode for this test
|
|
422
|
-
original_mode = FlowChat::Config.whatsapp.message_handling_mode
|
|
423
|
-
FlowChat::Config.whatsapp.message_handling_mode = :background
|
|
424
|
-
|
|
425
|
-
messages = 10.times.map do |i|
|
|
426
|
-
create_whatsapp_message_payload("user#{i}")
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
assert_enqueued_jobs 10 do
|
|
430
|
-
messages.each do |msg|
|
|
431
|
-
post "/whatsapp/webhook", params: msg
|
|
432
|
-
end
|
|
433
|
-
end
|
|
434
|
-
ensure
|
|
435
|
-
FlowChat::Config.whatsapp.message_handling_mode = original_mode
|
|
436
|
-
end
|
|
437
|
-
```
|
|
329
|
+
### Best Practices
|
|
438
330
|
|
|
439
|
-
|
|
331
|
+
#### Security
|
|
440
332
|
|
|
441
|
-
|
|
333
|
+
- Always set `FlowChat::Config.simulator_secret` in production
|
|
334
|
+
- Use secure cookies over HTTPS in production
|
|
335
|
+
- Restrict simulator access to development/staging environments
|
|
442
336
|
|
|
443
|
-
|
|
444
|
-
# config/environments/test.rb
|
|
445
|
-
config.log_level = :debug
|
|
337
|
+
#### Testing Strategy
|
|
446
338
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
339
|
+
1. **Start with Simulator**: Test flows before gateway integration
|
|
340
|
+
2. **Test All Platforms**: Verify behavior across USSD, WhatsApp, HTTP
|
|
341
|
+
3. **Check Character Limits**: Test USSD character restrictions
|
|
342
|
+
4. **Verify Session Management**: Test session boundaries and persistence
|
|
343
|
+
5. **Monitor Request Logs**: Debug integration issues in real-time
|
|
344
|
+
|
|
345
|
+
#### Performance
|
|
346
|
+
|
|
347
|
+
- Use simulator for rapid prototyping and debugging
|
|
348
|
+
- Test edge cases and error conditions safely
|
|
349
|
+
- Validate webhook payloads before production deployment
|
|
350
|
+
|
|
351
|
+
The FlowChat simulator provides a complete testing environment that accurately simulates real-world platform behavior while offering comprehensive debugging capabilities for efficient development and testing workflows.
|
|
450
352
|
|
|
451
|
-
### Inspect Flow State
|
|
452
353
|
|
|
453
|
-
```ruby
|
|
454
|
-
test "debug flow execution" do
|
|
455
|
-
context = FlowChat::Context.new
|
|
456
|
-
context.session = FlowChat::Session::CacheSessionStore.new
|
|
457
|
-
context.session.init_session("debug_session")
|
|
458
|
-
|
|
459
|
-
# Add debugging
|
|
460
|
-
context.input = "test@example.com"
|
|
461
|
-
app = FlowChat::Ussd::App.new(context)
|
|
462
|
-
|
|
463
|
-
flow = RegistrationFlow.new(app)
|
|
464
|
-
|
|
465
|
-
# Inspect state before execution
|
|
466
|
-
puts "Session before: #{context.session.data}"
|
|
467
|
-
|
|
468
|
-
begin
|
|
469
|
-
flow.main_page
|
|
470
|
-
rescue FlowChat::Interrupt::Input => e
|
|
471
|
-
puts "Flow interrupted with: #{e.prompt}"
|
|
472
|
-
puts "Session after: #{context.session.data}"
|
|
473
|
-
end
|
|
474
|
-
end
|
|
475
|
-
```
|