oxidized-web 0.15.1 → 0.17.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.

Potentially problematic release.


This version of oxidized-web might be problematic. Click here for more details.

Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.rubocop.yml +22 -2
  4. data/CHANGELOG.md +30 -1
  5. data/README.md +13 -3
  6. data/Rakefile +9 -4
  7. data/docs/configuration.md +90 -0
  8. data/docs/development.md +42 -39
  9. data/lib/oxidized/web/public/scripts/oxidized.js +13 -13
  10. data/lib/oxidized/web/public/weblibs/bootstrap-icons.css +31 -3
  11. data/lib/oxidized/web/public/weblibs/bootstrap.bundle.js +20 -19
  12. data/lib/oxidized/web/public/weblibs/bootstrap.bundle.js.map +1 -1
  13. data/lib/oxidized/web/public/weblibs/bootstrap.css +110 -124
  14. data/lib/oxidized/web/public/weblibs/bootstrap.css.map +1 -1
  15. data/lib/oxidized/web/public/weblibs/bootstrap.js +20 -17
  16. data/lib/oxidized/web/public/weblibs/bootstrap.js.map +1 -1
  17. data/lib/oxidized/web/public/weblibs/buttons.bootstrap5.css +3 -3
  18. data/lib/oxidized/web/public/weblibs/buttons.colVis.js +14 -5
  19. data/lib/oxidized/web/public/weblibs/dataTables.bootstrap5.css +111 -17
  20. data/lib/oxidized/web/public/weblibs/dataTables.buttons.js +25 -7
  21. data/lib/oxidized/web/public/weblibs/dataTables.js +336 -106
  22. data/lib/oxidized/web/public/weblibs/dayjs-plugin-utc.min.js +1 -0
  23. data/lib/oxidized/web/public/weblibs/dayjs.min.js +1 -0
  24. data/lib/oxidized/web/public/weblibs/fonts/bootstrap-icons.woff +0 -0
  25. data/lib/oxidized/web/public/weblibs/fonts/bootstrap-icons.woff2 +0 -0
  26. data/lib/oxidized/web/version.rb +1 -1
  27. data/lib/oxidized/web/views/conf_search.haml +1 -1
  28. data/lib/oxidized/web/views/diffs.haml +6 -7
  29. data/lib/oxidized/web/views/head.haml +4 -0
  30. data/lib/oxidized/web/views/node.haml +3 -2
  31. data/lib/oxidized/web/views/nodes.haml +13 -5
  32. data/lib/oxidized/web/views/stats.haml +11 -3
  33. data/lib/oxidized/web/views/version.haml +1 -2
  34. data/lib/oxidized/web/views/versions.haml +11 -7
  35. data/lib/oxidized/web/webapp.rb +41 -29
  36. data/lib/oxidized/web.rb +72 -16
  37. data/oxidized-web.gemspec +22 -13
  38. data/package-lock.json +37 -25
  39. data/package.json +7 -5
  40. data/spec/spec_helper.rb +1 -0
  41. data/spec/web/node/show_spec.rb +100 -0
  42. data/spec/web/node/version_spec.rb +161 -0
  43. data/spec/{node_spec.rb → web/node_spec.rb} +1 -1
  44. data/spec/{nodes_spec.rb → web/nodes_spec.rb} +1 -1
  45. data/spec/{root_spec.rb → web/root_spec.rb} +1 -1
  46. data/spec/{webapp_spec.rb → web/webapp_spec.rb} +1 -1
  47. data/spec/web_spec.rb +98 -0
  48. metadata +69 -69
  49. data/.rubocop_todo.yml +0 -64
  50. data/spec/node_version_spec.rb +0 -102
data/package-lock.json CHANGED
@@ -9,11 +9,13 @@
9
9
  "version": "1.0.0",
10
10
  "license": "Apache 2.0",
