feat: define BDD tests per RFC6415
This commit is contained in:
parent
76cb235dce
commit
469b002fb4
3 changed files with 311 additions and 0 deletions
44
features/environment.py
Normal file
44
features/environment.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
# SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
from behave import *
|
||||
|
||||
from ftest_common.logger import upload_logs_to_ftest
|
||||
|
||||
|
||||
def before_all(context):
|
||||
context.success = []
|
||||
context.failure = {}
|
||||
|
||||
|
||||
def after_all(context):
|
||||
max_score = 9
|
||||
score = 0
|
||||
|
||||
score = len(context.success)
|
||||
print("\n\n===============")
|
||||
if score == max_score:
|
||||
print("All tests passed")
|
||||
elif score > 0:
|
||||
print(f"Partial success. {score} out of {max_score} tests passed")
|
||||
|
||||
print("Summary:\n")
|
||||
|
||||
logs = ""
|
||||
|
||||
if context.success:
|
||||
print(f"Successful tests:\n")
|
||||
for s in context.success:
|
||||
log = f"[OK] {s}\n"
|
||||
print(log)
|
||||
logs += log
|
||||
|
||||
if context.failure:
|
||||
print(f"\n\nFailed tests:\n")
|
||||
for _, (test, error) in enumerate(context.failure.items()):
|
||||
log = f"[FAIL] {test} failed with error:\n{error}\n-----\n"
|
||||
print(log)
|
||||
logs += log
|
||||
|
||||
upload_logs_to_ftest(score == max_score, logs)
|
15
features/host_meta.feature
Normal file
15
features/host_meta.feature
Normal file
|
@ -0,0 +1,15 @@
|
|||
Feature: Host Meta
|
||||
As defined in https://www.rfc-editor.org/rfc/rfc6415.html
|
||||
|
||||
Scenario:
|
||||
Given A Fediverse server
|
||||
When Querying /.well-known/host-meta
|
||||
Then The page must resolve at port 80 or 443
|
||||
And If both port 80 _and_ 443 work, then they SHOULD return same document
|
||||
And The document SHOULD be served with "application/xrd+xml" media type
|
||||
And The host-meta document root MUST be an "XRD" element
|
||||
And The document SHOULD NOT include a "Subject" element
|
||||
And The document SHOULD include "Link" element
|
||||
And The document's "Link" element should include either "template" or "href" attributes
|
||||
And The document should have at least on 'lrdd' document
|
||||
And Fediverse specific: The lrdd document must include template containing WebFinger well-known URI
|
252
features/steps/host_meta.py
Normal file
252
features/steps/host_meta.py
Normal file
|
@ -0,0 +1,252 @@
|
|||
# SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import os
|
||||
import collections.abc
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
import requests
|
||||
import xmltodict
|
||||
from behave import *
|
||||
|
||||
from ftest_common.env import FTEST_USER
|
||||
from ftest_common.logger import logger
|
||||
from ftest_common.webfinger import get_webfinger
|
||||
from ftest_common.obj import get_ap_obj
|
||||
|
||||
|
||||
def query_host_meta(https: bool):
|
||||
from ftest_common.env import TARGET_HOST
|
||||
|
||||
if https:
|
||||
scheme = "https"
|
||||
else:
|
||||
scheme = "http"
|
||||
parsed_target_host = urlparse(TARGET_HOST)
|
||||
host_meta = urlunparse(
|
||||
(
|
||||
scheme,
|
||||
parsed_target_host.netloc,
|
||||
"/.well-known/host-meta",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
)
|
||||
)
|
||||
logger.debug(f"fetching {host_meta}")
|
||||
resp = requests.get(host_meta)
|
||||
logger.debug(
|
||||
f"host-meta response:\n\nSTATUS: {resp.status_code}\n\nHEADERS:\n {resp.headers}\n\nRESPONSE PAYLOAD:\n{resp.content}"
|
||||
)
|
||||
return resp
|
||||
|
||||
|
||||
@given("A Fediverse server")
|
||||
def step_impl(context):
|
||||
pass
|
||||
|
||||
|
||||
@when("Querying /.well-known/host-meta")
|
||||
def step_impl(context):
|
||||
try:
|
||||
http_res = query_host_meta(False)
|
||||
context.http_res = http_res
|
||||
if http_res.status_code == 200:
|
||||
context.http_host_meta = xmltodict.parse(http_res.content)
|
||||
logger.info("[SUCCESS] host-meta available on 80")
|
||||
context.host_meta = context.http_host_meta
|
||||
context.resp = context.http_res
|
||||
except:
|
||||
logger.debug("[ERROR] host-meta not available on 80")
|
||||
pass
|
||||
|
||||
try:
|
||||
https_res = query_host_meta(True)
|
||||
context.https_res = https_res
|
||||
if https_res.status_code == 200:
|
||||
context.https_host_meta = xmltodict.parse(https_res.content)
|
||||
logger.info("[SUCCESS] host-meta available on 443")
|
||||
context.host_meta = context.https_host_meta
|
||||
context.resp = context.https_res
|
||||
except:
|
||||
logger.debug("[ERROR] host-meta not available on 443")
|
||||
pass
|
||||
|
||||
|
||||
@then("The page must resolve at port 80 or 443")
|
||||
def step_impl(context):
|
||||
name = "Page must resolve at port 80 or 443"
|
||||
|
||||
try:
|
||||
assert (
|
||||
any(
|
||||
[
|
||||
"https_host_meta" in context,
|
||||
"http_host_meta" in context,
|
||||
]
|
||||
)
|
||||
is True
|
||||
), name
|
||||
logger.info("[SUCCESS] {name}")
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
context.failure[name] = e
|
||||
raise e
|
||||
context.success.append(name)
|
||||
|
||||
|
||||
@then("If both port 80 _and_ 443 work, then they SHOULD return same document")
|
||||
def step_impl(context):
|
||||
if all(["https_host_meta" in context, "http_host_meta" in context]):
|
||||
assert (
|
||||
context.host_meta == context.https_host_meta
|
||||
), "Both 80 and 443 returned the same response"
|
||||
|
||||
|
||||
@then('The document SHOULD be served with "application/xrd+xml" media type')
|
||||
def step_impl(context):
|
||||
name = 'The document SHOULD be served with "application/xrd+xml" media type'
|
||||
try:
|
||||
resps = []
|
||||
if all(["https_host_meta" in context, "http_host_meta" in context]):
|
||||
resps = [context.http_res, context.https_res]
|
||||
else:
|
||||
resps = [context.resp]
|
||||
|
||||
for resp in resps:
|
||||
assert (
|
||||
"application/xrd+xml" in resp.headers["Content-Type"]
|
||||
), "Document served with 'application/xrd+xml' media type"
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
context.failure[name] = e
|
||||
raise e
|
||||
context.success.append(name)
|
||||
|
||||
|
||||
@then('The host-meta document root MUST be an "XRD" element')
|
||||
def step_impl(context):
|
||||
name = 'The host-meta document root MUST be an "XRD" element'
|
||||
try:
|
||||
assert len(context.host_meta) == 1, "Only one root element"
|
||||
assert "XRD" in context.host_meta, "'XRX' is the root element"
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
context.failure[name] = e
|
||||
raise e
|
||||
context.success.append(name)
|
||||
|
||||
|
||||
@then('The document SHOULD NOT include a "Subject" element')
|
||||
def step_impl(context):
|
||||
name = 'The document SHOULD NOT include a "Subject" element'
|
||||
try:
|
||||
assert (
|
||||
"Subject" not in context.host_meta["XRD"]
|
||||
), "Document SHOULD NOT include a 'Subject' element"
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
context.failure[name] = e
|
||||
raise e
|
||||
context.success.append(name)
|
||||
|
||||
|
||||
@then('The document SHOULD include "Link" element')
|
||||
def step_impl(context):
|
||||
name = 'The document SHOULD include "Link" element'
|
||||
try:
|
||||
assert (
|
||||
"Link" in context.host_meta["XRD"]
|
||||
), "Document SHOULD include a 'Link' element"
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
context.failure[name] = e
|
||||
raise e
|
||||
context.success.append(name)
|
||||
|
||||
|
||||
@then(
|
||||
'The document\'s "Link" element should include either "template" or "href" attributes'
|
||||
)
|
||||
def step_impl(context):
|
||||
def _check(obj):
|
||||
return any(["@template" in obj, "@href" in obj])
|
||||
|
||||
name = 'The document\'s "Link" element should include either "template" or "href" attributes'
|
||||
try:
|
||||
if isinstance(context.host_meta["XRD"]["Link"], collections.abc.Sequence):
|
||||
for link in context.host_meta["XRD"]["Link"]:
|
||||
assert _check(
|
||||
link
|
||||
), f'The document\'s "Link" element should include either "template" or "href" attributes. Got {link}'
|
||||
else:
|
||||
link = context.host_meta["XRD"]["Link"]
|
||||
assert _check(
|
||||
link
|
||||
), f'The document\'s "Link" element should include either "template" or "href" attributes. Got {link}'
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
context.failure[name] = e
|
||||
raise e
|
||||
context.success.append(name)
|
||||
|
||||
|
||||
@then("The document should have at least on 'lrdd' document")
|
||||
def step_impl(context):
|
||||
def _check(obj):
|
||||
return obj["@rel"] == "lrdd"
|
||||
|
||||
lrdd_found = False
|
||||
name = "The document should have at least on 'lrdd' document"
|
||||
|
||||
if isinstance(context.host_meta["XRD"]["Link"], collections.abc.Sequence):
|
||||
for link in context.host_meta["XRD"]["Link"]:
|
||||
if _check(link):
|
||||
lrdd_found = True
|
||||
break
|
||||
else:
|
||||
link = context.host_meta["XRD"]["Link"]
|
||||
if _check(link):
|
||||
lrdd_found = True
|
||||
|
||||
try:
|
||||
assert lrdd_found is True
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
context.failure[name] = e
|
||||
raise e
|
||||
context.success.append(name)
|
||||
|
||||
|
||||
@then(
|
||||
"Fediverse specific: The lrdd document must include template containing WebFinger well-known URI"
|
||||
)
|
||||
def step_impl(context):
|
||||
def _check(obj):
|
||||
if obj["@rel"] == "lrdd":
|
||||
if "@template" in obj:
|
||||
if "/.well-known/webfinger?resource={uri}" in obj["@template"]:
|
||||
return True
|
||||
return False
|
||||
|
||||
webfinger_found = False
|
||||
name = "Fediverse specific: The lrdd document must include template containing WebFinger well-known URI"
|
||||
|
||||
if isinstance(context.host_meta["XRD"]["Link"], collections.abc.Sequence):
|
||||
for link in context.host_meta["XRD"]["Link"]:
|
||||
if _check(link):
|
||||
webfinger_found = True
|
||||
break
|
||||
else:
|
||||
link = context.host_meta["XRD"]["Link"]
|
||||
if _check(link):
|
||||
webfinger_found = True
|
||||
|
||||
try:
|
||||
assert webfinger_found is True
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
context.failure[name] = e
|
||||
raise e
|
||||
context.success.append(name)
|
Loading…
Reference in a new issue