ssh-hull 1.0.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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +44 -0
  3. data/.gitignore +27 -0
  4. data/.rubocop.yml +62 -0
  5. data/.tool-versions +1 -0
  6. data/Gemfile +5 -0
  7. data/Gemfile.lock +142 -0
  8. data/LICENSE +19 -0
  9. data/README.md +12 -0
  10. data/Rakefile +7 -0
  11. data/bin/bundle +114 -0
  12. data/bin/rake +29 -0
  13. data/bin/rspec +29 -0
  14. data/bin/rubocop +29 -0
  15. data/bomb +12 -0
  16. data/config/locales/en.yml +71 -0
  17. data/config/locales/fr.yml +71 -0
  18. data/exe/ssh-hull +12 -0
  19. data/exe/ssh-tunnel +1 -0
  20. data/lib/ssh-hull/cli.rb +113 -0
  21. data/lib/ssh-hull/logger.rb +6 -0
  22. data/lib/ssh-hull/ui/application.rb +47 -0
  23. data/lib/ssh-hull/ui/forms/application_form.rb +69 -0
  24. data/lib/ssh-hull/ui/forms/host_form.rb +27 -0
  25. data/lib/ssh-hull/ui/forms/tunnel_form.rb +31 -0
  26. data/lib/ssh-hull/ui/helpers/application_window_helper.rb +233 -0
  27. data/lib/ssh-hull/ui/helpers/common/form_helper.rb +169 -0
  28. data/lib/ssh-hull/ui/helpers/common/minimize_helper.rb +46 -0
  29. data/lib/ssh-hull/ui/helpers/common/modal_helper.rb +43 -0
  30. data/lib/ssh-hull/ui/helpers/common/toolbar_helper.rb +106 -0
  31. data/lib/ssh-hull/ui/helpers/common/translation_helper.rb +18 -0
  32. data/lib/ssh-hull/ui/helpers/common/tree_view_helper.rb +40 -0
  33. data/lib/ssh-hull/ui/helpers/host_window_helper.rb +230 -0
  34. data/lib/ssh-hull/ui/helpers/tunnel_window_helper.rb +96 -0
  35. data/lib/ssh-hull/ui/models/config.rb +82 -0
  36. data/lib/ssh-hull/ui/models/host.rb +90 -0
  37. data/lib/ssh-hull/ui/models/tunnel.rb +118 -0
  38. data/lib/ssh-hull/ui/status_icon.rb +45 -0
  39. data/lib/ssh-hull/ui/windows/about_window.rb +32 -0
  40. data/lib/ssh-hull/ui/windows/application_window.rb +42 -0
  41. data/lib/ssh-hull/ui/windows/hosts/delete_window.rb +56 -0
  42. data/lib/ssh-hull/ui/windows/hosts/edit_window.rb +39 -0
  43. data/lib/ssh-hull/ui/windows/hosts/new_window.rb +45 -0
  44. data/lib/ssh-hull/ui/windows/tunnels/delete_window.rb +57 -0
  45. data/lib/ssh-hull/ui/windows/tunnels/edit_window.rb +39 -0
  46. data/lib/ssh-hull/ui/windows/tunnels/new_window.rb +45 -0
  47. data/lib/ssh-hull/version.rb +17 -0
  48. data/lib/ssh-tunnel.rb +94 -0
  49. data/resources/gresources.xml +13 -0
  50. data/resources/ui/about_window.glade +48 -0
  51. data/resources/ui/application_window.glade +196 -0
  52. data/resources/ui/hosts/delete_window.glade +74 -0
  53. data/resources/ui/hosts/edit_window.glade +331 -0
  54. data/resources/ui/hosts/new_window.glade +328 -0
  55. data/resources/ui/tunnels/delete_window.glade +73 -0
  56. data/resources/ui/tunnels/edit_window.glade +305 -0
  57. data/resources/ui/tunnels/new_window.glade +305 -0
  58. data/snap/snapcraft.yaml +48 -0
  59. data/spec/factories/host.rb +22 -0
  60. data/spec/factories/tunnel.rb +16 -0
  61. data/spec/spec_helper.rb +31 -0
  62. data/spec/ssh_tunnel/ui/forms/host_form_spec.rb +103 -0
  63. data/spec/ssh_tunnel/ui/forms/tunnel_form_spec.rb +132 -0
  64. data/spec/ssh_tunnel/ui/models/host_spec.rb +116 -0
  65. data/spec/ssh_tunnel/ui/models/tunnel_spec.rb +43 -0
  66. data/spec/ssh_tunnel_spec.rb +45 -0
  67. data/ssh-hull.gemspec +38 -0
  68. metadata +320 -0
