ssh-hull 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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