|
2 | 2 | # |
3 | 3 | # Authors: Tom Kralidis <tomkralidis@gmail.com> |
4 | 4 | # Authors: Benjamin Webb <benjamin.miller.webb@gmail.com> |
| 5 | +# Authors: Bernhard Mallinger <bernhard.mallinger@eox.at> |
5 | 6 | # |
6 | 7 | # Copyright (c) 2024 Tom Kralidis |
7 | 8 | # Copyright (c) 2023 Benjamin Webb |
| 9 | +# Copyright (c) 2024 Bernhard Mallinger |
8 | 10 | # |
9 | 11 | # Permission is hereby granted, free of charge, to any person |
10 | 12 | # obtaining a copy of this software and associated documentation |
|
30 | 32 | # ================================================================= |
31 | 33 |
|
32 | 34 | from datetime import datetime |
33 | | -import time |
34 | | -import unittest |
| 35 | +import json |
| 36 | +import os |
35 | 37 |
|
36 | 38 | from pathlib import Path |
37 | | -from requests import Session |
| 39 | +import pytest |
38 | 40 |
|
39 | 41 | from pygeoapi.util import yaml_load |
| 42 | +from pygeoapi.admin import ( |
| 43 | + Admin, delete_resource, get_config_, get_resource, |
| 44 | + get_resources, patch_config, patch_resource, post_resource, |
| 45 | + put_config, put_resource) |
| 46 | + |
| 47 | +from tests.util import mock_api_request |
40 | 48 |
|
41 | 49 | THISDIR = Path(__file__).resolve().parent |
42 | 50 |
|
43 | 51 |
|
44 | | -class APITest(unittest.TestCase): |
45 | | - def setUp(self): |
46 | | - """setup test fixtures, etc.""" |
| 52 | +@pytest.fixture() |
| 53 | +def admin_config_path(tmp_path, monkeypatch): |
| 54 | + # create a temporary config file because the test will modify it in place |
| 55 | + config_path = tmp_path / "config.yml" |
| 56 | + config_path.write_text( |
| 57 | + (Path(THISDIR) / "pygeoapi-test-config-admin.yml").read_text() |
| 58 | + ) |
| 59 | + |
| 60 | + # get_config() reads the config directly, so we need to patch os.environ |
| 61 | + monkeypatch.setitem(os.environ, "PYGEOAPI_CONFIG", str(config_path)) |
| 62 | + |
| 63 | + return config_path |
47 | 64 |
|
48 | | - self.admin_endpoint = 'http://localhost:5000/admin/config' |
49 | | - self.http = Session() |
50 | | - self.http.headers.update({ |
51 | | - 'Content-type': 'application/json', |
52 | | - 'Accept': 'application/json' |
53 | | - }) |
54 | 65 |
|
55 | | - def tearDown(self): |
56 | | - """return to pristine state""" |
| 66 | +def reload_api(config_path, monkeypatch, openapi): |
| 67 | + # initialize admin api with current config contents |
| 68 | + with config_path.open() as config_handle: |
| 69 | + admin = Admin(yaml_load(config_handle), openapi) |
57 | 70 |
|
58 | | - pass |
| 71 | + # the config paths are set on a class level, so they are set before |
| 72 | + # we can patch os.environ and need to patch them directly |
| 73 | + monkeypatch.setattr(admin, "PYGEOAPI_CONFIG", str(config_path)) |
| 74 | + openapi_filename = str(config_path).replace("config.yml", "openapi.yml") |
| 75 | + monkeypatch.setattr(admin, "PYGEOAPI_OPENAPI", openapi_filename) |
59 | 76 |
|
60 | | - def test_admin(self): |
| 77 | + return admin |
61 | 78 |
|
62 | | - url = f'{self.admin_endpoint}' |
63 | | - content = self.http.get(url).json() |
64 | 79 |
|
65 | | - keys = ['logging', 'metadata', 'resources', 'server'] |
66 | | - self.assertEqual(sorted(content.keys()), keys) |
| 80 | +def test_admin(monkeypatch, admin_config_path, openapi): |
67 | 81 |
|
68 | | - # PUT configuration |
69 | | - with get_abspath('admin-put.json').open() as fh: |
70 | | - put = fh.read() |
71 | | - response = self.http.put(url, data=put) |
72 | | - self.assertEqual(response.status_code, 204) |
| 82 | + admin_api = reload_api(admin_config_path, monkeypatch, openapi) |
73 | 83 |
|
74 | | - # NOTE: we sleep 5 between CRUD requests so as to let gunicorn |
75 | | - # restart with the refreshed configuration |
76 | | - time.sleep(5) |
| 84 | + req = mock_api_request() |
| 85 | + headers, status_code, content = get_config_(admin_api, req) |
77 | 86 |
|
78 | | - content = self.http.get(url).json() |
79 | | - self.assertEqual(content['logging']['level'], 'INFO') |
| 87 | + keys = {'logging', 'metadata', 'resources', 'server'} |
| 88 | + assert set(json.loads(content).keys()) == keys |
80 | 89 |
|
81 | | - # PATCH configuration |
82 | | - with get_abspath('admin-patch.json').open() as fh: |
83 | | - patch = fh.read() |
| 90 | + # PUT configuration |
| 91 | + with get_abspath('admin-put.json').open() as fh: |
| 92 | + put = fh.read() |
| 93 | + req = mock_api_request(data=put) |
| 94 | + headers, status_code, content = put_config(admin_api, req) |
| 95 | + assert status_code == 204 |
84 | 96 |
|
85 | | - response = self.http.patch(url, data=patch) |
86 | | - self.assertEqual(response.status_code, 204) |
| 97 | + admin_api = reload_api(admin_config_path, monkeypatch, openapi) |
87 | 98 |
|
88 | | - time.sleep(5) |
| 99 | + req = mock_api_request() |
| 100 | + headers, status_code, content = get_config_(admin_api, req) |
| 101 | + assert json.loads(content)['logging']['level'] == 'INFO' |
89 | 102 |
|
90 | | - content = self.http.get(url).json() |
91 | | - self.assertEqual(content['logging']['level'], 'DEBUG') |
| 103 | + # PATCH configuration |
| 104 | + with get_abspath('admin-patch.json').open() as fh: |
| 105 | + patch = fh.read() |
92 | 106 |
|
93 | | - def test_resources_crud(self): |
| 107 | + req = mock_api_request(data=patch) |
| 108 | + headers, status_code, content = patch_config(admin_api, req) |
| 109 | + assert status_code == 204 |
94 | 110 |
|
95 | | - url = f'{self.admin_endpoint}/resources' |
96 | | - content = self.http.get(url).json() |
97 | | - self.assertEqual(len(content.keys()), 1) |
| 111 | + admin_api = reload_api(admin_config_path, monkeypatch, openapi) |
98 | 112 |
|
99 | | - # POST a new resource |
100 | | - with get_abspath('resource-post.json').open() as fh: |
101 | | - post_data = fh.read() |
| 113 | + assert json.loads(content)['logging']['level'] == 'DEBUG' |
102 | 114 |
|
103 | | - response = self.http.post(url, data=post_data) |
104 | | - self.assertEqual(response.status_code, 201) |
105 | | - self.assertEqual(response.text, |
106 | | - 'Location: /admin/config/resources/data2') |
107 | 115 |
|
108 | | - # NOTE: we sleep 5 between CRUD requests so as to let gunicorn |
109 | | - # restart with the refreshed configuration |
110 | | - time.sleep(5) |
| 116 | +def test_resources_crud(monkeypatch, admin_config_path, openapi): |
| 117 | + admin_api = reload_api(admin_config_path, monkeypatch, openapi) |
111 | 118 |
|
112 | | - content = self.http.get(url).json() |
113 | | - self.assertEqual(len(content.keys()), 2) |
| 119 | + empty_req = mock_api_request() |
| 120 | + headers, status_code, content = get_resources(admin_api, empty_req) |
| 121 | + assert len(json.loads(content).keys()) == 1 |
114 | 122 |
|
115 | | - with get_abspath('../../pygeoapi-test-config-admin.yml').open() as fh: |
116 | | - d = yaml_load(fh) |
117 | | - temporal_extent_begin = d['resources']['data2']['extents']['temporal']['begin'] # noqa |
118 | | - self.assertIsInstance(temporal_extent_begin, datetime) |
| 123 | + # POST a new resource |
| 124 | + with get_abspath('resource-post.json').open() as fh: |
| 125 | + post_data = fh.read() |
119 | 126 |
|
120 | | - # PUT an existing resource |
121 | | - url = f'{self.admin_endpoint}/resources/data2' |
122 | | - with get_abspath('resource-put.json').open() as fh: |
123 | | - post_data = fh.read() |
| 127 | + req = mock_api_request(data=post_data) |
| 128 | + headers, status_code, content = post_resource(admin_api, req) |
| 129 | + assert status_code == 201 |
| 130 | + assert content == 'Location: //data2' |
124 | 131 |
|
125 | | - response = self.http.put(url, data=post_data) |
126 | | - self.assertEqual(response.status_code, 204) |
| 132 | + admin_api = reload_api(admin_config_path, monkeypatch, openapi) |
127 | 133 |
|
128 | | - time.sleep(5) |
| 134 | + headers, status_code, content = get_resources(admin_api, empty_req) |
| 135 | + assert len(json.loads(content).keys()) == 2 |
129 | 136 |
|
130 | | - content = self.http.get(url).json() |
131 | | - self.assertEqual(content['title']['en'], |
132 | | - 'Data assets, updated by HTTP PUT') |
| 137 | + d = yaml_load(admin_config_path.read_text()) |
| 138 | + temporal_extent_begin = d['resources']['data2']['extents']['temporal']['begin'] # noqa |
| 139 | + assert isinstance(temporal_extent_begin, datetime) |
133 | 140 |
|
134 | | - # PATCH an existing resource |
135 | | - url = f'{self.admin_endpoint}/resources/data2' |
136 | | - with get_abspath('resource-patch.json').open() as fh: |
137 | | - post_data = fh.read() |
| 141 | + # PUT an existing resource |
| 142 | + with get_abspath('resource-put.json').open() as fh: |
| 143 | + post_data = fh.read() |
138 | 144 |
|
139 | | - response = self.http.patch(url, data=post_data) |
140 | | - self.assertEqual(response.status_code, 204) |
| 145 | + req = mock_api_request(data=post_data) |
| 146 | + headers, status_code, content = put_resource(admin_api, req, 'data2') |
| 147 | + assert status_code == 204 |
141 | 148 |
|
142 | | - time.sleep(5) |
| 149 | + headers, status_code, content = get_resource(admin_api, empty_req, 'data2') |
| 150 | + assert ( |
| 151 | + json.loads(content)['title']['en'] == |
| 152 | + 'Data assets, updated by HTTP PUT' |
| 153 | + ) |
143 | 154 |
|
144 | | - content = self.http.get(url).json() |
145 | | - self.assertEqual(content['title']['en'], |
146 | | - 'Data assets, updated by HTTP PATCH') |
| 155 | + # PATCH an existing resource |
| 156 | + with get_abspath('resource-patch.json').open() as fh: |
| 157 | + post_data = fh.read() |
147 | 158 |
|
148 | | - # DELETE an existing new resource |
149 | | - response = self.http.delete(url) |
150 | | - self.assertEqual(response.status_code, 204) |
| 159 | + req = mock_api_request(data=post_data) |
| 160 | + headers, status_code, content = patch_resource(admin_api, req, 'data2') |
| 161 | + assert status_code == 204 |
151 | 162 |
|
152 | | - time.sleep(5) |
| 163 | + headers, status_code, content = get_resource(admin_api, empty_req, 'data2') |
| 164 | + assert ( |
| 165 | + json.loads(content)['title']['en'] == |
| 166 | + 'Data assets, updated by HTTP PATCH' |
| 167 | + ) |
153 | 168 |
|
154 | | - url = f'{self.admin_endpoint}/resources' |
155 | | - content = self.http.get(url).json() |
156 | | - self.assertEqual(len(content.keys()), 1) |
| 169 | + # DELETE an existing new resource |
| 170 | + headers, status_code, content = \ |
| 171 | + delete_resource(admin_api, empty_req, 'data2') |
| 172 | + assert status_code == 204 |
| 173 | + |
| 174 | + headers, status_code, content = get_resources(admin_api, empty_req) |
| 175 | + assert len(json.loads(content).keys()) == 1 |
157 | 176 |
|
158 | 177 |
|
159 | 178 | def get_abspath(filepath): |
160 | 179 | """helper function absolute file access""" |
161 | 180 |
|
162 | 181 | return Path(THISDIR) / 'data' / 'admin' / filepath |
163 | | - |
164 | | - |
165 | | -if __name__ == '__main__': |
166 | | - unittest.main() |
|
0 commit comments