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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.rubocop.yml +22 -2
- data/CHANGELOG.md +30 -1
- data/README.md +13 -3
- data/Rakefile +9 -4
- data/docs/configuration.md +90 -0
- data/docs/development.md +42 -39
- data/lib/oxidized/web/public/scripts/oxidized.js +13 -13
- data/lib/oxidized/web/public/weblibs/bootstrap-icons.css +31 -3
- data/lib/oxidized/web/public/weblibs/bootstrap.bundle.js +20 -19
- data/lib/oxidized/web/public/weblibs/bootstrap.bundle.js.map +1 -1
- data/lib/oxidized/web/public/weblibs/bootstrap.css +110 -124
- data/lib/oxidized/web/public/weblibs/bootstrap.css.map +1 -1
- data/lib/oxidized/web/public/weblibs/bootstrap.js +20 -17
- data/lib/oxidized/web/public/weblibs/bootstrap.js.map +1 -1
- data/lib/oxidized/web/public/weblibs/buttons.bootstrap5.css +3 -3
- data/lib/oxidized/web/public/weblibs/buttons.colVis.js +14 -5
- data/lib/oxidized/web/public/weblibs/dataTables.bootstrap5.css +111 -17
- data/lib/oxidized/web/public/weblibs/dataTables.buttons.js +25 -7
- data/lib/oxidized/web/public/weblibs/dataTables.js +336 -106
- data/lib/oxidized/web/public/weblibs/dayjs-plugin-utc.min.js +1 -0
- data/lib/oxidized/web/public/weblibs/dayjs.min.js +1 -0
- data/lib/oxidized/web/public/weblibs/fonts/bootstrap-icons.woff +0 -0
- data/lib/oxidized/web/public/weblibs/fonts/bootstrap-icons.woff2 +0 -0
- data/lib/oxidized/web/version.rb +1 -1
- data/lib/oxidized/web/views/conf_search.haml +1 -1
- data/lib/oxidized/web/views/diffs.haml +6 -7
- data/lib/oxidized/web/views/head.haml +4 -0
- data/lib/oxidized/web/views/node.haml +3 -2
- data/lib/oxidized/web/views/nodes.haml +13 -5
- data/lib/oxidized/web/views/stats.haml +11 -3
- data/lib/oxidized/web/views/version.haml +1 -2
- data/lib/oxidized/web/views/versions.haml +11 -7
- data/lib/oxidized/web/webapp.rb +41 -29
- data/lib/oxidized/web.rb +72 -16
- data/oxidized-web.gemspec +22 -13
- data/package-lock.json +37 -25
- data/package.json +7 -5
- data/spec/spec_helper.rb +1 -0
- data/spec/web/node/show_spec.rb +100 -0
- data/spec/web/node/version_spec.rb +161 -0
- data/spec/{node_spec.rb → web/node_spec.rb} +1 -1
- data/spec/{nodes_spec.rb → web/nodes_spec.rb} +1 -1
- data/spec/{root_spec.rb → web/root_spec.rb} +1 -1
- data/spec/{webapp_spec.rb → web/webapp_spec.rb} +1 -1
- data/spec/web_spec.rb +98 -0
- metadata +69 -69
- data/.rubocop_todo.yml +0 -64
- 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": "
|
13
|
-
"bootstrap-icons": "
|
14
|
-
"datatables.net-bs5": "
|
15
|
-
"datatables.net-buttons-bs5": "
|
16
|
-
"
|
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.
|
31
|
-
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.
|
32
|
-
"integrity": "sha512-
|
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.
|
49
|
-
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.
|
50
|
-
"integrity": "sha512
|
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.
|
64
|
-
"resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.
|
65
|
-
"integrity": "sha512-
|
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.
|
72
|
-
"resolved": "https://registry.npmjs.org/datatables.net-bs5/-/datatables.net-bs5-2.
|
73
|
-
"integrity": "sha512-
|
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.
|
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.
|
81
|
-
"resolved": "https://registry.npmjs.org/datatables.net-buttons/-/datatables.net-buttons-3.2.
|
82
|
-
"integrity": "sha512
|
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.
|
90
|
-
"resolved": "https://registry.npmjs.org/datatables.net-buttons-bs5/-/datatables.net-buttons-bs5-3.2.
|
91
|
-
"integrity": "sha512-
|
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.
|
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": "
|
16
|
-
"bootstrap-icons": "
|
17
|
-
"datatables.net-bs5": "
|
18
|
-
"datatables.net-buttons-bs5": "
|
19
|
-
"
|
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
@@ -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 "name": "sw5"," \
|
48
|
+
"\n "full_name": "sw5.example.com"," \
|
49
|
+
"\n "ip": "10.42.12.42"," \
|
50
|
+
"\n "group": null,\n "model": "ios"," \
|
51
|
+
"\n "last": {" \
|
52
|
+
"\n "start": "2025-02-05 19:49:00 +0100"," \
|
53
|
+
"\n "end": "2025-02-05 19:49:10 +0100"," \
|
54
|
+
"\n "status": "no_connection"," \
|
55
|
+
"\n "time": 10\n }," \
|
56
|
+
"\n "vars": {" \
|
57
|
+
"\n "oxi": "dized"," \
|
58
|
+
"\n "enable": "secret_enable"," \
|
59
|
+
"\n "username": "oxidized"," \
|
60
|
+
"\n "password": "secret_password"" \
|
61
|
+
"\n },\n "mtime": "2025-02-05 19:49:11 +0100"" \
|
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(/<hidden>/)
|
77
|
+
_(body.include?(
|
78
|
+
"<code>{\n "name": "sw5"," \
|
79
|
+
"\n "full_name": "sw5.example.com"," \
|
80
|
+
"\n "ip": "10.42.12.42"," \
|
81
|
+
"\n "group": null,\n "model": "ios"," \
|
82
|
+
"\n "last": {" \
|
83
|
+
"\n "start": "2025-02-05 19:49:00 +0100"," \
|
84
|
+
"\n "end": "2025-02-05 19:49:10 +0100"," \
|
85
|
+
"\n "status": "no_connection"," \
|
86
|
+
"\n "time": 10\n }," \
|
87
|
+
"\n "vars": {" \
|
88
|
+
"\n "oxi": "dized"," \
|
89
|
+
"\n "enable": "<hidden>"," \
|
90
|
+
"\n "username": "oxidized"," \
|
91
|
+
"\n "password": "<hidden>"" \
|
92
|
+
"\n },\n "mtime": "2025-02-05 19:49:11 +0100"" \
|
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&group=&oid=C006&" \
|
36
|
+
"epoch=1738781340&num=3' title='configuration'>"
|
37
|
+
)).must_equal true
|
38
|
+
_(last_response.body.include?(
|
39
|
+
"href='/node/version/diffs?node=sw5&group=&oid=C006&" \
|
40
|
+
"epoch=1738781340&num=3' title='Compare with previous version'>"
|
41
|
+
)).must_equal true
|
42
|
+
_(last_response.body.include?(
|
43
|
+
"href='/node/version/view?node=sw5&group=&oid=C001&" \
|
44
|
+
"epoch=1738778460&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&group=&oid=C001&" \
|
49
|
+
"epoch=1738778460&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
|
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
|