@@ -0,0 +1,305 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Generated with glade 3.38.2 -->
3
+ <interface>
4
+ <requires lib="gtk+" version="3.18"/>
5
+ <template class="SSHTunnelUIWindowsTunnelsNewWindow" parent="GtkWindow">
6
+ <property name="visible">True</property>
7
+ <property name="can-focus">False</property>
8
+ <property name="has-focus">True</property>
9
+ <property name="is-focus">True</property>
10
+ <property name="type">popup</property>
11
+ <property name="modal">True</property>
12
+ <property name="window-position">center</property>
13
+ <property name="type-hint">dialog</property>
14
+ <property name="skip-taskbar-hint">True</property>
15
+ <property name="gravity">center</property>
16
+ <child>
17
+ <object class="GtkBox">
18
+ <property name="visible">True</property>
19
+ <property name="can-focus">False</property>
20
+ <property name="orientation">vertical</property>
21
+ <child>
22
+ <!-- n-columns=3 n-rows=7 -->
23
+ <object class="GtkGrid">
24
+ <property name="visible">True</property>
25
+ <property name="can-focus">False</property>
26
+ <child>
27
+ <object class="GtkLabel" id="label_name">
28
+ <property name="visible">True</property>
29
+ <property name="can-focus">False</property>
30
+ <property name="halign">start</property>
31
+ <property name="margin-start">10</property>
32
+ <property name="margin-end">5</property>
33
+ <property name="margin-top">10</property>
34
+ <property name="margin-bottom">5</property>
35
+ <property name="label" translatable="yes">Name</property>
36
+ </object>
37
+ <packing>
38
+ <property name="left-attach">0</property>
39
+ <property name="top-attach">0</property>
40
+ </packing>
41
+ </child>
42
+ <child>
43
+ <object class="GtkEntry" id="input_name">
44
+ <property name="visible">True</property>
45
+ <property name="can-focus">True</property>
46
+ <property name="margin-end">10</property>
47
+ <property name="margin-top">10</property>
48
+ <property name="margin-bottom">5</property>
49
+ <property name="hexpand">True</property>
50
+ </object>
51
+ <packing>
52
+ <property name="left-attach">1</property>
53
+ <property name="top-attach">0</property>
54
+ </packing>
55
+ </child>
56
+ <child>
57
+ <object class="GtkLabel" id="label_local_host">
58
+ <property name="visible">True</property>
59
+ <property name="can-focus">False</property>
60
+ <property name="halign">start</property>
61
+ <property name="margin-start">10</property>
62
+ <property name="margin-end">5</property>
63
+ <property name="margin-top">5</property>
64
+ <property name="margin-bottom">5</property>
65
+ <property name="label" translatable="yes">Local Host</property>
66
+ </object>
67
+ <packing>
68
+ <property name="left-attach">0</property>
69
+ <property name="top-attach">2</property>
70
+ </packing>
71
+ </child>
72
+ <child>
73
+ <object class="GtkLabel" id="label_local_port">
74
+ <property name="visible">True</property>
75
+ <property name="can-focus">False</property>
76
+ <property name="halign">start</property>
77
+ <property name="margin-start">10</property>
78
+ <property name="margin-end">5</property>
79
+ <property name="margin-top">5</property>
80
+ <property name="margin-bottom">5</property>
81
+ <property name="label" translatable="yes">Local Port</property>
82
+ </object>
83
+ <packing>
84
+ <property name="left-attach">0</property>
85
+ <property name="top-attach">3</property>
86
+ </packing>
87
+ </child>
88
+ <child>
89
+ <object class="GtkLabel" id="label_remote_host">
90
+ <property name="visible">True</property>
91
+ <property name="can-focus">False</property>
92
+ <property name="halign">start</property>
93
+ <property name="margin-start">10</property>
94
+ <property name="margin-end">5</property>
95
+ <property name="margin-top">5</property>
96
+ <property name="margin-bottom">5</property>
97
+ <property name="label" translatable="yes">Remote Host</property>
98
+ </object>
99
+ <packing>
100
+ <property name="left-attach">0</property>
101
+ <property name="top-attach">4</property>
102
+ </packing>
103
+ </child>
104
+ <child>
105
+ <object class="GtkLabel" id="label_remote_port">
106
+ <property name="visible">True</property>
107
+ <property name="can-focus">False</property>
108
+ <property name="halign">start</property>
109
+ <property name="margin-start">10</property>
110
+ <property name="margin-end">5</property>
111
+ <property name="margin-top">5</property>
112
+ <property name="margin-bottom">5</property>
113
+ <property name="label" translatable="yes">Remote Port</property>
114
+ </object>
115
+ <packing>
116
+ <property name="left-attach">0</property>
117
+ <property name="top-attach">5</property>
118
+ </packing>
119
+ </child>
120
+ <child>
121
+ <object class="GtkEntry" id="input_local_host">
122
+ <property name="visible">True</property>
123
+ <property name="can-focus">True</property>
124
+ <property name="margin-end">10</property>
125
+ <property name="margin-top">5</property>
126
+ <property name="margin-bottom">5</property>
127
+ <property name="hexpand">True</property>
128
+ </object>
129
+ <packing>
130
+ <property name="left-attach">1</property>
131
+ <property name="top-attach">2</property>
132
+ </packing>
133
+ </child>
134
+ <child>
135
+ <object class="GtkEntry" id="input_local_port">
136
+ <property name="visible">True</property>
137
+ <property name="can-focus">True</property>
138
+ <property name="margin-end">10</property>
139
+ <property name="margin-top">5</property>
140
+ <property name="margin-bottom">5</property>
141
+ <property name="hexpand">True</property>
142
+ </object>
143
+ <packing>
144
+ <property name="left-attach">1</property>
145
+ <property name="top-attach">3</property>
146
+ </packing>
147
+ </child>
148
+ <child>
149
+ <object class="GtkEntry" id="input_remote_host">
150
+ <property name="visible">True</property>
151
+ <property name="can-focus">True</property>
152
+ <property name="margin-end">10</property>
153
+ <property name="margin-top">5</property>
154
+ <property name="margin-bottom">5</property>
155
+ <property name="hexpand">True</property>
156
+ </object>
157
+ <packing>
158
+ <property name="left-attach">1</property>
159
+ <property name="top-attach">4</property>
160
+ </packing>
161
+ </child>
162
+ <child>
163
+ <object class="GtkEntry" id="input_remote_port">
164
+ <property name="visible">True</property>
165
+ <property name="can-focus">True</property>
166
+ <property name="margin-end">10</property>
167
+ <property name="margin-top">5</property>
168
+ <property name="margin-bottom">5</property>
169
+ <property name="hexpand">True</property>
170
+ </object>
171
+ <packing>
172
+ <property name="left-attach">1</property>
173
+ <property name="top-attach">5</property>
174
+ </packing>
175
+ </child>
176
+ <child>
177
+ <object class="GtkLabel" id="label_type">
178
+ <property name="visible">True</property>
179
+ <property name="can-focus">False</property>
180
+ <property name="halign">start</property>
181
+ <property name="margin-start">10</property>
182
+ <property name="margin-end">5</property>
183
+ <property name="margin-top">5</property>
184
+ <property name="margin-bottom">5</property>
185
+ <property name="label" translatable="yes">Type</property>
186
+ </object>
187
+ <packing>
188
+ <property name="left-attach">0</property>
189
+ <property name="top-attach">1</property>
190
+ </packing>
191
+ </child>
192
+ <child>
193
+ <object class="GtkComboBoxText" id="input_type">
194
+ <property name="visible">True</property>
195
+ <property name="can-focus">False</property>
196
+ <property name="margin-end">10</property>
197
+ <property name="margin-top">5</property>
198
+ <property name="margin-bottom">5</property>
199
+ </object>
200
+ <packing>
201
+ <property name="left-attach">1</property>
202
+ <property name="top-attach">1</property>
203
+ </packing>
204
+ </child>
205
+ <child>
206
+ <object class="GtkLabel" id="label_auto_start">
207
+ <property name="visible">True</property>
208
+ <property name="can-focus">False</property>
209
+ <property name="halign">start</property>
210
+ <property name="margin-left">10</property>
211
+ <property name="margin-right">5</property>
212
+ <property name="margin-start">10</property>
213
+ <property name="margin-end">5</property>
214
+ <property name="margin-top">5</property>
215
+ <property name="margin-bottom">10</property>
216
+ <property name="label" translatable="yes">Auto Start</property>
217
+ </object>
218
+ <packing>
219
+ <property name="left-attach">0</property>
220
+ <property name="top-attach">6</property>
221
+ </packing>
222
+ </child>
223
+ <child>
224
+ <object class="GtkCheckButton" id="input_auto_start">
225
+ <property name="visible">True</property>
226
+ <property name="can-focus">True</property>
227
+ <property name="receives-default">False</property>
228
+ <property name="margin-top">5</property>
229
+ <property name="margin-bottom">10</property>
230
+ <property name="draw-indicator">True</property>
231
+ </object>
232
+ <packing>
233
+ <property name="left-attach">1</property>
234
+ <property name="top-attach">6</property>
235
+ </packing>
236
+ </child>
237
+ <child>
238
+ <placeholder/>
239
+ </child>
240
+ <child>
241
+ <placeholder/>
242
+ </child>
243
+ <child>
244
+ <placeholder/>
245
+ </child>
246
+ <child>
247
+ <placeholder/>
248
+ </child>
249
+ <child>
250
+ <placeholder/>
251
+ </child>
252
+ <child>
253
+ <placeholder/>
254
+ </child>
255
+ <child>
256
+ <placeholder/>
257
+ </child>
258
+ </object>
259
+ <packing>
260
+ <property name="expand">True</property>
261
+ <property name="fill">True</property>
262
+ <property name="position">0</property>
263
+ </packing>
264
+ </child>
265
+ <child>
266
+ <object class="GtkActionBar">
267
+ <property name="visible">True</property>
268
+ <property name="can-focus">False</property>
269
+ <child>
270
+ <object class="GtkButton" id="button_cancel">
271
+ <property name="label">gtk-cancel</property>
272
+ <property name="visible">True</property>
273
+ <property name="can-focus">True</property>
274
+ <property name="receives-default">True</property>
275
+ <property name="use-stock">True</property>
276
+ </object>
277
+ <packing>
278
+ <property name="pack-type">end</property>
279
+ <property name="position">0</property>
280
+ </packing>
281
+ </child>
282
+ <child>
283
+ <object class="GtkButton" id="button_submit">
284
+ <property name="label">gtk-apply</property>
285
+ <property name="visible">True</property>
286
+ <property name="can-focus">True</property>
287
+ <property name="receives-default">True</property>
288
+ <property name="use-stock">True</property>
289
+ </object>
290
+ <packing>
291
+ <property name="pack-type">end</property>
292
+ <property name="position">0</property>
293
+ </packing>
294
+ </child>
295
+ </object>
296
+ <packing>
297
+ <property name="expand">False</property>
298
+ <property name="fill">True</property>
299
+ <property name="position">1</property>
300
+ </packing>
301
+ </child>
302
+ </object>
303
+ </child>
304
+ </template>
305
+ </interface>
@@ -0,0 +1,48 @@
1
+ name: ssh-hull
2
+ version: '1.0.0'
3
+ grade: devel
4
+ confinement: devmode
5
+ summary: SSH Tunnel Manager
6
+ description: |
7
+ A Ruby/GTK3 application to manage ssh hulls.
8
+
9
+ apps:
10
+ ssh-hull:
11
+ command: desktop-launch $SNAP/bin/ssh-hull
12
+ plugs:
13
+ - home
14
+ - desktop
15
+ - desktop-legacy
16
+ - network
17
+ - x11
18
+
19
+ parts:
20
+ ssh-hull:
21
+ source: https://github.com/ungtb10d/ssh-hull.git
22
+ source-type: git
23
+ plugin: ruby
24
+ ruby-version: 2.5.5
25
+ after:
26
+ - desktop-gtk3
27
+ build-packages:
28
+ - build-essential
29
+ - libatk1.0-dev
30
+ - libgirepository1.0-dev
31
+ - libpango1.0-dev
32
+ - libgtk-3-dev
33
+ stage-packages:
34
+ - gobject-introspection
35
+ - gir1.2-atk-1.0
36
+ - gir1.2-atspi-2.0
37
+ - gir1.2-freedesktop
38
+ - gir1.2-gdkpixbuf-2.0
39
+ - gir1.2-glib-2.0
40
+ - gir1.2-gtk-3.0
41
+ - gir1.2-pango-1.0
42
+ - adwaita-icon-theme
43
+ - libcanberra-gtk3-0
44
+ override-build: |
45
+ snapcraftctl build
46
+ git checkout snap/snapcraft.yaml
47
+ gem build ssh-hull.gemspec
48
+ gem install ssh-hull-*.gem --env-shebang
@@ -0,0 +1,22 @@
1
+ FactoryBot.define do
2
+ factory :host, class: 'SSHTunnel::UI::Models::Host' do
3
+
4
+ uuid { '7be48819-97bf-49fc-98af-4f7f096e1977' }
5
+ name { 'foo' }
6
+ user { 'root' }
7
+ host { 'host.example.net' }
8
+ port { 22 }
9
+
10
+ factory :host_with_one_tunnel do
11
+ after(:build) do |object, _evaluator|
12
+ object.tunnels = [build(:tunnel, parent: object)]
13
+ end
14
+ end
15
+
16
+ factory :host_with_two_tunnels do
17
+ after(:build) do |object, _evaluator|
18
+ object.tunnels = [build(:tunnel, name: 'zzz', parent: object), build(:tunnel, name: 'aaa', parent: object)]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ FactoryBot.define do
2
+ factory :tunnel, class: 'SSHTunnel::UI::Models::Tunnel' do
3
+
4
+ uuid { '117aacde-5adf-4a6d-a0f9-5acdddf4a5b9' }
5
+ name { 'mysql' }
6
+ type { 'local' }
7
+ local_host { '127.0.0.1' }
8
+ local_port { 10000 }
9
+ remote_host { '127.0.0.1' }
10
+ remote_port { 3306 }
11
+
12
+ factory :tunnel_wih_parent do
13
+ parent { build(:host) }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ require 'simplecov'
2
+ require 'rspec'
3
+ require 'factory_bot'
4
+
5
+ # Start Simplecov
6
+ SimpleCov.start do
7
+ add_filter 'spec/'
8
+ end
9
+
10
+ # Configure RSpec
11
+ RSpec.configure do |config|
12
+ config.color = true
13
+ config.fail_fast = false
14
+
15
+ config.order = :random
16
+ Kernel.srand config.seed
17
+
18
+ config.expect_with :rspec do |c|
19
+ c.syntax = :expect
20
+ end
21
+
22
+ # disable monkey patching
23
+ # see: https://relishapp.com/rspec/rspec-core/v/3-8/docs/configuration/zero-monkey-patching-mode
24
+ config.disable_monkey_patching!
25
+
26
+ config.before(:suite) do
27
+ FactoryBot.find_definitions
28
+ end
29
+ end
30
+
31
+ require 'ssh-hull'
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SSHTunnel::UI::Forms::HostForm do
4
+
5
+ let(:host) { FactoryBot.build(:host) }
6
+ let(:form) { described_class.new(host) }
7
+ let(:params) { { name: 'My Host', user: 'root', host: 'foo.bar', port: 22 } }
8
+
9
+ describe 'form' do
10
+ it 'has attributes' do
11
+ expect(form.name).to be nil
12
+ expect(form.user).to be nil
13
+ expect(form.host).to be nil
14
+ expect(form.port).to be nil
15
+ end
16
+ end
17
+
18
+ describe '#submit' do
19
+ it 'assigns attributes value' do
20
+ form.submit(params)
21
+ expect(form.name).to eq 'My Host'
22
+ expect(form.user).to eq 'root'
23
+ expect(form.host).to eq 'foo.bar'
24
+ expect(form.port).to eq 22
25
+ end
26
+ end
27
+
28
+ describe '#valid?' do
29
+ context 'with valid params' do
30
+ it 'returns true' do
31
+ form.submit(params)
32
+ expect(form.valid?).to be true
33
+ end
34
+ end
35
+
36
+ context 'with invalid params' do
37
+ let(:params) { {} }
38
+
39
+ it 'returns false' do
40
+ form.submit(params)
41
+ expect(form.valid?).to be false
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#save' do
47
+ it 'should save value on model' do
48
+ expect(host.uuid).to eq '7be48819-97bf-49fc-98af-4f7f096e1977'
49
+ expect(host.name).to eq 'foo'
50
+ expect(host.user).to eq 'root'
51
+ expect(host.host).to eq 'host.example.net'
52
+ expect(host.port).to eq "22"
53
+
54
+ form.submit(params)
55
+ form.save
56
+
57
+ expect(host.uuid).to eq '7be48819-97bf-49fc-98af-4f7f096e1977'
58
+ expect(host.name).to eq 'My Host'
59
+ expect(host.user).to eq 'root'
60
+ expect(host.host).to eq 'foo.bar'
61
+ expect(host.port).to eq "22"
62
+ end
63
+ end
64
+
65
+ describe 'valid form' do
66
+ it 'should cast port to Integer' do
67
+ params = { name: 'My Host', user: 'root', host: 'foo.bar', port: '22' }
68
+ form.submit(params)
69
+ expect(form.valid?).to be true
70
+ expect(form.port).to eq 22
71
+ end
72
+ end
73
+
74
+ describe 'invalid form' do
75
+ it 'should have a name' do
76
+ params = { name: '', user: 'root', host: 'foo.bar', port: 22 }
77
+ form.submit(params)
78
+ expect(form.valid?).to be false
79
+ expect(form.errors.messages_for(:name)).to eq ["can't be blank"]
80
+ end
81
+
82
+ it 'should have a user' do
83
+ params = { name: 'My Host', user: '', host: 'foo.bar', port: 22 }
84
+ form.submit(params)
85
+ expect(form.valid?).to be false
86
+ expect(form.errors.messages_for(:user)).to eq ["can't be blank"]
87
+ end
88
+
89
+ it 'should have a host' do
90
+ params = { name: 'My Host', user: 'root', host: '', port: 22 }
91
+ form.submit(params)
92
+ expect(form.valid?).to be false
93
+ expect(form.errors.messages_for(:host)).to eq ["can't be blank"]
94
+ end
95
+
96
+ it 'should have a port' do
97
+ params = { name: 'My Host', user: 'root', host: 'foo.bar', port: '' }
98
+ form.submit(params)
99
+ expect(form.valid?).to be false
100
+ expect(form.errors.messages_for(:port)).to eq ["can't be blank", "is not included in the list"]
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SSHTunnel::UI::Forms::TunnelForm do
4
+
5
+ let(:tunnel) { FactoryBot.build(:tunnel_wih_parent) }
6
+ let(:form) { described_class.new(tunnel) }
7
+ let(:params) { { name: 'My Tunnel', type: 'remote', local_host: '127.0.0.1', local_port: 5000, remote_host: '127.0.0.1', remote_port: 6379 } }
8
+
9
+ describe 'form' do
10
+ it 'has attributes' do
11
+ expect(form.name).to be nil
12
+ expect(form.type).to be nil
13
+ expect(form.local_host).to be nil
14
+ expect(form.local_port).to be nil
15
+ expect(form.remote_host).to be nil
16
+ expect(form.remote_port).to be nil
17
+ end
18
+ end
19
+
20
+ describe '#submit' do
21
+ it 'assigns attributes value' do
22
+ form.submit(params)
23
+ expect(form.name).to eq 'My Tunnel'
24
+ expect(form.type).to eq 'remote'
25
+ expect(form.local_host).to eq '127.0.0.1'
26
+ expect(form.local_port).to eq 5000
27
+ expect(form.remote_host).to eq '127.0.0.1'
28
+ expect(form.remote_port).to eq 6379
29
+ end
30
+ end
31
+
32
+ describe '#valid?' do
33
+ context 'with valid params' do
34
+ it 'returns true' do
35
+ form.submit(params)
36
+ expect(form.valid?).to be true
37
+ end
38
+ end
39
+
40
+ context 'with invalid params' do
41
+ let(:params) { {} }
42
+
43
+ it 'returns false' do
44
+ form.submit(params)
45
+ expect(form.valid?).to be false
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '#save' do
51
+ it 'should save value on model' do
52
+ expect(tunnel.uuid).to eq '117aacde-5adf-4a6d-a0f9-5acdddf4a5b9'
53
+ expect(tunnel.name).to eq 'mysql'
54
+ expect(tunnel.type).to eq 'local'
55
+ expect(tunnel.local_host).to eq '127.0.0.1'
56
+ expect(tunnel.local_port).to eq "10000"
57
+ expect(tunnel.remote_host).to eq '127.0.0.1'
58
+ expect(tunnel.remote_port).to eq "3306"
59
+
60
+ form.submit(params)
61
+ form.save
62
+
63
+ expect(tunnel.uuid).to eq '117aacde-5adf-4a6d-a0f9-5acdddf4a5b9'
64
+ expect(tunnel.name).to eq 'My Tunnel'
65
+ expect(tunnel.type).to eq 'remote'
66
+ expect(tunnel.local_host).to eq '127.0.0.1'
67
+ expect(tunnel.local_port).to eq "5000"
68
+ expect(tunnel.remote_host).to eq '127.0.0.1'
69
+ expect(tunnel.remote_port).to eq "6379"
70
+ end
71
+ end
72
+
73
+ describe 'valid form' do
74
+ it 'should cast local_port to Integer' do
75
+ params = { name: 'My Tunnel', type: 'local', local_host: '127.0.0.1', local_port: '5000', remote_host: '127.0.0.1', remote_port: 3306 }
76
+ form.submit(params)
77
+ expect(form.valid?).to be true
78
+ expect(form.local_port).to eq 5000
79
+ end
80
+
81
+ it 'should cast remote_port to Integer' do
82
+ params = { name: 'My Tunnel', type: 'local', local_host: '127.0.0.1', local_port: 5000, remote_host: '127.0.0.1', remote_port: '3306' }
83
+ form.submit(params)
84
+ expect(form.valid?).to be true
85
+ expect(form.remote_port).to eq 3306
86
+ end
87
+ end
88
+
89
+ describe 'invalid form' do
90
+ it 'should have a name' do
91
+ params = { name: '', type: 'local', local_host: '127.0.0.1', local_port: 5000, remote_host: '127.0.0.1', remote_port: 3306 }
92
+ form.submit(params)
93
+ expect(form.valid?).to be false
94
+ expect(form.errors.messages_for(:name)).to eq ["can't be blank"]
95
+ end
96
+
97
+ it 'should have a type' do
98
+ params = { name: 'My Tunnel', type: '', local_host: '127.0.0.1', local_port: 5000, remote_host: '127.0.0.1', remote_port: 3306 }
99
+ form.submit(params)
100
+ expect(form.valid?).to be false
101
+ expect(form.errors.messages_for(:type)).to eq ["can't be blank"]
102
+ end
103
+
104
+ it 'should have a local_host' do
105
+ params = { name: 'My Tunnel', type: 'local', local_host: '', local_port: 5000, remote_host: '127.0.0.1', remote_port: 3306 }
106
+ form.submit(params)
107
+ expect(form.valid?).to be false
108
+ expect(form.errors.messages_for(:local_host)).to eq ["can't be blank"]
109
+ end
110
+
111
+ it 'should have a local_port' do
112
+ params = { name: 'My Tunnel', type: 'local', local_host: '127.0.0.1', local_port: '', remote_host: '127.0.0.1', remote_port: 3306 }
113
+ form.submit(params)
114
+ expect(form.valid?).to be false
115
+ expect(form.errors.messages_for(:local_port)).to eq ["can't be blank", "is not included in the list"]
116
+ end
117
+
118
+ it 'should have a remote_host' do
119
+ params = { name: 'My Tunnel', type: 'local', local_host: '127.0.0.1', local_port: 5000, remote_host: '', remote_port: 3306 }
120
+ form.submit(params)
121
+ expect(form.valid?).to be false
122
+ expect(form.errors.messages_for(:remote_host)).to eq ["can't be blank"]
123
+ end
124
+
125
+ it 'should have a remote_port' do
126
+ params = { name: 'My Tunnel', type: 'local', local_host: '127.0.0.1', local_port: 5000, remote_host: '127.0.0.1', remote_port: '' }
127
+ form.submit(params)
128
+ expect(form.valid?).to be false
129
+ expect(form.errors.messages_for(:remote_port)).to eq ["can't be blank", "is not included in the list"]
130
+ end
131
+ end
132
+ end