Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework how functions from edr, ogcapi and profile is collected and ru… #33

Merged
merged 14 commits into from
Nov 29, 2024
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## What is sedr?

An experimental validator for OGC EDR APIs using schemathesis. Main focus will be on the Rodeo Profile, which is a subset of the OGC EDR API.
An experimental validator for OGC EDR APIs. Main focus will be on the Rodeo Profile, which is a subset of the OGC EDR API.

## Who is responsible?

Expand Down Expand Up @@ -40,17 +40,28 @@ Run manually as noted in [Test it out](#test-it-out), or add it to your CI using

## Overview of architecture

- __init__ includes tests from ogcapi, edrreq and rodeoprofile at startup. Tests are categorized as landing, conformance and collection.
- Landing and conformance tests are run first, in the preflight phase.
- Then schemathesis will validate the OpenAPI spec and run lots of automatic tests, including fuzzing of query parameters. Collection tests are run during this phase.

## Documentation

- Use --rodeo-profile to force a test against the profile
- Use --strict to also fail on SHOULD requirements.
- Use --log-file debug.log to get all output. For docker variant, see [Test it out](#test-it-out).

### Limitations

- Assuming Openapi 3.1
- Assuming OGC EDR API version 1.2 (draft)
- Few, basic tests for now
- Will focus more on profiles (limitations within the EDR spec) like <https://github.com/EURODEO/rodeo-edr-profile> than the full EDR spec.
- Use --rodeo-profile to force a test against the profile (will happen automatically if conformance includes the profile)

### Understanding errors
### Testing the sedr code to look for regressions

For development, source a venv and run `tox p` to run all tests.

### Understanding errors from schemathesis

For each "FAILED" line, you can scroll back to see the full error and, if relevant, with a curl-example to reproduce it.

Expand Down Expand Up @@ -96,7 +107,7 @@ ERROR sedr/schemat.py - schemathesis.exceptions.SchemaError: Failed to load sche

#### Wrong API version / missing conformance link

Sedr supports EDR 1.1, but the API is EDR 1.0.
Sedr wants EDR 1.1, but the API is EDR 1.0.

```python
if not requirementA2_2_A5:
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ schemathesis~=3.34
pytest~=8.3
shapely~=2.0
requests~=2.32.0
rich~=13.8.0
27 changes: 26 additions & 1 deletion sedr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

__author__ = "Lars Falk-Petersen"
__license__ = "GPL-3.0"
__version__ = "v0.7.9"
__version__ = "v0.8.0"

import sys
import pytest

import util
import preflight
import edreq12 as edreq
import ogcapi10 as ogcapi
import rodeoprofile10 as rodeoprofile


def run_schemat() -> None:
Expand All @@ -18,6 +22,27 @@ def run_schemat() -> None:

def main() -> None:
"""Run the main program."""

# Collect tests to run
util.test_functions["landing"] += edreq.tests_landing + ogcapi.tests_landing
util.test_functions["conformance"] += (
edreq.tests_conformance + ogcapi.tests_conformance
)
util.test_functions["collection"] += (
edreq.tests_collection + ogcapi.tests_collections
)
if util.args.rodeo_profile:
util.logger.info(
"Including tests for Rodeo profile %s", rodeoprofile.conformance_url
)
util.test_functions["landing"] += rodeoprofile.tests_landing
util.test_functions["conformance"] += rodeoprofile.tests_conformance
util.test_functions["collection"] += rodeoprofile.tests_collection

# TODO: include profile tests based on conformance_url, https://github.com/metno/sedr/issues/32
# if rodeoprofile.conformance_url in conformance_json["conformsTo"]:
# util.args.rodeo_profile = True

if preflight.main():
run_schemat()
else:
Expand Down
144 changes: 0 additions & 144 deletions sedr/edreq11.py

This file was deleted.

71 changes: 40 additions & 31 deletions sedr/edreq12.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""EDR requirements."""

from collections.abc import Callable
import util

edr_version = "1.2"
Expand All @@ -16,7 +17,7 @@
]


def requirementA2_2_A5(jsondata: dict, siteurl="") -> tuple[bool, str]:
def requirementA2_2_A5(jsondata: dict) -> tuple[bool, str]:
"""
OGC API - Environmental Data Retrieval Standard
Version: 1.2
Expand All @@ -26,40 +27,21 @@ def requirementA2_2_A5(jsondata: dict, siteurl="") -> tuple[bool, str]:
jsondata should be the "conformsTo"-part of the conformance page.
"""
spec_url = f"{edr_root_url}#req_core_conformance"

if "conformsTo" not in jsondata:
return (
False,
f"Conformance page <{siteurl}conformance> does not contain a "
f"conformsTo attribute. See <{spec_url}> for more info.",
return False, (
f"Conformance page does not contain a "
f"conformsTo attribute. See <{spec_url}> for more info."
)

for url in conformance_urls:
if url not in jsondata["conformsTo"]:
return (
False,
f"Conformance page <{siteurl}conformance> does not contain "
f"the core edr class {url}. See <{spec_url}> for more info.",
return False, (
f"Conformance page does not contain "
f"the core edr class {url}. See <{spec_url}> for more info."
)

util.logger.debug(
"requirementA2_2_A5: conformance page contains the required EDR classes."
)
return True, ""


def requirementA2_2_A7(version: int) -> tuple[bool, str]:
"""
OGC API - Environmental Data Retrieval Standard
Version: 1.2
Requirement Annex A2.2 A7

Check if HTTP1.1 was used.
"""
spec_url = f"{edr_root_url}#_req_core_http"
if version == 11:
util.logger.debug("requirementA2_2_A7 HTTP version 1.1 was used.")
return True, ""

return False, f"HTTP version 1.1 was not used. See <{spec_url}> for more info."
return True, "Conformance page contains the required EDR classes."


def requirementA11_1(jsondata: dict) -> tuple[bool, str]:
Expand All @@ -77,8 +59,7 @@ def requirementA11_1(jsondata: dict) -> tuple[bool, str]:
if (
"oas31" in url or "oas30" in url # TODO: oas30 should be removed
):
util.logger.debug("requirementA11_1 Found openapi class <%s>", url)
return True, url
return True, f"Found openapi class <{url}>. "
return (
False,
f"OpenAPI version {util.args.openapi_version} and version in "
Expand All @@ -90,3 +71,31 @@ def requirementA11_1(jsondata: dict) -> tuple[bool, str]:
f"Conformance page /conformance does not contain an openapi class. "
f"See <{spec_url}> for more info.",
)


def requrementA5_2(jsondata: dict) -> tuple[bool, str]:
"""
OGC API - Environmental Data Retrieval Standard
Version: 1.2
Requirement A5.2

Check extent spatial bbox
"""

spec_url = f"{edr_root_url}#req_core_rc-bbox-definition"

extent = None
extent = util.parse_spatial_bbox(jsondata)
if extent == [] or len(extent) > 1 or not isinstance(extent, list):
return (
False,
f"Extent→spatial→bbox should be a list of bboxes with exactly "
f"one bbox in, found {len(extent)} in collection "
f"<{jsondata['id']}>. See {spec_url} for more info.",
)
return True, f"Extent→spatial→bbox for collection is {extent}"


tests_landing: list[Callable[[dict], tuple[bool, str]]] = []
tests_conformance = [requirementA2_2_A5, requirementA11_1]
tests_collection = [requrementA5_2]
Loading