11
11
  "dependencies": {
12
- "bootstrap": "^5.3.3",
13
- "bootstrap-icons": "^1.11.3",
14
- "datatables.net-bs5": "^2.0.7",
15
- "datatables.net-buttons-bs5": "^3.0.2",
16
- "jquery": "^3.7.1"
12
+ "bootstrap": "~5.3.3",
13
+ "bootstrap-icons": "~1.13.1",
14
+ "datatables.net-bs5": "~2.3.2",
15
+ "datatables.net-buttons-bs5": "~3.2.4",
16
+ "dayjs": "~1.11.13",
17
+ "dayjs-plugin-utc": "~0.1.2",
18
+ "jquery": "~3.7.1"
17
19
  }
18
20
  },
19
21
  "node_modules/@popperjs/core": {
@@ -27,9 +29,9 @@
27
29
  }
28
30
  },
29
31
  "node_modules/bootstrap": {
30
- "version": "5.3.3",
31
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
32
- "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
32
+ "version": "5.3.7",
33
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz",
34
+ "integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==",
33
35
  "funding": [
34
36
  {
35
37
  "type": "github",
@@ -45,9 +47,9 @@
45
47
  }
46
48
  },
47
49
  "node_modules/bootstrap-icons": {
48
- "version": "1.11.3",
49
- "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
50
- "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
50
+ "version": "1.13.1",
51
+ "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz",
52
+ "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==",
51
53
  "funding": [
52
54
  {
53
55
  "type": "github",
@@ -60,41 +62,51 @@
60
62
  ]
61
63
  },
62
64
  "node_modules/datatables.net": {
63
- "version": "2.2.2",
64
- "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.2.2.tgz",
65
- "integrity": "sha512-gfODIKE3gpgbVeZy2QGj2Dq9roO6hy00S+k1knklrqlMyAMrh1wt0Q6ryBUM7gU96U77ysbq8dYhxFdmcC/oPQ==",
65
+ "version": "2.3.2",
66
+ "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.3.2.tgz",
67
+ "integrity": "sha512-31TzwIQM0+pr2ZOEOEH6dsHd/WSAl5GDDGPezOHPI3mM2NK4lcDyOoG8xXeWmSbVfbi852LNK5C84fpp4Q+qxg==",
66
68
  "dependencies": {
67
69
  "jquery": ">=1.7"
68
70
  }
69
71
  },
70
72
  "node_modules/datatables.net-bs5": {
71
- "version": "2.2.2",
72
- "resolved": "https://registry.npmjs.org/datatables.net-bs5/-/datatables.net-bs5-2.2.2.tgz",
73
- "integrity": "sha512-0mAbpUf0EpnIEc0RlN6vSrSk9y/+NuReiwDpjHYY3RfzdvH6Lt0+7Q9OU5RIbYxaFxES/z60thxdrw7IUFnBhw==",
73
+ "version": "2.3.2",
74
+ "resolved": "https://registry.npmjs.org/datatables.net-bs5/-/datatables.net-bs5-2.3.2.tgz",
75
+ "integrity": "sha512-1rh0ZTLoiziIQ4oAtgr+IOYVgJfAIceDnbDe535u8kv191pBAdTrKF6ovQO98Xy9mDXLdLNB7QCrLiV/sgPoQw==",
74
76
  "dependencies": {
75
- "datatables.net": "2.2.2",
77
+ "datatables.net": "2.3.2",
76
78
  "jquery": ">=1.7"
77
79
  }
78
80
  },
79
81
  "node_modules/datatables.net-buttons": {
80
- "version": "3.2.2",
81
- "resolved": "https://registry.npmjs.org/datatables.net-buttons/-/datatables.net-buttons-3.2.2.tgz",
82
- "integrity": "sha512-+aLTbkbksNmyGpK+8KXbpwYKXYOXvZQR2ySA/8oOQeJU53Xw/67cOHowenEr2d43/RLaz+I0zvV/1Yn+jMRiDw==",
82
+ "version": "3.2.4",
83
+ "resolved": "https://registry.npmjs.org/datatables.net-buttons/-/datatables.net-buttons-3.2.4.tgz",
84
+ "integrity": "sha512-anA39/R0kpHA2DOwqEHy/ZMXD5vf4tWmyNO0BnO0kJG7AFNvGTUCWBnBifXYg3G64U6JYpYY+MuTFKIB1/ZMTQ==",
83
85
  "dependencies": {
84
86
  "datatables.net": "^2",
85
87
  "jquery": ">=1.7"
86
88
  }
87
89
  },
88
90
  "node_modules/datatables.net-buttons-bs5": {
89
- "version": "3.2.2",
90
- "resolved": "https://registry.npmjs.org/datatables.net-buttons-bs5/-/datatables.net-buttons-bs5-3.2.2.tgz",
91
- "integrity": "sha512-xjUcbYCBHcUthD1pvo5ghTNjqE6fTMygRrKd0QjBHKQxcqxmHG/m0djD2s6cFBfm8oov132U7U2JCXgQifOoUA==",
91
+ "version": "3.2.4",
92
+ "resolved": "https://registry.npmjs.org/datatables.net-buttons-bs5/-/datatables.net-buttons-bs5-3.2.4.tgz",
93
+ "integrity": "sha512-yX8Ia32P1D9L02XWjisA6a9fFp0LgmzpIcvf/4ty+QG5/qj1tOOqWgsXsxxIY2Uj918WmLJS6VaJabRAMoAqHA==",
92
94
  "dependencies": {
93
95
  "datatables.net-bs5": "^2",
94
- "datatables.net-buttons": "3.2.2",
96
+ "datatables.net-buttons": "3.2.4",
95
97
  "jquery": ">=1.7"
96
98
  }
97
99
  },
100
+ "node_modules/dayjs": {
101
+ "version": "1.11.13",
102
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
103
+ "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
104
+ },
105
+ "node_modules/dayjs-plugin-utc": {
106
+ "version": "0.1.2",
107
+ "resolved": "https://registry.npmjs.org/dayjs-plugin-utc/-/dayjs-plugin-utc-0.1.2.tgz",
108
+ "integrity": "sha512-ExERH5o3oo6jFOdkvMP3gytTCQ9Ksi5PtylclJWghr7k7m3o2U5QrwtdiJkOxLOH4ghr0EKhpqGefzGz1VvVJg=="
109
+ },
98
110
  "node_modules/jquery": {
99
111
  "version": "3.7.1",
100
112
  "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
data/package.json CHANGED
@@ -12,10 +12,12 @@
12
12
  "author": "The oxidized project",
13
13
  "license": "Apache 2.0",
14
14
  "dependencies": {
15
- "bootstrap": "^5.3.3",
16
- "bootstrap-icons": "^1.11.3",
17
- "datatables.net-bs5": "^2.0.7",
18
- "datatables.net-buttons-bs5": "^3.0.2",
19
- "jquery": "^3.7.1"
15
+ "bootstrap": "~5.3.3",
16
+ "bootstrap-icons": "~1.13.1",
17
+ "datatables.net-bs5": "~2.3.2",
18
+ "datatables.net-buttons-bs5": "~3.2.4",
19
+ "dayjs": "~1.11.13",
20
+ "dayjs-plugin-utc": "~0.1.2",
21
+ "jquery": "~3.7.1"
20
22
  }
21
23
  }
data/spec/spec_helper.rb CHANGED
@@ -7,5 +7,6 @@ SimpleCov.start
7
7
  require 'minitest/autorun'
8
8
  require 'rack/test'
9
9
  require 'oxidized'
10
+ require 'oxidized/web'
10
11
  require 'oxidized/web/webapp'
11
12
  require 'mocha/minitest'
@@ -0,0 +1,100 @@
1
+ require_relative '../../spec_helper'
2
+ describe Oxidized::API::WebApp do
3
+ include Rack::Test::Methods
4
+
5
+ def app
6
+ Oxidized::API::WebApp
7
+ end
8
+
9
+ before do
10
+ @nodes = mock('Oxidized::Nodes')
11
+ app.set(:nodes, @nodes)
12
+
13
+ @serialized_node = {
14
+ name: "sw5",
15
+ full_name: "sw5.example.com",
16
+ ip: "10.42.12.42",
17
+ group: nil,
18
+ model: "ios",
19
+ last: {
20
+ start: Time.parse("2025-02-05 19:49:00 +0100"),
21
+ end: Time.parse("2025-02-05 19:49:10 +0100"),
22
+ status: :no_connection,
23
+ time: 10
24
+ },
25
+ vars: {
26
+ oxi: "dized",
27
+ enable: "secret_enable",
28
+ username: "oxidized",
29
+ password: "secret_password"
30
+ },
31
+ mtime: Time.parse("2025-02-05 19:49:11 +0100")
32
+ }
33
+ end
34
+
35
+ describe "get /node/show/:node" do
36
+ it "shows the metadata of a node" do
37
+ app.set(:configuration, { hide_node_vars: [] })
38
+ @nodes.expects(:show).with("sw5").returns(@serialized_node)
39
+
40
+ get '/node/show/sw5'
41
+ _(last_response.ok?).must_equal true
42
+ body = last_response.body
43
+
44
+ _(body).must_match(/secret_enable/)
45
+ _(body).must_match(/secret_password/)
46
+ _(body.include?(
47
+ "<code>{\n &quot;name&quot;: &quot;sw5&quot;," \
48
+ "\n &quot;full_name&quot;: &quot;sw5.example.com&quot;," \
49
+ "\n &quot;ip&quot;: &quot;10.42.12.42&quot;," \
50
+ "\n &quot;group&quot;: null,\n &quot;model&quot;: &quot;ios&quot;," \
51
+ "\n &quot;last&quot;: {" \
52
+ "\n &quot;start&quot;: &quot;2025-02-05 19:49:00 +0100&quot;," \
53
+ "\n &quot;end&quot;: &quot;2025-02-05 19:49:10 +0100&quot;," \
54
+ "\n &quot;status&quot;: &quot;no_connection&quot;," \
55
+ "\n &quot;time&quot;: 10\n }," \
56
+ "\n &quot;vars&quot;: {" \
57
+ "\n &quot;oxi&quot;: &quot;dized&quot;," \
58
+ "\n &quot;enable&quot;: &quot;secret_enable&quot;," \
59
+ "\n &quot;username&quot;: &quot;oxidized&quot;," \
60
+ "\n &quot;password&quot;: &quot;secret_password&quot;" \
61
+ "\n },\n &quot;mtime&quot;: &quot;2025-02-05 19:49:11 +0100&quot;" \
62
+ "\n}</code>"
63
+ )).must_equal true
64
+ end
65
+
66
+ it "hides vars in hide_node_vars" do
67
+ app.set(:configuration, { hide_node_vars: %i[enable password] })
68
+ @nodes.expects(:show).with("sw5").returns(@serialized_node)
69
+
70
+ get '/node/show/sw5'
71
+ _(last_response.ok?).must_equal true
72
+ body = last_response.body
73
+
74
+ _(body).wont_match(/secret_enable/)
75
+ _(body).wont_match(/secret_password/)
76
+ _(body).must_match(/&lt;hidden&gt;/)
77
+ _(body.include?(
78
+ "<code>{\n &quot;name&quot;: &quot;sw5&quot;," \
79
+ "\n &quot;full_name&quot;: &quot;sw5.example.com&quot;," \
80
+ "\n &quot;ip&quot;: &quot;10.42.12.42&quot;," \
81
+ "\n &quot;group&quot;: null,\n &quot;model&quot;: &quot;ios&quot;," \
82
+ "\n &quot;last&quot;: {" \
83
+ "\n &quot;start&quot;: &quot;2025-02-05 19:49:00 +0100&quot;," \
84
+ "\n &quot;end&quot;: &quot;2025-02-05 19:49:10 +0100&quot;," \
85
+ "\n &quot;status&quot;: &quot;no_connection&quot;," \
86
+ "\n &quot;time&quot;: 10\n }," \
87
+ "\n &quot;vars&quot;: {" \
88
+ "\n &quot;oxi&quot;: &quot;dized&quot;," \
89
+ "\n &quot;enable&quot;: &quot;&lt;hidden&gt;&quot;," \
90
+ "\n &quot;username&quot;: &quot;oxidized&quot;," \
91
+ "\n &quot;password&quot;: &quot;&lt;hidden&gt;&quot;" \
92
+ "\n },\n &quot;mtime&quot;: &quot;2025-02-05 19:49:11 +0100&quot;" \
93
+ "\n}</code>"
94
+ )).must_equal true
95
+
96
+ # The note data is not changed (deep copy with Marshal)
97
+ _(@serialized_node[:vars][:enable]).must_equal "secret_enable"
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,161 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Oxidized::API::WebApp do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Oxidized::API::WebApp
8
+ end
9
+
10
+ before do
11
+ @nodes = mock('Oxidized::Nodes')
12
+ app.set(:nodes, @nodes)
13
+ end
14
+
15
+ describe 'get /node/version.?:format?' do
16
+ before do
17
+ @versions = [
18
+ { oid: "C006", time: Time.parse("2025-02-05 19:49:00 +0100") },
19
+ { oid: "C003", time: Time.parse("2025-02-05 19:03:00 +0100") },
20
+ { oid: "C001", time: Time.parse("2025-02-05 19:01:00 +0100") }
21
+ ]
22
+ end
23
+
24
+ it 'fetches all versions of a node without a group' do
25
+ @nodes.expects(:version).with('sw5', nil).returns(@versions)
26
+
27
+ get '/node/version?node_full=sw5'
28
+ _(last_response.ok?).must_equal true
29
+ _(last_response.body.include?(
30
+ "<tr>\n<td>3</td>\n<td class='time' epoch='1738781340'>" \
31
+ "2025-02-05 19:49:00 +0100</td>\n"
32
+ )).must_equal true
33
+
34
+ _(last_response.body.include?(
35
+ "href='/node/version/view?node=sw5&amp;group=&amp;oid=C006&amp;" \
36
+ "epoch=1738781340&amp;num=3' title='configuration'>"
37
+ )).must_equal true
38
+ _(last_response.body.include?(
39
+ "href='/node/version/diffs?node=sw5&amp;group=&amp;oid=C006&amp;" \
40
+ "epoch=1738781340&amp;num=3' title='Compare with previous version'>"
41
+ )).must_equal true
42
+ _(last_response.body.include?(
43
+ "href='/node/version/view?node=sw5&amp;group=&amp;oid=C001&amp;" \
44
+ "epoch=1738778460&amp;num=1' title='configuration'>"
45
+ )).must_equal true
46
+ # Compare to the version previous 1 is not possible, so don't display it
47
+ _(last_response.body.include?(
48
+ "href='/node/version/diffs?node=sw5&amp;group=&amp;oid=C001&amp;" \
49
+ "epoch=1738778460&amp;num=1' title='Compare with previous version'>"
50
+ )).must_equal false
51
+ end
52
+
53
+ it 'fetches all versions of a node with a group' do
54
+ @nodes.expects(:version).with('sw5', 'group1').returns(@versions)
55
+
56
+ get '/node/version?node_full=group1/sw5'
57
+ _(last_response.ok?).must_equal true
58
+ _(last_response.body.include?(
59
+ "<tr>\n<td>3</td>\n<td class='time' epoch='1738781340'>" \
60
+ "2025-02-05 19:49:00 +0100</td>\n"
61
+ )).must_equal true
62
+ end
63
+
64
+ it 'fetches all versions of a node with a group with /' do
65
+ @nodes.expects(:version).with('sw5', 'gr/oup1').returns(@versions)
66
+
67
+ get '/node/version?node_full=gr/oup1/sw5'
68
+ _(last_response.ok?).must_equal true
69
+ _(last_response.body.include?(
70
+ "<tr>\n<td>3</td>\n<td class='time' epoch='1738781340'>" \
71
+ "2025-02-05 19:49:00 +0100</td>\n"
72
+ )).must_equal true
73
+ end
74
+ end
75
+
76
+ describe 'get /node/version/view.?:format?' do
77
+ it 'fetches a previous version from git' do
78
+ @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns('Old configuration of sw5')
79
+
80
+ get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&epoch=1738781340&num=2'
81
+ _(last_response.ok?).must_equal true
82
+ _(last_response.body.include?('Old configuration of sw5')).must_equal true
83
+ # The test needs to pass in any timezone, so we use Time.parse to get the
84
+ # right string
85
+ _(last_response.body.include?(
86
+ "Date of version:\n" \
87
+ "<span class='time' epoch='1738781340'>" \
88
+ "#{Time.at(1738781340)}</span>"
89
+ )).must_equal true
90
+ end
91
+
92
+ it 'does not display binary content' do
93
+ @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns("\xff\x42 binary content\x00")
94
+
95
+ get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&epoch=1738781340&num=2'
96
+ _(last_response.ok?).must_equal true
97
+ _(last_response.body.include?('cannot display')).must_equal true
98
+ end
99
+
100
+ it 'fetches a git-version when using a group containing /' do
101
+ @nodes.expects(:get_version).with('sw5', 'my/group', 'c8aa93cab5').returns('Old configuration of sw5')
102
+
103
+ get '/node/version/view?node=sw5&group=my/group&oid=c8aa93cab5&epoch=1738781340&num=2'
104
+ _(last_response.ok?).must_equal true
105
+ _(last_response.body.include?('Old configuration of sw5')).must_equal true
106
+ end
107
+
108
+ it 'does not encode html-chars in text-format' do
109
+ configuration = "text &/<> \n ascii;"
110
+ @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns(configuration)
111
+ get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&epoch=1738781340&num=2&format=text'
112
+
113
+ _(last_response.ok?).must_equal true
114
+ _(last_response.body).must_equal configuration
115
+ end
116
+
117
+ it 'does not encode html-chars in json-format' do
118
+ configuration = "text &/<> \n ascii;"
119
+ @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns(configuration)
120
+ get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&epoch=1738781340&num=2&format=json'
121
+
122
+ _(last_response.ok?).must_equal true
123
+ _(last_response.body).must_equal '["text &/<> \n"," ascii;"]'
124
+ end
125
+ end
126
+
127
+ describe 'get /node/version/diffs' do
128
+ it 'diffs a version with the latest configuration' do
129
+ @versions = [
130
+ { oid: "C006", time: Time.parse("2025-02-05 19:49:00 +0100") },
131
+ { oid: "C003", time: Time.parse("2025-02-05 19:03:00 +0100") },
132
+ { oid: "C001", time: Time.parse("2025-02-05 19:01:00 +0100") }
133
+ ]
134
+ @diff = { patch: "diff --git a/sw5 b/sw5\n" \
135
+ "index C006..C003 100644\n" \
136
+ "--- a/sw5\n" \
137
+ "+++ b/sw5\n" \
138
+ "@@ -38,12 +38,12 @@ some_line\n " \
139
+ "unchanged line\n" \
140
+ "-changed line old\n" \
141
+ "+changed line new1\n" \
142
+ "+changed line new2\n " \
143
+ "\n " \
144
+ "unchanged line\n",
145
+ stat: [1, 2] }
146
+
147
+ @nodes.expects(:version).with('sw5', nil).returns(@versions)
148
+ @nodes.expects(:get_diff).returns(@diff)
149
+
150
+ get '/node/version/diffs?node=sw5&group=&oid=C006&epoch=1738781340&num=3'
151
+ _(last_response.ok?).must_equal true
152
+ # The test needs to pass in any timezone, so we use Time.parse to get the
153
+ # right string
154
+ _(last_response.body.include?(
155
+ "Date of version:\n" \
156
+ "<span class='time' epoch='1738781340'>" \
157
+ "#{Time.at(1738781340)}</span>"
158
+ )).must_equal true
159
+ end
160
+ end
161
+ end
@@ -1,4 +1,4 @@
1
- require_relative 'spec_helper'
1
+ require_relative '../spec_helper'
2
2
 
3
3
  describe Oxidized::API::WebApp do
4
4
  include Rack::Test::Methods
@@ -1,4 +1,4 @@
1
- require_relative 'spec_helper'
1
+ require_relative '../spec_helper'
2
2
  require 'json'
3
3
 
4
4
  describe Oxidized::API::WebApp do
@@ -1,4 +1,4 @@
1
- require_relative 'spec_helper'
1
+ require_relative '../spec_helper'
2
2
 
3
3
  describe Oxidized::API::WebApp do
4
4
  include Rack::Test::Methods
@@ -1,4 +1,4 @@
1
- require_relative 'spec_helper'
1
+ require_relative '../spec_helper'
2
2
 
3
3
  # Test helper methods in Oxidized::API::WebApp
4
4
 
data/spec/web_spec.rb ADDED
@@ -0,0 +1,98 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Oxidized::API::Web do
4
+ describe "#parse_legacy_configuration" do
5
+ test_cases = [
6
+ {
7
+ configuration: '192.168.1.100:9999',
8
+ expected: { addr: '192.168.1.100', port: 9999, uri_prefix: '/',
9
+ vhosts: [], hide_node_vars: [] },
10
+ description: 'IP address and port'
11
+ },
12
+ {
13
+ configuration: '[::1]:8080',
14
+ expected: { addr: '[::1]', port: 8080, uri_prefix: '/',
15
+ vhosts: [], hide_node_vars: [] },
16
+ description: 'IPv6 address with port'
17
+ },
18
+ {
19
+ configuration: '1234',
20
+ expected: { addr: '', port: 1234, uri_prefix: '/',
21
+ vhosts: [], hide_node_vars: [] },
22
+ description: 'port only'
23
+ },
24
+ {
25
+ configuration: '0.0.0.0:9999/api',
26
+ expected: { addr: '0.0.0.0', port: 9999, uri_prefix: '/api',
27
+ vhosts: [], hide_node_vars: [] },
28
+ description: 'host:port/prefix'
29
+ },
30
+ {
31
+ configuration: '8888/v1',
32
+ expected: { addr: '', port: 8888, uri_prefix: '/v1',
33
+ vhosts: [], hide_node_vars: [] },
34
+ description: 'port/prefix'
35
+ },
36
+ {
37
+ configuration: '127.0.0.1:3000/api/v2',
38
+ expected: { addr: '127.0.0.1', port: 3000, uri_prefix: '/api/v2',
39
+ vhosts: [], hide_node_vars: [] },
40
+ description: 'complex URI prefix'
41
+ },
42
+ {
43
+ configuration: '127.0.0.1:3000/',
44
+ expected: { addr: '127.0.0.1', port: 3000, uri_prefix: '/',
45
+ vhosts: [], hide_node_vars: [] },
46
+ description: 'empty URI prefix after slash'
47
+ }
48
+ ]
49
+
50
+ test_cases.each do |test_case|
51
+ it "should parse #{test_case[:description]} correctly" do
52
+ result = Oxidized::API::Web.parse_legacy_configuration(test_case[:configuration])
53
+ expect(result).must_equal test_case[:expected]
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "#parse_new_configuration" do
59
+ test_cases = [
60
+ {
61
+ configuration: Asetus::ConfigStruct.new(
62
+ {
63
+ 'listen' => '0.0.0.0',
64
+ 'port' => 3000,
65
+ 'url_prefix' => '/api',
66
+ 'vhosts' => ['example.com', 'test.com'],
67
+ 'hide_node_vars' => %w[enable password]
68
+ }
69
+ ),
70
+ expected: { addr: '0.0.0.0', port: 3000, uri_prefix: '/api',
71
+ vhosts: ["example.com", "test.com"],
72
+ hide_node_vars: %i[enable password] },
73
+ description: 'all values provided'
74
+ },
75
+ {
76
+ configuration: Asetus::ConfigStruct.new,
77
+ expected: { addr: '127.0.0.1', port: 8888, uri_prefix: '/',
78
+ vhosts: [], hide_node_vars: [] },
79
+ description: 'all default values'
80
+ },
81
+ {
82
+ configuration: Asetus::ConfigStruct.new(
83
+ { 'hide_node_vars' => 'enable' }
84
+ ),
85
+ expected: { addr: '127.0.0.1', port: 8888, uri_prefix: '/',
86
+ vhosts: [], hide_node_vars: [] },
87
+ description: 'return an empty list when hide_node_vars not a hash'
88
+ }
89
+ ]
90
+
91
+ test_cases.each do |test_case|
92
+ it "should parse #{test_case[:description]} correctly" do
93
+ result = Oxidized::API::Web.parse_configuration(test_case[:configuration])
94
+ expect(result).must_equal test_case[:expected]
95
+ end
96
+ end
97
+ end
98
+ end