Skip to content

Commit 81073c2

Browse files
committed
[client-python] Add tests multi-tenant
1 parent 9e3ed3f commit 81073c2

8 files changed

Lines changed: 282 additions & 0 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Feature: URL normalization in OpenAEV client
2+
3+
Scenario Outline: URL normalization combines base_url and path correctly
4+
Given an OpenAEV client with base_url "<base_url>"
5+
When I build the URL for "<path>"
6+
Then the resulting URL should be "<expected>"
7+
8+
Examples:
9+
| base_url | path | expected |
10+
| base_url | path | base_url/api/path |
11+
| base_url/ | /path | base_url/api/path |
12+
| base_url// | //path | base_url/api/path |
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import pytest
2+
3+
from pyoaev import OpenAEV
4+
5+
6+
@pytest.mark.parametrize(
7+
"base_url, input_path, expected",
8+
[
9+
(
10+
"base_url",
11+
"path",
12+
"base_url/api/path",
13+
),
14+
(
15+
"base_url/",
16+
"/path",
17+
"base_url/api/path",
18+
),
19+
(
20+
"base_url//",
21+
"//path",
22+
"base_url/api/path",
23+
),
24+
],
25+
ids=[
26+
"clean-base-url-and-relative-path",
27+
"base-url-trailing-slash",
28+
"base-url-double-slash-and-path-double-slash",
29+
],
30+
)
31+
def test_url_normalization(base_url, input_path, expected):
32+
client = OpenAEV(
33+
url=base_url,
34+
token="token",
35+
tenant_id=None,
36+
)
37+
result = client._build_url(input_path)
38+
assert result == expected
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Feature: Multi-tenant API routing in OpenAEV client
2+
3+
Scenario: Full URL bypasses tenant routing
4+
Given an OpenAEV client with tenant_id "2cffad3a-0001-4078-b0e2-ef74274022c3"
5+
When I build the URL for "https://external.service/api/path"
6+
Then the resulting URL should be "https://external.service/api/path"
7+
8+
Scenario Outline: Relative path routing behavior
9+
Given an OpenAEV client with tenant_id "<tenant_id>"
10+
When I build the URL for "/path"
11+
Then the resulting URL should be "<output>"
12+
13+
Examples:
14+
| tenant_id | output |
15+
| None | base_url/api/path |
16+
| 2cffad3a-0001-4078-b0e2-ef74274022c3 | base_url/api/tenants/2cffad3a-0001-4078-b0e2-ef74274022c3/path |
17+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Feature: Tenant propagation in BaseDaemon API client initialization
2+
3+
Scenario Outline: BaseDaemon propagates tenant_id correctly from configuration
4+
Given a daemon configuration with <tenant_id>
5+
When the BaseDaemon is initialized
6+
Then the API client should be created with tenant_id "<expected_tenant_id>"
7+
8+
Examples:
9+
| tenant_id | expected_tenant_id |
10+
| Missing tenant key | None |
11+
| None | None |
12+
| 2cffad3a-0001-4078-b0e2-ef74274022c3 | 2cffad3a-0001-4078-b0e2-ef74274022c3 |
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Feature: searchTargets API routing with and without tenant_id
2+
3+
Scenario Outline: searchTargets routing behavior
4+
Given an OpenAEV client with tenant_id "<tenant_id>"
5+
And a valid SearchPaginationInput
6+
When I call searchTargets on endpoint
7+
Then the request URL should be "<expected_url>"
8+
9+
Examples:
10+
| tenant_id | expected_url |
11+
| None | url/api/endpoints/targets |
12+
| 2cffad3a-0001-4078-b0e2-ef74274022c3 | url/api/tenants/2cffad3a-0001-4078-b0e2-ef74274022c3/endpoints/targets |
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from uuid import UUID
2+
3+
import pytest
4+
5+
from pyoaev import OpenAEV
6+
7+
8+
@pytest.mark.parametrize(
9+
"tenant_id, path, expected",
10+
[
11+
(
12+
None,
13+
"/path",
14+
"base_url/api/path",
15+
),
16+
(
17+
UUID("2cffad3a-0001-4078-b0e2-ef74274022c3"),
18+
"/path",
19+
"base_url/api/tenants/2cffad3a-0001-4078-b0e2-ef74274022c3/path",
20+
),
21+
(
22+
None,
23+
"https://external.service/api/path",
24+
"https://external.service/api/path",
25+
),
26+
(
27+
UUID("2cffad3a-0001-4078-b0e2-ef74274022c3"),
28+
"https://external.service/api/path",
29+
"https://external.service/api/path",
30+
),
31+
],
32+
ids=[
33+
"legacy-relative-path",
34+
"tenant-relative-path",
35+
"legacy-full-url-bypass",
36+
"tenant-full-url-bypass",
37+
],
38+
)
39+
def test_build_url_behavior(tenant_id, path, expected):
40+
client = OpenAEV(
41+
"base_url",
42+
"token",
43+
tenant_id=tenant_id,
44+
)
45+
result = client._build_url(path)
46+
assert result == expected
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from unittest.mock import MagicMock
2+
from uuid import UUID
3+
4+
import pytest
5+
6+
from pyoaev.daemons.base_daemon import BaseDaemon
7+
8+
9+
class DummyDaemon(BaseDaemon):
10+
def _setup(self):
11+
pass
12+
13+
def _start_loop(self):
14+
pass
15+
16+
17+
@pytest.mark.parametrize(
18+
"config_map, expected_tenant",
19+
[
20+
(
21+
{
22+
"openaev_url": "url",
23+
"openaev_token": "token",
24+
},
25+
None,
26+
),
27+
(
28+
{
29+
"openaev_url": "url",
30+
"openaev_token": "token",
31+
"openaev_tenant_id": None,
32+
},
33+
None,
34+
),
35+
(
36+
{
37+
"openaev_url": "url",
38+
"openaev_token": "token",
39+
"openaev_tenant_id": UUID("2cffad3a-0001-4078-b0e2-ef74274022c3"),
40+
},
41+
UUID("2cffad3a-0001-4078-b0e2-ef74274022c3"),
42+
),
43+
],
44+
ids=[
45+
"missing_tenant_key",
46+
"explicit_none_tenant",
47+
"valid_uuid_tenant",
48+
],
49+
)
50+
def test_default_api_client_propagates_tenant_id(
51+
monkeypatch, config_map, expected_tenant
52+
):
53+
captured = {}
54+
55+
def fake_client(url, token, tenant_id=None):
56+
captured["url"] = url
57+
captured["token"] = token
58+
captured["tenant_id"] = tenant_id
59+
return MagicMock()
60+
61+
monkeypatch.setattr("pyoaev.daemons.base_daemon.OpenAEV", fake_client)
62+
63+
config = MagicMock()
64+
config.get.side_effect = lambda key: config_map.get(key)
65+
66+
daemon = DummyDaemon(configuration=config)
67+
assert daemon.api is not None
68+
69+
assert "openaev_tenant_id" in config_map or expected_tenant is None
70+
assert captured["url"] == "url"
71+
assert captured["token"] == "token"
72+
assert captured["tenant_id"] == expected_tenant
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from unittest.mock import MagicMock
2+
from uuid import UUID
3+
4+
import pytest
5+
6+
from pyoaev import OpenAEV
7+
from pyoaev.apis.inputs.search import Filter, FilterGroup, SearchPaginationInput
8+
9+
10+
class MockResponse:
11+
def __init__(self, json_data=None, status_code=200):
12+
self._json_data = json_data
13+
self.status_code = status_code
14+
self.history = None
15+
self.content = None
16+
self.headers = {"Content-Type": "application/json"}
17+
18+
def json(self):
19+
return self._json_data or {}
20+
21+
22+
def build_search_input():
23+
return SearchPaginationInput(
24+
0,
25+
20,
26+
FilterGroup(
27+
"or",
28+
[
29+
Filter(
30+
"targets",
31+
"and",
32+
"eq",
33+
["target_1", "target_2", "target_3"],
34+
)
35+
],
36+
),
37+
None,
38+
None,
39+
)
40+
41+
42+
@pytest.mark.parametrize(
43+
"tenant_id, expected_url",
44+
[
45+
(
46+
None,
47+
"url/api/endpoints/targets",
48+
),
49+
(
50+
UUID("2cffad3a-0001-4078-b0e2-ef74274022c3"),
51+
"url/api/tenants/2cffad3a-0001-4078-b0e2-ef74274022c3/endpoints/targets",
52+
),
53+
],
54+
ids=[
55+
"legacy_routing_no_tenant",
56+
"tenant_routing_enabled",
57+
],
58+
)
59+
def test_search_input_correctly_serialised(monkeypatch, tenant_id, expected_url):
60+
mock_request = MagicMock(return_value=MockResponse())
61+
monkeypatch.setattr("requests.Session.request", mock_request)
62+
63+
api_client = OpenAEV("url", "token", tenant_id=tenant_id)
64+
search_input = build_search_input()
65+
expected_json = search_input.to_dict()
66+
api_client.endpoint.searchTargets(search_input)
67+
68+
assert mock_request.call_count == 1
69+
_, kwargs = mock_request.call_args
70+
71+
assert kwargs["method"] == "post"
72+
assert kwargs["json"] == expected_json
73+
assert kwargs["url"] == expected_url

0 commit comments

Comments
 (0)