sefrone-api-e2e 1.1.8__tar.gz → 1.2.0__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.
Files changed (28) hide show
  1. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/PKG-INFO +1 -1
  2. sefrone_api_e2e-1.2.0/sefrone_api_e2e/env_patch.py +77 -0
  3. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/runner_email_mock.py +12 -52
  4. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/runner_satim_mock.py +7 -40
  5. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/runner_webhook_mock.py +7 -42
  6. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e.egg-info/PKG-INFO +1 -1
  7. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e.egg-info/SOURCES.txt +1 -0
  8. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/setup.py +1 -1
  9. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/README.md +0 -0
  10. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/__init__.py +0 -0
  11. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/api_e2e_manager.py +0 -0
  12. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/e2e/Scenarios/Auth/0_check_features.yaml +0 -0
  13. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/e2e/Scenarios/Auth/1_normal_auth_flow.yaml +0 -0
  14. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/e2e/Scenarios/Auth/2_account_endpoints.yaml +0 -0
  15. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/e2e/Scenarios/Auth/3_admin_account_endpoints.yaml +0 -0
  16. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/e2e/Scenarios/Auth/4_workspace_endpoints.yaml +0 -0
  17. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/e2e/Scenarios/Auth/5_apikey_endpoints.yaml +0 -0
  18. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/e2e/Scenarios/Auth/6_project_endpoints.yaml +0 -0
  19. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/e2e/Scenarios/Auth/7_email_2fa_flow.yaml +0 -0
  20. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/e2e_scenarios_manager.py +0 -0
  21. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/json_mock_server.py +0 -0
  22. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/runner_assets.py +0 -0
  23. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/runner_rest_api.py +0 -0
  24. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e/step_runner.py +0 -0
  25. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e.egg-info/dependency_links.txt +0 -0
  26. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e.egg-info/requires.txt +0 -0
  27. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/sefrone_api_e2e.egg-info/top_level.txt +0 -0
  28. {sefrone_api_e2e-1.1.8 → sefrone_api_e2e-1.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sefrone_api_e2e
3
- Version: 1.1.8
3
+ Version: 1.2.0
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
@@ -0,0 +1,77 @@
1
+ import os
2
+ from typing import Dict, Optional
3
+
4
+
5
+ def apply_env_overrides(
6
+ env_file_path: str,
7
+ overrides: Dict[str, Optional[str]],
8
+ process_env_strategy: str = "none",
9
+ ) -> None:
10
+ """
11
+ Apply key/value overrides to a .env file.
12
+
13
+ - value is None: remove key from .env
14
+ - value is str: set/replace key in .env
15
+
16
+ process_env_strategy:
17
+ - "none": do not modify os.environ
18
+ - "sync": apply same set/unset semantics to os.environ
19
+ - "unset": remove every overridden key from os.environ
20
+ """
21
+ if process_env_strategy == "sync":
22
+ for key, val in overrides.items():
23
+ if val is None:
24
+ os.environ.pop(key, None)
25
+ else:
26
+ os.environ[key] = str(val)
27
+ elif process_env_strategy == "unset":
28
+ for key in overrides.keys():
29
+ os.environ.pop(key, None)
30
+
31
+ try:
32
+ with open(env_file_path, "r", encoding="utf-8") as f:
33
+ lines = f.readlines()
34
+ except FileNotFoundError:
35
+ lines = []
36
+
37
+ new_lines = []
38
+ replaced = set()
39
+
40
+ for line in lines:
41
+ stripped = line.strip()
42
+ if stripped.startswith("#") or "=" not in stripped:
43
+ new_lines.append(line)
44
+ continue
45
+
46
+ key = stripped.split("=", 1)[0].strip()
47
+ if key not in overrides:
48
+ new_lines.append(line)
49
+ continue
50
+
51
+ val = overrides[key]
52
+ if val is not None:
53
+ new_lines.append(f"{key}={val}\n")
54
+ replaced.add(key)
55
+
56
+ for key, val in overrides.items():
57
+ if key not in replaced and val is not None:
58
+ new_lines.append(f"{key}={val}\n")
59
+
60
+ with open(env_file_path, "w", encoding="utf-8") as f:
61
+ f.writelines(new_lines)
62
+
63
+
64
+ def read_env_value(env_file_path: str, key: str) -> Optional[str]:
65
+ """Read one key from .env and strip optional surrounding quotes."""
66
+ try:
67
+ with open(env_file_path, "r", encoding="utf-8") as f:
68
+ for line in f:
69
+ stripped = line.strip()
70
+ if stripped.startswith("#") or "=" not in stripped:
71
+ continue
72
+ k, _, v = stripped.partition("=")
73
+ if k.strip() == key:
74
+ return v.strip().strip('"').strip("'")
75
+ except FileNotFoundError:
76
+ pass
77
+ return None
@@ -1,4 +1,3 @@
1
- import os
2
1
  import re
3
2
  import time
4
3
  from typing import Optional
@@ -6,6 +5,7 @@ from typing import Optional
6
5
  import requests
7
6
 
8
7
  from .step_runner import StepRunner, RunContext, StepResult
8
+ from .env_patch import apply_env_overrides, read_env_value
9
9
 
10
10
  class EmailMockRunner(StepRunner):
11
11
  """
@@ -110,54 +110,25 @@ class EmailMockRunner(StepRunner):
110
110
  Variables with string values will be set to those values.
111
111
 
112
112
  Note: this also applies the same overrides to the current Python
113
- process environment (os.environ). This is important because Docker
114
- Compose resolves ${VAR} from the process env before reading .env.
113
+ process environment key space by unsetting every patched key. This
114
+ prevents Docker Compose from resolving stale ${VAR} values from the
115
+ process env before reading .env.
115
116
  """
116
117
  overrides = {
117
118
  **EmailMockRunner._ENV_OVERRIDES,
118
119
  "MAIL_SERVER": host,
119
120
  "MAIL_SERVER_PORT": str(smtp_port),
120
121
  }
121
-
122
- # Keep process env in sync with file overrides so docker-compose
123
- # interpolation does not pick stale MAIL_* values from OS env vars.
124
- for key, val in overrides.items():
125
- if val is None:
126
- os.environ.pop(key, None)
127
- else:
128
- os.environ[key] = val
129
-
130
122
  try:
131
- with open(env_file_path, "r", encoding="utf-8") as f:
132
- lines = f.readlines()
133
- except FileNotFoundError:
134
- print(f"[WARN] env file not found for EmailMockRunner patch: {env_file_path}")
123
+ apply_env_overrides(
124
+ env_file_path=env_file_path,
125
+ overrides=overrides,
126
+ process_env_strategy="unset",
127
+ )
128
+ except Exception as e:
129
+ print(f"[WARN] env file patch failed for EmailMockRunner: {env_file_path} -> {e}")
135
130
  return
136
131
 
137
- new_lines = []
138
- replaced = set()
139
- for line in lines:
140
- stripped = line.strip()
141
- if stripped.startswith("#") or "=" not in stripped:
142
- new_lines.append(line)
143
- continue
144
- key = stripped.split("=", 1)[0].strip()
145
- if key in overrides:
146
- # If override value is None, skip (unset) this variable
147
- if overrides[key] is not None:
148
- new_lines.append(f"{key}={overrides[key]}\n")
149
- replaced.add(key)
150
- else:
151
- new_lines.append(line)
152
-
153
- # Add any new overrides that weren't in the original file
154
- for key, val in overrides.items():
155
- if key not in replaced and val is not None:
156
- new_lines.append(f"{key}={val}\n")
157
-
158
- with open(env_file_path, "w", encoding="utf-8") as f:
159
- f.writelines(new_lines)
160
-
161
132
  unset_vars = [k for k, v in EmailMockRunner._ENV_OVERRIDES.items() if v is None]
162
133
  print(f"[EmailMockRunner] Patched {env_file_path}:")
163
134
  print(f" Set: MAIL_SERVER={host}:{smtp_port}, MAIL_USE_SECURITY=false")
@@ -435,15 +406,4 @@ class EmailMockRunner(StepRunner):
435
406
  @staticmethod
436
407
  def _read_env_value(env_file_path: str, key: str) -> Optional[str]:
437
408
  """Read a single key's value from a .env file, stripping surrounding quotes."""
438
- try:
439
- with open(env_file_path, "r", encoding="utf-8") as f:
440
- for line in f:
441
- stripped = line.strip()
442
- if stripped.startswith("#") or "=" not in stripped:
443
- continue
444
- k, _, v = stripped.partition("=")
445
- if k.strip() == key:
446
- return v.strip().strip('"').strip("'")
447
- except FileNotFoundError:
448
- pass
449
- return None
409
+ return read_env_value(env_file_path, key)
@@ -1,4 +1,3 @@
1
- import json
2
1
  import os
3
2
  import re
4
3
  import time
@@ -7,6 +6,7 @@ from typing import Optional
7
6
  import requests
8
7
 
9
8
  from .step_runner import StepRunner, RunContext, StepResult
9
+ from .env_patch import apply_env_overrides, read_env_value
10
10
 
11
11
 
12
12
  _SERVER_SCRIPT = r"""
@@ -338,45 +338,12 @@ class SatimMockRunner(StepRunner):
338
338
 
339
339
  @staticmethod
340
340
  def _patch_env_file(env_file_path: str, key: str, value: str) -> None:
341
- try:
342
- with open(env_file_path, "r", encoding="utf-8") as f:
343
- lines = f.readlines()
344
- except FileNotFoundError:
345
- lines = []
346
-
347
- new_lines = []
348
- found = False
349
- for line in lines:
350
- stripped = line.strip()
351
- if stripped.startswith("#") or "=" not in stripped:
352
- new_lines.append(line)
353
- continue
354
- k = stripped.split("=", 1)[0].strip()
355
- if k == key:
356
- new_lines.append(f"{key}={value}\n")
357
- found = True
358
- else:
359
- new_lines.append(line)
360
-
361
- if not found:
362
- if new_lines and not new_lines[-1].endswith("\n"):
363
- new_lines[-1] = new_lines[-1] + "\n"
364
- new_lines.append(f"{key}={value}\n")
365
-
366
- with open(env_file_path, "w", encoding="utf-8") as f:
367
- f.writelines(new_lines)
341
+ apply_env_overrides(
342
+ env_file_path=env_file_path,
343
+ overrides={key: value},
344
+ process_env_strategy="unset",
345
+ )
368
346
 
369
347
  @staticmethod
370
348
  def _read_env_value(env_file_path: str, key: str) -> Optional[str]:
371
- try:
372
- with open(env_file_path, "r", encoding="utf-8") as f:
373
- for line in f:
374
- stripped = line.strip()
375
- if stripped.startswith("#") or "=" not in stripped:
376
- continue
377
- k, _, v = stripped.partition("=")
378
- if k.strip() == key:
379
- return v.strip()
380
- except FileNotFoundError:
381
- pass
382
- return None
349
+ return read_env_value(env_file_path, key)
@@ -8,6 +8,7 @@ from typing import Optional
8
8
  import requests
9
9
 
10
10
  from .step_runner import StepRunner, RunContext, StepResult
11
+ from .env_patch import apply_env_overrides, read_env_value
11
12
 
12
13
 
13
14
  # ---------------------------------------------------------------------------
@@ -498,49 +499,13 @@ class WebhookMockRunner(StepRunner):
498
499
 
499
500
  @staticmethod
500
501
  def _patch_env_file(env_file_path: str, key: str, value: str) -> None:
501
- """Set or add a key=value line in the env file."""
502
- try:
503
- with open(env_file_path, "r", encoding="utf-8") as f:
504
- lines = f.readlines()
505
- except FileNotFoundError:
506
- lines = []
507
-
508
- new_lines = []
509
- found = False
510
- for line in lines:
511
- stripped = line.strip()
512
- if stripped.startswith("#") or "=" not in stripped:
513
- new_lines.append(line)
514
- continue
515
- k = stripped.split("=", 1)[0].strip()
516
- if k == key:
517
- new_lines.append(f"{key}={value}\n")
518
- found = True
519
- else:
520
- new_lines.append(line)
521
-
522
- if not found:
523
- # Ensure the previously copied last line ends with a newline,
524
- # otherwise the new key gets concatenated and corrupts .env.
525
- if new_lines and not new_lines[-1].endswith("\n"):
526
- new_lines[-1] = new_lines[-1] + "\n"
527
- new_lines.append(f"{key}={value}\n")
528
-
529
- with open(env_file_path, "w", encoding="utf-8") as f:
530
- f.writelines(new_lines)
502
+ apply_env_overrides(
503
+ env_file_path=env_file_path,
504
+ overrides={key: value},
505
+ process_env_strategy="unset",
506
+ )
531
507
 
532
508
  @staticmethod
533
509
  def _read_env_value(env_file_path: str, key: str) -> Optional[str]:
534
510
  """Read a single value from an env file."""
535
- try:
536
- with open(env_file_path, "r", encoding="utf-8") as f:
537
- for line in f:
538
- stripped = line.strip()
539
- if stripped.startswith("#") or "=" not in stripped:
540
- continue
541
- k, _, v = stripped.partition("=")
542
- if k.strip() == key:
543
- return v.strip()
544
- except FileNotFoundError:
545
- pass
546
- return None
511
+ return read_env_value(env_file_path, key)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sefrone_api_e2e
3
- Version: 1.1.8
3
+ Version: 1.2.0
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
@@ -3,6 +3,7 @@ setup.py
3
3
  sefrone_api_e2e/__init__.py
4
4
  sefrone_api_e2e/api_e2e_manager.py
5
5
  sefrone_api_e2e/e2e_scenarios_manager.py
6
+ sefrone_api_e2e/env_patch.py
6
7
  sefrone_api_e2e/json_mock_server.py
7
8
  sefrone_api_e2e/runner_assets.py
8
9
  sefrone_api_e2e/runner_email_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.1.8",
8
+ version="1.2.0",
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",