sefrone-api-e2e 1.3.6__tar.gz → 1.3.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/PKG-INFO +2 -1
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/__init__.py +2 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/api_e2e_manager.py +7 -5
- sefrone_api_e2e-1.3.7/sefrone_api_e2e/runner_excel.py +215 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e.egg-info/PKG-INFO +2 -1
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e.egg-info/SOURCES.txt +1 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e.egg-info/requires.txt +1 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/setup.py +3 -2
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/README.md +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/0_check_features.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/1_normal_auth_flow.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/2_account_endpoints.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/3_admin_account_endpoints.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/4_workspace_endpoints.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/5_apikey_endpoints.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/6_project_endpoints.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/7_email_2fa_flow.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/8_temporary_passwords_flow.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Auth/9_project_account_access_control_flow.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Oidc/1_relying_party_admin.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Oidc/2_authorization_code_flow.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Oidc/3_disable_propagation_flow.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e/Scenarios/Oidc/4_multi_project_isolation.yaml +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/e2e_scenarios_manager.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/env_patch.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/json_mock_server.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/runner_assets.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/runner_csv.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/runner_email_mock.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/runner_rest_api.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/runner_satim_mock.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/runner_webhook_mock.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e/step_runner.py +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e.egg-info/dependency_links.txt +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e.egg-info/top_level.txt +0 -0
- {sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sefrone_api_e2e
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.7
|
|
4
4
|
Summary: A Python package to provide e2e testing helpers for sefrone API projects
|
|
5
5
|
Home-page: https://bitbucket.org/sefrone/sefrone_pypi
|
|
6
6
|
Author: Sefrone
|
|
@@ -12,6 +12,7 @@ Requires-Python: >=3.7
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
Requires-Dist: PyYAML>=6.0.1
|
|
14
14
|
Requires-Dist: requests
|
|
15
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
15
16
|
Dynamic: author
|
|
16
17
|
Dynamic: author-email
|
|
17
18
|
Dynamic: classifier
|
|
@@ -6,6 +6,7 @@ from .runner_webhook_mock import WebhookMockRunner
|
|
|
6
6
|
from .runner_assets import AssetsRunner
|
|
7
7
|
from .runner_satim_mock import SatimMockRunner
|
|
8
8
|
from .runner_csv import CsvRunner
|
|
9
|
+
from .runner_excel import ExcelRunner
|
|
9
10
|
from .json_mock_server import JsonMockServer
|
|
10
11
|
from .e2e_scenarios_manager import E2EScenariosManager
|
|
11
12
|
|
|
@@ -21,6 +22,7 @@ __all__ = [
|
|
|
21
22
|
"AssetsRunner",
|
|
22
23
|
"SatimMockRunner",
|
|
23
24
|
"CsvRunner",
|
|
25
|
+
"ExcelRunner",
|
|
24
26
|
"JsonMockServer",
|
|
25
27
|
"E2EScenariosManager",
|
|
26
28
|
]
|
|
@@ -14,6 +14,7 @@ from .step_runner import StepRunner, RunContext
|
|
|
14
14
|
from .runner_rest_api import RestApiRunner
|
|
15
15
|
from .runner_assets import AssetsRunner
|
|
16
16
|
from .runner_csv import CsvRunner
|
|
17
|
+
from .runner_excel import ExcelRunner
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class ScenarioSkippedException(Exception):
|
|
@@ -42,11 +43,12 @@ class ApiE2ETestsManager:
|
|
|
42
43
|
ApiE2ETestsManager.global_store = {}
|
|
43
44
|
ApiE2ETestsManager.scenario_results = []
|
|
44
45
|
|
|
45
|
-
# RestApiRunner (default), AssetsRunner and
|
|
46
|
-
# (no external config needed), so they're always
|
|
47
|
-
# construct and pass them in. Runners
|
|
48
|
-
# mocks, etc.) still come in
|
|
49
|
-
|
|
46
|
+
# RestApiRunner (default), AssetsRunner, CsvRunner and ExcelRunner are
|
|
47
|
+
# stateless/self-contained (no external config needed), so they're always
|
|
48
|
+
# available without callers having to construct and pass them in. Runners
|
|
49
|
+
# that need caller-supplied config (mail/webhook mocks, etc.) still come in
|
|
50
|
+
# through the `runners` param.
|
|
51
|
+
active_runners: List[StepRunner] = [RestApiRunner(), AssetsRunner(), CsvRunner(), ExcelRunner()]
|
|
50
52
|
if runners:
|
|
51
53
|
# make sure to distinct on runner name
|
|
52
54
|
active_runners.extend(runners)
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import os
|
|
3
|
+
from typing import Any, Dict, List, Set
|
|
4
|
+
|
|
5
|
+
from openpyxl import Workbook, load_workbook
|
|
6
|
+
|
|
7
|
+
from .step_runner import StepRunner, RunContext, StepResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ExcelRunner(StepRunner):
|
|
11
|
+
"""
|
|
12
|
+
Runner for generating and reading .xlsx files during scenario execution, so a
|
|
13
|
+
batch-upload fixture (e.g. a tenant import file) can be built with values tied
|
|
14
|
+
to the scenario's own dynamically-created data (workspace, register, etc.)
|
|
15
|
+
instead of relying on a static file that silently drifts out of sync with
|
|
16
|
+
whatever the setup steps actually created, and so a downloaded/generated
|
|
17
|
+
workbook can be asserted on by structured row/column content.
|
|
18
|
+
|
|
19
|
+
This runner never makes HTTP requests itself -- pair a normal HTTP step's
|
|
20
|
+
``files:`` upload (already supported by the default rest runner) with a
|
|
21
|
+
``generate`` step to build the fixture first, or pair a ``save_file:``
|
|
22
|
+
download with a ``read`` step to assert on what came back.
|
|
23
|
+
|
|
24
|
+
Step syntax — generate (write an .xlsx fixture with specific content)::
|
|
25
|
+
|
|
26
|
+
- name: "Generate tenant batch fixture"
|
|
27
|
+
runner: excel
|
|
28
|
+
excel:
|
|
29
|
+
action: generate
|
|
30
|
+
path: "./e2e/tmp/tenants_batch_{$stored.seed_id}.xlsx"
|
|
31
|
+
sheet_name: "Tenants" # optional, default: "Sheet1"
|
|
32
|
+
headers: ["wilaya", "code caisse", "matricule locataire", "nom"]
|
|
33
|
+
rows:
|
|
34
|
+
- ["Alger", "{$stored.register_number}", "E2E-{$stored.seed_id}-001", "Benali"]
|
|
35
|
+
|
|
36
|
+
Step syntax — read (parse an existing/just-downloaded .xlsx file)::
|
|
37
|
+
|
|
38
|
+
- name: "Verify exported workbook content"
|
|
39
|
+
runner: excel
|
|
40
|
+
excel:
|
|
41
|
+
action: read # optional, default: read
|
|
42
|
+
path: "./e2e/tmp/export_{$stored.seed_id}.xlsx"
|
|
43
|
+
sheet_name: "Tenants" # optional, defaults to the first sheet
|
|
44
|
+
assertions:
|
|
45
|
+
- "{{excel.row_count}} == 3"
|
|
46
|
+
- "{{excel.headers}} include 'code caisse'"
|
|
47
|
+
- "{{excel.rows[0].nom}} == 'Benali'"
|
|
48
|
+
save:
|
|
49
|
+
first_row_name: "{{excel.rows[0].nom}}"
|
|
50
|
+
|
|
51
|
+
``excel.rows`` in the read context is a list of dicts keyed by header name
|
|
52
|
+
(e.g. ``{{excel.rows[0].nom}}``); ``excel.raw_rows`` is the same data as
|
|
53
|
+
plain lists (positional access) for files with duplicate/blank headers.
|
|
54
|
+
Every cell value is read back as a **string** (matching CsvRunner), so
|
|
55
|
+
compare numeric-looking values against a quoted literal (``== '500'``, not
|
|
56
|
+
``== 500``). Runner-generated files are removed during teardown, matching
|
|
57
|
+
AssetsRunner/CsvRunner; files a ``read`` step merely reads (e.g. ones saved
|
|
58
|
+
by an HTTP step) are left alone since this runner doesn't own them.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self):
|
|
62
|
+
self._generated_paths: Set[str] = set()
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def name(self) -> str:
|
|
66
|
+
return "excel"
|
|
67
|
+
|
|
68
|
+
def handles(self, step: dict) -> bool:
|
|
69
|
+
return "excel" in step and "method" not in step
|
|
70
|
+
|
|
71
|
+
def execute(self, step: dict, ctx: RunContext) -> StepResult:
|
|
72
|
+
try:
|
|
73
|
+
spec = step.get("excel")
|
|
74
|
+
if not isinstance(spec, dict):
|
|
75
|
+
return StepResult.failed("excel step requires an 'excel' object")
|
|
76
|
+
|
|
77
|
+
action = str(spec.get("action", "read")).strip().lower()
|
|
78
|
+
|
|
79
|
+
if action == "read":
|
|
80
|
+
return self._read(step, spec, ctx)
|
|
81
|
+
if action == "generate":
|
|
82
|
+
return self._generate(spec, ctx)
|
|
83
|
+
|
|
84
|
+
return StepResult.failed(f"Unsupported excel action: '{action}'. Supported: 'read', 'generate'")
|
|
85
|
+
except AssertionError as e:
|
|
86
|
+
return StepResult.failed(str(e))
|
|
87
|
+
except Exception as e:
|
|
88
|
+
return StepResult.failed(str(e))
|
|
89
|
+
|
|
90
|
+
def teardown(self) -> None:
|
|
91
|
+
for path in list(self._generated_paths):
|
|
92
|
+
try:
|
|
93
|
+
os.remove(path)
|
|
94
|
+
print(f"[excel] Removed: {path}")
|
|
95
|
+
except FileNotFoundError:
|
|
96
|
+
pass
|
|
97
|
+
except Exception as e:
|
|
98
|
+
print(f"[excel] Failed removing '{path}': {e}")
|
|
99
|
+
self._generated_paths.clear()
|
|
100
|
+
|
|
101
|
+
# ------------------------------------------------------------------ #
|
|
102
|
+
# read
|
|
103
|
+
# ------------------------------------------------------------------ #
|
|
104
|
+
|
|
105
|
+
def _read(self, step: dict, spec: Dict[str, Any], ctx: RunContext) -> StepResult:
|
|
106
|
+
path = spec.get("path")
|
|
107
|
+
if not isinstance(path, str) or not path.strip():
|
|
108
|
+
return StepResult.failed("excel.path is required and must be a non-empty string")
|
|
109
|
+
|
|
110
|
+
resolved_path = os.path.normpath(ctx.substitute_stored(path))
|
|
111
|
+
|
|
112
|
+
if not os.path.exists(resolved_path):
|
|
113
|
+
return StepResult.failed(f"excel file not found: '{resolved_path}'")
|
|
114
|
+
|
|
115
|
+
sheet_name = spec.get("sheet_name")
|
|
116
|
+
workbook = load_workbook(filename=resolved_path, read_only=True, data_only=True)
|
|
117
|
+
try:
|
|
118
|
+
worksheet = workbook[ctx.substitute_stored(sheet_name)] if sheet_name else workbook.worksheets[0]
|
|
119
|
+
all_rows = [
|
|
120
|
+
[self._cell_to_str(cell) for cell in row]
|
|
121
|
+
for row in worksheet.iter_rows(values_only=True)
|
|
122
|
+
]
|
|
123
|
+
finally:
|
|
124
|
+
workbook.close()
|
|
125
|
+
|
|
126
|
+
if not all_rows:
|
|
127
|
+
headers: List[str] = []
|
|
128
|
+
raw_rows: List[List[str]] = []
|
|
129
|
+
else:
|
|
130
|
+
headers, raw_rows = all_rows[0], all_rows[1:]
|
|
131
|
+
|
|
132
|
+
row_dicts = [dict(zip(headers, row)) for row in raw_rows]
|
|
133
|
+
|
|
134
|
+
template_context: Dict[str, Any] = {
|
|
135
|
+
"excel": {
|
|
136
|
+
"headers": headers,
|
|
137
|
+
"rows": row_dicts,
|
|
138
|
+
"raw_rows": raw_rows,
|
|
139
|
+
"row_count": len(row_dicts),
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if "assertions" in step:
|
|
144
|
+
for expr in step["assertions"]:
|
|
145
|
+
try:
|
|
146
|
+
rendered_expr = ctx.substitute_stored(expr)
|
|
147
|
+
rendered = ctx.render_template(rendered_expr, template_context, add_quotes=True)
|
|
148
|
+
ctx.try_eval(rendered, rendered_expr)
|
|
149
|
+
except Exception as e:
|
|
150
|
+
raise AssertionError(f"Assertion failed for expression\n -->'{expr}'\n -->{e}")
|
|
151
|
+
|
|
152
|
+
if "save" in step:
|
|
153
|
+
for key, path_expr in step["save"].items():
|
|
154
|
+
rendered_path = ctx.substitute_stored(str(path_expr))
|
|
155
|
+
rendered = ctx.render_template(rendered_path, template_context)
|
|
156
|
+
ctx.store_value(key, rendered)
|
|
157
|
+
|
|
158
|
+
return StepResult.passed()
|
|
159
|
+
|
|
160
|
+
# ------------------------------------------------------------------ #
|
|
161
|
+
# generate
|
|
162
|
+
# ------------------------------------------------------------------ #
|
|
163
|
+
|
|
164
|
+
def _generate(self, spec: Dict[str, Any], ctx: RunContext) -> StepResult:
|
|
165
|
+
path = spec.get("path")
|
|
166
|
+
headers = spec.get("headers")
|
|
167
|
+
rows = spec.get("rows", [])
|
|
168
|
+
sheet_name = spec.get("sheet_name", "Sheet1")
|
|
169
|
+
|
|
170
|
+
if not isinstance(path, str) or not path.strip():
|
|
171
|
+
return StepResult.failed("excel.path is required and must be a non-empty string")
|
|
172
|
+
if not isinstance(headers, list) or not headers:
|
|
173
|
+
return StepResult.failed("excel.headers is required and must be a non-empty list")
|
|
174
|
+
if not isinstance(rows, list):
|
|
175
|
+
return StepResult.failed("excel.rows must be a list of row lists")
|
|
176
|
+
|
|
177
|
+
resolved_path = os.path.normpath(ctx.substitute_stored(path))
|
|
178
|
+
parent_dir = os.path.dirname(resolved_path)
|
|
179
|
+
if parent_dir:
|
|
180
|
+
os.makedirs(parent_dir, exist_ok=True)
|
|
181
|
+
|
|
182
|
+
resolved_headers = [ctx.substitute_stored(str(h)) for h in headers]
|
|
183
|
+
resolved_rows = [[ctx.substitute_stored(str(cell)) for cell in row] for row in rows]
|
|
184
|
+
|
|
185
|
+
workbook = Workbook()
|
|
186
|
+
worksheet = workbook.active
|
|
187
|
+
worksheet.title = ctx.substitute_stored(str(sheet_name))
|
|
188
|
+
worksheet.append(resolved_headers)
|
|
189
|
+
for row in resolved_rows:
|
|
190
|
+
worksheet.append(row)
|
|
191
|
+
workbook.save(resolved_path)
|
|
192
|
+
|
|
193
|
+
self._generated_paths.add(resolved_path)
|
|
194
|
+
print(f"[excel] Generated: {resolved_path} ({len(resolved_rows)} row(s))")
|
|
195
|
+
return StepResult.passed()
|
|
196
|
+
|
|
197
|
+
# ------------------------------------------------------------------ #
|
|
198
|
+
# helpers
|
|
199
|
+
# ------------------------------------------------------------------ #
|
|
200
|
+
|
|
201
|
+
@staticmethod
|
|
202
|
+
def _cell_to_str(value: Any) -> str:
|
|
203
|
+
if value is None:
|
|
204
|
+
return ""
|
|
205
|
+
if isinstance(value, bool):
|
|
206
|
+
return str(value)
|
|
207
|
+
if isinstance(value, float):
|
|
208
|
+
# Avoid scientific notation / trailing ".0" for integral values, mirroring
|
|
209
|
+
# how the .NET ExcelFileParser formats numeric cells on the reading side.
|
|
210
|
+
if value.is_integer():
|
|
211
|
+
return str(int(value))
|
|
212
|
+
return repr(value)
|
|
213
|
+
if isinstance(value, (datetime.datetime, datetime.date)):
|
|
214
|
+
return value.isoformat()
|
|
215
|
+
return str(value).strip()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sefrone_api_e2e
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.7
|
|
4
4
|
Summary: A Python package to provide e2e testing helpers for sefrone API projects
|
|
5
5
|
Home-page: https://bitbucket.org/sefrone/sefrone_pypi
|
|
6
6
|
Author: Sefrone
|
|
@@ -12,6 +12,7 @@ Requires-Python: >=3.7
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
Requires-Dist: PyYAML>=6.0.1
|
|
14
14
|
Requires-Dist: requests
|
|
15
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
15
16
|
Dynamic: author
|
|
16
17
|
Dynamic: author-email
|
|
17
18
|
Dynamic: classifier
|
|
@@ -8,6 +8,7 @@ sefrone_api_e2e/json_mock_server.py
|
|
|
8
8
|
sefrone_api_e2e/runner_assets.py
|
|
9
9
|
sefrone_api_e2e/runner_csv.py
|
|
10
10
|
sefrone_api_e2e/runner_email_mock.py
|
|
11
|
+
sefrone_api_e2e/runner_excel.py
|
|
11
12
|
sefrone_api_e2e/runner_rest_api.py
|
|
12
13
|
sefrone_api_e2e/runner_satim_mock.py
|
|
13
14
|
sefrone_api_e2e/runner_webhook_mock.py
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="sefrone_api_e2e",
|
|
8
|
-
version="1.3.
|
|
8
|
+
version="1.3.7",
|
|
9
9
|
author="Sefrone",
|
|
10
10
|
author_email="contact@sefrone.com",
|
|
11
11
|
description="A Python package to provide e2e testing helpers for sefrone API projects",
|
|
@@ -21,7 +21,8 @@ setup(
|
|
|
21
21
|
python_requires=">=3.7",
|
|
22
22
|
install_requires=[
|
|
23
23
|
"PyYAML>=6.0.1",
|
|
24
|
-
"requests"
|
|
24
|
+
"requests",
|
|
25
|
+
"openpyxl>=3.1.0"
|
|
25
26
|
],
|
|
26
27
|
package_data={
|
|
27
28
|
"sefrone_api_e2e": ["e2e/Scenarios/Auth/*.yaml", "e2e/Scenarios/Oidc/*.yaml"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sefrone_api_e2e-1.3.6 → sefrone_api_e2e-1.3.7}/sefrone_api_e2e.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|