Skip to content

Commit e48b2be

Browse files
authored
Merge pull request #85 from silx-kit/tests
Add first tests and set-up pytest
2 parents 488650c + 5bbc456 commit e48b2be

File tree

9 files changed

+186
-6
lines changed

9 files changed

+186
-6
lines changed
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
steps:
1515
- name: Checkout 🏷️
16-
uses: actions/checkout@v2
16+
uses: actions/checkout@v3
1717

1818
- name: Set up Python 🐍
1919
uses: actions/setup-python@v2
@@ -37,3 +37,16 @@ jobs:
3737

3838
- name: check-manifest 📰
3939
run: check-manifest
40+
41+
- name: Install Node v16
42+
uses: actions/setup-node@v3
43+
with:
44+
node-version: '16'
45+
46+
- name: Install configurable-http-proxy
47+
run: |
48+
npm install -g configurable-http-proxy
49+
npm list
50+
51+
- name: pytest tests 👓
52+
run: python -m pytest -v

CONTRIBUTING.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,23 @@ manually using
2828
black .
2929
```
3030

31+
### Testing
32+
33+
[pytest](https://docs.pytest.org/en/latest/) is used to run the tests. The
34+
config is located in `pyproject.toml`. Tests can be run using:
35+
36+
```
37+
python -m pytest
38+
```
39+
40+
Note: This is different from calling `pytest`, see
41+
[Invoking pytest versus python -m pytest](https://docs.pytest.org/en/latest/explanation/pythonpath.html#invoking-pytest-versus-python-m-pytest).
42+
3143
### CI
3244

33-
The CI will check that the lint check passes and that all files are correctly
34-
formatted (using `black --check .`). Before commiting, be sure to run `flake8`
35-
and `black` to ensure CI passes.
45+
The CI will check that the lint check passes, that all files are correctly
46+
formatted (using `black --check .`) and that tests passes. Before commiting, be
47+
sure to run `flake8`, `black` and `python -m pytest` to ensure CI passes.
3648

3749
## Generate the spawn page locally
3850

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
graft demo
22
graft jupyterhub_moss
3+
graft test
34
prune .vscode
45
exclude .bumpversion.cfg
56
exclude .editorconfig

pyproject.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,13 @@ requires = [
33
"setuptools>=42",
44
"wheel"
55
]
6-
build-backend = "setuptools.build_meta"
6+
build-backend = "setuptools.build_meta"
7+
8+
[tool.pytest.ini_options]
9+
addopts = [
10+
"--import-mode=importlib",
11+
]
12+
asyncio_mode = "auto"
13+
testpaths = [
14+
"test",
15+
]

setup.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ install_requires =
3030

3131
[options.extras_require]
3232
dev =
33+
black
3334
build
3435
bump2version
3536
check-manifest
3637
flake8
37-
black
38+
jupyter_server
39+
pytest
40+
pytest-asyncio
3841

3942
# E501 (line too long) ignored
4043
# E203 and W503 incompatible with black formatting (https://black.readthedocs.io/en/stable/compatible_configs.html#flake8)

test/__init__.py

Whitespace-only changes.

test/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Use jupyterhub's fixtures
2+
pytest_plugins = "jupyterhub.tests.conftest"

test/test_spawn_page.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import json
2+
import re
3+
from unittest import mock
4+
5+
from jupyterhub.tests.utils import get_page
6+
7+
from .utils import MOSlurmSpawnerMock, post_request
8+
9+
10+
class MOSSMockSimple(MOSlurmSpawnerMock):
11+
"""MOSlurmSpawner with mocks and a simple configuration"""
12+
13+
def __init__(self, *args, **kwargs):
14+
super().__init__(*args, **kwargs)
15+
16+
# Partition name, nnodes (allocated/idle/other/total), ncores_per_node,
17+
# ncores (allocated/idle/other/total), gpu, memory, timelimit
18+
self.slurm_info_cmd = (
19+
'echo "partition_1 2/46/0/48 35+ 38/1642/0/1680 (null) 196000+ 1-00:00:00"'
20+
)
21+
22+
# Set partitions here so that validation is run
23+
# Minimalistic default partitions config
24+
self.partitions = {
25+
"partition_1": {
26+
"architecture": "x86_86",
27+
"description": "Partition 1",
28+
"simple": True,
29+
"jupyter_environments": {
30+
"default": {
31+
"path": "/default/jupyter_env_path/bin/",
32+
"description": "default",
33+
"add_to_path": True,
34+
},
35+
},
36+
},
37+
}
38+
39+
40+
async def test_spawn_page(app):
41+
"""Test display of spawn page and check embedded SLURM resources info"""
42+
with mock.patch.dict(app.users.settings, {"spawner_class": MOSSMockSimple}):
43+
cookies = await app.login_user("jones")
44+
r = await get_page("spawn", app, cookies=cookies)
45+
46+
assert r.status_code == 200
47+
assert r.url.endswith("/spawn")
48+
49+
match = re.search(r"window.SLURM_DATA = JSON.parse\('(?P<json>.*)'\)", r.text)
50+
assert match is not None
51+
slurm_data = json.loads(match.group("json"))
52+
53+
ref_partitions_info = {
54+
"partition_1": {
55+
"nnodes_idle": 46,
56+
"nnodes_total": 48,
57+
"ncores_total": 1680,
58+
"ncores_idle": 1642,
59+
"max_nprocs": 35,
60+
"max_mem": 196000,
61+
"gpu": None,
62+
"max_ngpus": 0,
63+
"max_runtime": 86400,
64+
"architecture": "x86_86",
65+
"description": "Partition 1",
66+
"simple": True,
67+
"jupyter_environments": {
68+
"default": {
69+
"path": "/default/jupyter_env_path/bin/",
70+
"description": "default",
71+
"add_to_path": True,
72+
},
73+
},
74+
}
75+
}
76+
assert ref_partitions_info == slurm_data["partitions"]
77+
78+
79+
async def test_spawn_from_get_query(app):
80+
"""Test spawning through a GET query"""
81+
with mock.patch.dict(app.users.settings, {"spawner_class": MOSSMockSimple}):
82+
cookies = await app.login_user("jones")
83+
r = await get_page("spawn?partition=partition_1&nprocs=4", app, cookies=cookies)
84+
85+
assert r.status_code == 200
86+
assert "/hub/spawn-pending" in r.url
87+
88+
89+
async def test_spawn_from_post_request(app):
90+
"""Test spawning through a POST request"""
91+
with mock.patch.dict(app.users.settings, {"spawner_class": MOSSMockSimple}):
92+
cookies = await app.login_user("jones")
93+
r = await post_request(
94+
"spawn",
95+
app,
96+
cookies=cookies,
97+
data={"partition": "partition_1", "nprocs": 4},
98+
)
99+
100+
assert r.status_code == 200
101+
assert "/hub/spawn-pending" in r.url

test/utils.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from jupyterhub.tests.utils import async_requests, public_host
2+
from jupyterhub.utils import url_path_join
3+
from traitlets import Unicode, default
4+
5+
from jupyterhub_moss import MOSlurmSpawner
6+
7+
8+
def post_request(path, app, **kwargs):
9+
"""Send a POST request on the hub
10+
11+
Similar to jupyterhub.tests.utils.get_page
12+
"""
13+
base_url = url_path_join(public_host(app), app.hub.base_url)
14+
return async_requests.post(url_path_join(base_url, path), **kwargs)
15+
16+
17+
class MOSlurmSpawnerMock(MOSlurmSpawner):
18+
"""MOSlurmSpawner with some overrides to mock some features.
19+
20+
Adapted from jupyterhub.tests.mocking.MockSpawner and
21+
batchspawner.tests.test_spawner.BatchDummy
22+
"""
23+
24+
exec_prefix = Unicode("")
25+
batch_submit_cmd = Unicode("cat > /dev/null; sleep 1")
26+
batch_query_cmd = Unicode("echo PENDING")
27+
batch_cancel_cmd = Unicode("echo STOP")
28+
29+
req_homedir = Unicode(help="The home directory for the user")
30+
31+
@default("req_homedir")
32+
def _default_req_homedir(self):
33+
return f"/tmp/jupyterhub_moss_tests/{self.user.name}"
34+
35+
def user_env(self, env):
36+
env["USER"] = self.user.name
37+
env["HOME"] = self.req_homedir
38+
env["SHELL"] = "/bin/bash"
39+
return env

0 commit comments

Comments
 (0)