mirror of
https://github.com/h44z/wg-portal.git
synced 2025-04-19 08:55:12 +00:00
UI unit tests (#59)
* tests - add pytests for the UI * tests/api - fix NotImplemented * tests - add README Co-authored-by: Markus Koetter <koetter@cispa.de>
This commit is contained in:
parent
19c58fb5af
commit
04bc0b7a81
59
tests/README.md
Normal file
59
tests/README.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# pyswagger unittests for the API & UI
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
```
|
||||||
|
wg-quick up conf/wg-example0.conf
|
||||||
|
sudo LOG_LEVEL=debug CONFIG_FILE=conf/config.yml ../dist/wg-portal-amd64
|
||||||
|
|
||||||
|
python3 -m venv ~/venv/apitest
|
||||||
|
~/venv/apitest/bin/pip install pyswagger mechanize requests pytest PyYAML
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
### API
|
||||||
|
```
|
||||||
|
~/venv/apitest/bin/python3 -m unittest test_API.TestAPI
|
||||||
|
```
|
||||||
|
|
||||||
|
### UI
|
||||||
|
```
|
||||||
|
~/venv/lsl/bin/pytest pytest_UI.py
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
Debugging for requests http request/response is included for the API unittesting.
|
||||||
|
To use, adjust the log level for "api" logger to DEBUG
|
||||||
|
|
||||||
|
```python
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
<action>
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
|
```
|
||||||
|
This will provide:
|
||||||
|
```
|
||||||
|
2021-09-29 14:55:15,585 DEBUG api HTTP
|
||||||
|
---------------- request ----------------
|
||||||
|
GET http://localhost:8123/api/v1/provisioning/peers?Email=test%2Bn4gbm7%40example.org
|
||||||
|
User-Agent: python-requests/2.26.0
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Accept: application/json
|
||||||
|
Connection: keep-alive
|
||||||
|
Authorization: Basic d2dAZXhhbXBsZS5vcmc6YWJhZGNob2ljZQ==
|
||||||
|
|
||||||
|
None
|
||||||
|
---------------- response ----------------
|
||||||
|
200 OK http://localhost:8123/api/v1/provisioning/peers?Email=test%2Bn4gbm7%40example.org
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
Date: Wed, 29 Sep 2021 12:55:15 GMT
|
||||||
|
Content-Length: 285
|
||||||
|
|
||||||
|
[{"PublicKey":"hO3pxnft/8QL6nbE+79HN464Z+L4+D/JjUvNE+8LmTs=",
|
||||||
|
"Identifier":"Test User (Default)","Device":"wg-example0","DeviceIdentifier":"example0"},
|
||||||
|
{"PublicKey":"RVS2gsdRpFjyOpr1nAlEkrs194lQytaPHhaxL5amQxY=",
|
||||||
|
"Identifier":"debug","Device":"wg-example0","DeviceIdentifier":"example0"}]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
214
tests/pytest_UI.py
Normal file
214
tests/pytest_UI.py
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
import logging.config
|
||||||
|
import http.cookiejar
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
import mechanize
|
||||||
|
import yaml
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="function")
|
||||||
|
def browser():
|
||||||
|
# Fake Cookie Policy to send the Secure cookies via http
|
||||||
|
class InSecureCookiePolicy(http.cookiejar.DefaultCookiePolicy):
|
||||||
|
def set_ok(self, cookie, request):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def return_ok(self, cookie, request):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def domain_return_ok(self, domain, request):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def path_return_ok(self, path, request):
|
||||||
|
return True
|
||||||
|
|
||||||
|
b = mechanize.Browser()
|
||||||
|
b.set_cookiejar(http.cookiejar.CookieJar(InSecureCookiePolicy()))
|
||||||
|
b.set_handle_robots(False)
|
||||||
|
b.set_debug_http(True)
|
||||||
|
return b
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def config():
|
||||||
|
cfg = yaml.load(open('conf/config.yml', 'r'))
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def admin(browser, config):
|
||||||
|
auth = (c := config['core'])['adminUser'], c['adminPass']
|
||||||
|
return _login(browser, auth)
|
||||||
|
|
||||||
|
def _create_user(admin, values):
|
||||||
|
b = admin
|
||||||
|
b.follow_link(text="User Management")
|
||||||
|
b.follow_link(predicate=has_attr('Add a user'))
|
||||||
|
|
||||||
|
# FIXME name form
|
||||||
|
b.select_form(predicate=lambda x: x.method == 'post')
|
||||||
|
for k, v in values.items():
|
||||||
|
b.form.set_value(v, k)
|
||||||
|
b.submit()
|
||||||
|
alert = b._factory.root.findall('body/div/div[@role="alert"]')
|
||||||
|
assert len(alert) == 1 and alert[0].text.strip() == "user created successfully"
|
||||||
|
return values["email"],values["password"]
|
||||||
|
|
||||||
|
def _destroy_user(admin, uid):
|
||||||
|
b = admin
|
||||||
|
b.follow_link(text="User Management")
|
||||||
|
for user in b._factory.root.findall('body/div/div/table[@id="userTable"]/tbody/'):
|
||||||
|
email,*_ = list(map(lambda x: x.text.strip() if x.text else '', list(user)))
|
||||||
|
if email == uid:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
a = user.findall('td/a[@title="Edit user"]')
|
||||||
|
assert len(a) == 1
|
||||||
|
b.follow_link(url=a[0].attrib['href'])
|
||||||
|
|
||||||
|
# FIXME name form
|
||||||
|
b.select_form(predicate=lambda x: x.method == 'post')
|
||||||
|
disabled = b.find_control("isdisabled")
|
||||||
|
disabled.set_single("true")
|
||||||
|
b.submit()
|
||||||
|
|
||||||
|
def _destroy_peer(admin, uid):
|
||||||
|
b = admin
|
||||||
|
b.follow_link(text="Administration")
|
||||||
|
peers = b._factory.root.findall('body/div/div/table[@id="userTable"]/tbody/tr')
|
||||||
|
for idx,peer in enumerate(peers):
|
||||||
|
if idx % 2 == 1:
|
||||||
|
continue
|
||||||
|
head, Identifier, PublicKey, EMail, IPs, Handshake, tail = list(map(lambda x: x.text.strip() if x.text else x, list(peer)))
|
||||||
|
print(Identifier)
|
||||||
|
if EMail != uid:
|
||||||
|
continue
|
||||||
|
peer = peers[idx+1]
|
||||||
|
a = peer.findall('.//a[@title="Delete peer"]')
|
||||||
|
assert len(a) == 1
|
||||||
|
b.follow_link(url=a[0].attrib['href'])
|
||||||
|
|
||||||
|
|
||||||
|
def _list_peers(user):
|
||||||
|
r = []
|
||||||
|
b = user
|
||||||
|
b.follow_link(predicate=has_attr('User-Profile'))
|
||||||
|
profiles = b._factory.root.findall('body/div/div/table[@id="userTable"]/tbody/tr')
|
||||||
|
for idx,profile in enumerate(profiles):
|
||||||
|
if idx % 2 == 1:
|
||||||
|
continue
|
||||||
|
head, Identifier, PublicKey, EMail, IPs, Handshake = list(map(lambda x: x.text.strip() if x.text else x, list(profile)))
|
||||||
|
profile = profiles[idx+1]
|
||||||
|
pre = profile.findall('.//pre')
|
||||||
|
assert len(pre) == 1
|
||||||
|
r.append((PublicKey, pre))
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def user_data():
|
||||||
|
values = {
|
||||||
|
"email": f"test+{randstr()}@example.org",
|
||||||
|
"password": randstr(12),
|
||||||
|
"firstname": randstr(8),
|
||||||
|
"lastname": randstr(12)
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def user(admin, user_data, config):
|
||||||
|
b = admin
|
||||||
|
auth = _create_user(b, user_data)
|
||||||
|
_logout(b)
|
||||||
|
_login(b, auth)
|
||||||
|
assert b.find_link(predicate=has_attr('User-Profile'))
|
||||||
|
yield b
|
||||||
|
_logout(b)
|
||||||
|
auth = (c := config['core'])['adminUser'], c['adminPass']
|
||||||
|
_login(b, auth)
|
||||||
|
_destroy_user(b, user_data["email"])
|
||||||
|
_destroy_peer(b, user_data["email"])
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def peer(admin, user, user_data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _login(browser, auth):
|
||||||
|
b = browser
|
||||||
|
b.open("http://localhost:8123/")
|
||||||
|
|
||||||
|
b.follow_link(text="Login")
|
||||||
|
|
||||||
|
b.select_form(name="login")
|
||||||
|
username, password = auth
|
||||||
|
b.form.set_value(username, "username")
|
||||||
|
b.form.set_value(password, "password")
|
||||||
|
b.submit()
|
||||||
|
return b
|
||||||
|
|
||||||
|
def _logout(browser):
|
||||||
|
browser.follow_link(text="Logout")
|
||||||
|
return browser
|
||||||
|
|
||||||
|
def has_attr(value, attr='title'):
|
||||||
|
def find_attr(x):
|
||||||
|
return any([a == (attr, value) for a in x.attrs])
|
||||||
|
return find_attr
|
||||||
|
|
||||||
|
|
||||||
|
def _server(browser, addr):
|
||||||
|
b = browser
|
||||||
|
b.follow_link(text="Administration")
|
||||||
|
b.follow_link(predicate=has_attr('Edit interface settings'))
|
||||||
|
b.select_form("server")
|
||||||
|
|
||||||
|
values = {
|
||||||
|
"displayname": "example0",
|
||||||
|
"endpoint": "wg.example.org:51280",
|
||||||
|
"ip": addr
|
||||||
|
}
|
||||||
|
for k, v in values.items():
|
||||||
|
b.form.set_value(v, k)
|
||||||
|
|
||||||
|
b.submit()
|
||||||
|
return b
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def server(admin):
|
||||||
|
return _server(admin, "10.0.0.0/24")
|
||||||
|
|
||||||
|
def randstr(l=6):
|
||||||
|
return ''.join([random.choice(string.ascii_lowercase + string.digits) for i in range(l)])
|
||||||
|
|
||||||
|
|
||||||
|
def test_admin_login(admin):
|
||||||
|
b = admin
|
||||||
|
b.find_link("Administration")
|
||||||
|
|
||||||
|
|
||||||
|
def test_admin_server(admin):
|
||||||
|
ip = "10.0.0.0/28"
|
||||||
|
b = _server(admin, ip)
|
||||||
|
b.select_form("server")
|
||||||
|
assert ip == b.form.get_value("ip")
|
||||||
|
|
||||||
|
|
||||||
|
def test_admin_create_peer(server, user_data):
|
||||||
|
auth = _create_user(server, user_data)
|
||||||
|
|
||||||
|
|
||||||
|
def test_admin_create_user(admin, user_data):
|
||||||
|
auth = _create_user(admin, user_data)
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_login(server, user):
|
||||||
|
b = user
|
||||||
|
b.follow_link(predicate=has_attr('User-Profile'))
|
||||||
|
|
||||||
|
def test_user_config(server, user):
|
||||||
|
b = user
|
||||||
|
peers = _list_peers(b)
|
||||||
|
assert len(peers) >= 1
|
@ -134,7 +134,7 @@ class WGPClient:
|
|||||||
elif 500 == resp.status:
|
elif 500 == resp.status:
|
||||||
raise ValueError(resp.data["Message"])
|
raise ValueError(resp.data["Message"])
|
||||||
elif 501 == resp.status:
|
elif 501 == resp.status:
|
||||||
raise NotImplementedError(resp.data["Message"])
|
raise NotImplementedError(name)
|
||||||
elif 502 <= resp.status <= 599:
|
elif 502 <= resp.status <= 599:
|
||||||
raise ApiError(resp.data["Message"])
|
raise ApiError(resp.data["Message"])
|
||||||
return resp
|
return resp
|
||||||
@ -317,22 +317,25 @@ class TestAPI(unittest.TestCase):
|
|||||||
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
dev = self.c.GetDevice(DeviceName=device.DeviceName)
|
dev = self.c.GetDevice(DeviceName=device.DeviceName)
|
||||||
new = self.c.PutDevice(DeviceName=dev.DeviceName,
|
with self.assertRaises(NotImplementedError):
|
||||||
Device={
|
new = self.c.PutDevice(DeviceName=dev.DeviceName,
|
||||||
"DeviceName": dev.DeviceName,
|
Device={
|
||||||
"IPsStr": dev.IPsStr,
|
"DeviceName": dev.DeviceName,
|
||||||
"PrivateKey": dev.PrivateKey,
|
"IPsStr": dev.IPsStr,
|
||||||
"Type": "client",
|
"PrivateKey": dev.PrivateKey,
|
||||||
"PublicKey": dev.PublicKey}
|
"Type": "client",
|
||||||
)
|
"PublicKey": dev.PublicKey}
|
||||||
new = self.c.PatchDevice(DeviceName=dev.DeviceName,
|
)
|
||||||
Device={
|
with self.assertRaises(NotImplementedError):
|
||||||
"DeviceName": dev.DeviceName,
|
new = self.c.PatchDevice(DeviceName=dev.DeviceName,
|
||||||
"IPsStr": dev.IPsStr,
|
Device={
|
||||||
"PrivateKey": dev.PrivateKey,
|
"DeviceName": dev.DeviceName,
|
||||||
"Type": "client",
|
"IPsStr": dev.IPsStr,
|
||||||
"PublicKey": dev.PublicKey}
|
"PrivateKey": dev.PrivateKey,
|
||||||
)
|
"Type": "client",
|
||||||
|
"PublicKey": dev.PublicKey}
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
def easy_peer(self):
|
def easy_peer(self):
|
||||||
data = self.c.PostPeerDeploymentConfig(ProvisioningRequest={"Email": self.user, "Identifier": "debug"})
|
data = self.c.PostPeerDeploymentConfig(ProvisioningRequest={"Email": self.user, "Identifier": "debug"})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user