Skip to content

Commit fb13460

Browse files
tests: add test_rados_opener.py
Signed-off-by: John Mulligan <[email protected]>
1 parent 897d7b1 commit fb13460

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

tests/test_rados_opener.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#
2+
# sambacc: a samba container configuration tool
3+
# Copyright (C) 2023 John Mulligan
4+
#
5+
# This program is free software: you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# This program is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with this program. If not, see <http://www.gnu.org/licenses/>
17+
#
18+
19+
import io
20+
import sys
21+
import unittest.mock
22+
import urllib.request
23+
24+
import pytest
25+
26+
import sambacc.rados_opener
27+
28+
# CAUTION: nearly all of these tests are based on mocking the ceph rados API.
29+
# Testing this for real would require an operational ceph cluster and that's
30+
# not happening for a simple set of unit tests!
31+
32+
33+
def test_enable_rados_url_opener(monkeypatch):
34+
mock = unittest.mock.MagicMock()
35+
monkeypatch.setitem(sys.modules, "rados", mock)
36+
37+
cls_mock = unittest.mock.MagicMock()
38+
sambacc.rados_opener.enable_rados_url_opener(cls_mock)
39+
assert cls_mock._handlers.append.called
40+
41+
42+
def test_enable_rados_url_opener_fail(monkeypatch):
43+
cls_mock = unittest.mock.MagicMock()
44+
sambacc.rados_opener.enable_rados_url_opener(cls_mock)
45+
assert not cls_mock._handlers.append.called
46+
47+
48+
def test_rados_handler_parse():
49+
class RH(sambacc.rados_opener._RADOSHandler):
50+
_rados_api = unittest.mock.MagicMock()
51+
52+
rh = RH()
53+
rq = urllib.request.Request("rados://foo/bar/baz")
54+
rr = rh.rados_open(rq)
55+
assert rr._pool == "foo"
56+
assert rr._ns == "bar"
57+
assert rr._key == "baz"
58+
59+
rq = urllib.request.Request("rados:///foo1/bar1/baz1")
60+
rr = rh.rados_open(rq)
61+
assert rr._pool == "foo1"
62+
assert rr._ns == "bar1"
63+
assert rr._key == "baz1"
64+
65+
66+
def test_rados_handler_norados():
67+
# Generally, this shouldn't happen because the rados handler shouldn't
68+
# be added to the URLOpener if rados module was unavailable.
69+
class RH(sambacc.rados_opener._RADOSHandler):
70+
_rados_api = None
71+
72+
rh = RH()
73+
rq = urllib.request.Request("rados://foo/bar/baz")
74+
with pytest.raises(sambacc.rados_opener.RADOSUnsupported):
75+
rh.rados_open(rq)
76+
77+
78+
def test_rados_response_read_all():
79+
sval = b"Hello, World.\nI am a fake rados object.\n"
80+
81+
def _read(_, size, off):
82+
if off < len(sval):
83+
return sval
84+
85+
mock = unittest.mock.MagicMock()
86+
mock.Rados.return_value.open_ioctx.return_value.read.side_effect = _read
87+
88+
rr = sambacc.rados_opener._RADOSResponse(mock, "foo", "bar", "baz")
89+
assert rr.readable()
90+
assert not rr.seekable()
91+
assert not rr.writable()
92+
assert not rr.isatty()
93+
assert rr.mode == "rb"
94+
assert rr.name == "baz"
95+
assert not rr.closed
96+
data = rr.read()
97+
assert data == sval
98+
assert rr.tell() == len(sval)
99+
rr.flush()
100+
rr.close()
101+
assert rr.closed
102+
103+
104+
def test_rados_response_read_chunks():
105+
sval = b"a bad cat lives under the murky terrifying water"
106+
bio = io.BytesIO(sval)
107+
108+
def _read(_, size, off):
109+
bio.seek(off)
110+
return bio.read(size)
111+
112+
mock = unittest.mock.MagicMock()
113+
mock.Rados.return_value.open_ioctx.return_value.read.side_effect = _read
114+
115+
rr = sambacc.rados_opener._RADOSResponse(mock, "foo", "bar", "baz")
116+
assert rr.readable()
117+
assert rr.read(8) == b"a bad ca"
118+
assert rr.read(8) == b"t lives "
119+
assert rr.read(8) == b"under th"
120+
121+
122+
def test_rados_response_read_ctx_iter():
123+
sval = b"a bad cat lives under the murky terrifying water"
124+
bio = io.BytesIO(sval)
125+
126+
def _read(_, size, off):
127+
bio.seek(off)
128+
return bio.read(size)
129+
130+
mock = unittest.mock.MagicMock()
131+
mock.Rados.return_value.open_ioctx.return_value.read.side_effect = _read
132+
133+
rr = sambacc.rados_opener._RADOSResponse(mock, "foo", "bar", "baz")
134+
with rr:
135+
result = [value for value in rr]
136+
assert result == [sval]
137+
with pytest.raises(ValueError):
138+
rr.read(8)
139+
140+
141+
def test_rados_response_not_implemented():
142+
mock = unittest.mock.MagicMock()
143+
144+
rr = sambacc.rados_opener._RADOSResponse(mock, "foo", "bar", "baz")
145+
with pytest.raises(NotImplementedError):
146+
rr.seek(10)
147+
with pytest.raises(NotImplementedError):
148+
rr.fileno()
149+
with pytest.raises(NotImplementedError):
150+
rr.readline()
151+
with pytest.raises(NotImplementedError):
152+
rr.readlines()
153+
with pytest.raises(NotImplementedError):
154+
rr.truncate()
155+
with pytest.raises(NotImplementedError):
156+
rr.write(b"zzzzz")
157+
with pytest.raises(NotImplementedError):
158+
rr.writelines([b"zzzzz"])

0 commit comments

Comments
 (0)