Skip to content

Commit

Permalink
validate server url input
Browse files Browse the repository at this point in the history
  • Loading branch information
chrispyles committed Feb 19, 2024
1 parent 3034a61 commit 420a3d4
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 8 deletions.
16 changes: 14 additions & 2 deletions nbforms/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""nbforms: A Python library for interactive multi-user Jupyter notebook forms"""

import datascience as ds
import json
import os
import pandas as pd
import re
import requests
import os
import json

from dataclasses import dataclass
from getpass import getpass
Expand All @@ -22,6 +23,9 @@
from .widgets import QuestionWidget


SERVER_URL_REGEX = re.compile(r"^https?://[\w\-.]+(?::\d+)?/?$")


@dataclass
class _Question:
identifier: str
Expand Down Expand Up @@ -87,6 +91,14 @@ def __init__(self, config_path: str = "nbforms_config.json"):
else:
self._server_url = input("Enter the server URL:")

# check validity of server URL
if not SERVER_URL_REGEX.match(self._server_url):
raise ValueError(f"Invalid server URL; the server URL may contain only a protocol (http or https), a domain name, and a port")

# remove trailing slash if present
if self._server_url.endswith("/"):
self._server_url = self._server_url[:-1]

self._notebook = self._config["notebook"]

questions = self._config["questions"]
Expand Down
53 changes: 47 additions & 6 deletions test/test_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,38 +79,38 @@ def do_test(self):
return do_test

test_no_questions = make_test(
{"server_url": ""},
{"server_url": SERVER_URL},
want_error=ValueError("Config file missing required key: questions"),
)

test_no_notebook = make_test(
{"server_url": "", "questions": []},
{"server_url": SERVER_URL, "questions": []},
want_error=ValueError("Config file missing required key: notebook"),
)

test_no_question_identifier = make_test(
{"server_url": "", "notebook": "", "questions": [
{"server_url": SERVER_URL, "notebook": "", "questions": [
{},
]},
want_error=ValueError("Question at index 0 is missing required key 'identifier'"),
)

test_no_question_type = make_test(
{"server_url": "", "notebook": "", "questions": [
{"server_url": SERVER_URL, "notebook": "", "questions": [
{"identifier": "q1"},
]},
want_error=ValueError("Question at index 0 is missing required key 'type'"),
)

test_invalid_question_type = make_test(
{"server_url": "", "notebook": "", "questions": [
{"server_url": SERVER_URL, "notebook": "", "questions": [
{"identifier": "q1", "type": "foo", "question": ""},
]},
want_error=ValueError("Invalid question type: foo"),
)

test_no_question_question = make_test(
{"server_url": "", "notebook": "", "questions": [
{"server_url": SERVER_URL, "notebook": "", "questions": [
{"identifier": "q1", "type": "multiplechoice"},
]},
want_error=ValueError("Question at index 0 is missing required key 'question'"),
Expand Down Expand Up @@ -222,6 +222,47 @@ def test_no_server_url(self):
with make_form(config) as form:
assert form._api_key == "deadbeef"

invalid_url_error = ValueError("Invalid server URL; the server URL may contain only a protocol \\(http or https\\), a domain name, and a port")

@pytest.mark.parametrize(("server_url", "want_auth_domain", "want_error"), (
("http://myapp.com", "http://myapp.com", None),
("http://myapp.com/", "http://myapp.com", None),
("https://myapp.com/", "https://myapp.com", None),
("https://127.0.0.1:5000", "https://127.0.0.1:5000", None),
("http://127.0.0.1:5000/", "http://127.0.0.1:5000", None),
("http://myapp.com/foo", None, invalid_url_error),
("file:///some/path", None, invalid_url_error),
("notaurl", None, invalid_url_error),
))
@responses.activate
def test_server_url_validation(self, server_url, want_auth_domain, want_error):
"""Test server URL validation in the ``Form`` constructor."""
if want_auth_domain is not None:
responses.post(
url = f"{want_auth_domain}/auth",
body = "deadbeef",
match=[matchers.json_params_matcher({"username": "user1", "password": "pass1"})],
)

config = {
"server_url": server_url,
"notebook": NOTEBOOK,
"questions": [
{
"identifier": "q1",
"type": "text",
"question": "A, B, or C?",
}
]
}

cm = nullcontext()
if want_error is not None:
cm = pytest.raises(type(want_error), match=str(want_error))

with cm, make_form(config):
pass

@pytest.mark.parametrize(("identifiers", "want_widgets"), (
(("q1", "q2"), [
{
Expand Down

0 comments on commit 420a3d4

Please sign in to comment.