robotframework-env-manage-library 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EnvManageLibrary Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: robotframework-env-manage-library
3
+ Version: 0.1.0
4
+ Summary: Robot Framework library to override test variables from .env files
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/Non-Spk/robotframework-env-manage-library
7
+ Project-URL: Issues, https://github.com/Non-Spk/robotframework-env-manage-library/issues
8
+ Keywords: robotframework,env,testing,yaml,dotenv
9
+ Classifier: Framework :: Robot Framework
10
+ Classifier: Framework :: Robot Framework :: Library
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Topic :: Software Development :: Testing
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: robotframework>=5.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # EnvManageLibrary
26
+
27
+ Robot Framework keyword library that overrides test variables with values from `.env` files.
28
+
29
+ Variables loaded via Robot Framework's `Variables` directive (from YAML files) are automatically matched against `.env` entries and overridden at test-case scope.
30
+
31
+ ## Installation
32
+
33
+ ### Option 1: From PyPI
34
+
35
+ ```bash
36
+ pip install robotframework-env-manage-library
37
+ ```
38
+
39
+ ### Option 2: From local .whl / .tar.gz file
40
+
41
+ ```bash
42
+ pip install robotframework_env_manage_library-0.1.0-py3-none-any.whl
43
+ ```
44
+
45
+ ### Option 3: From local packages directory (offline)
46
+
47
+ ```bash
48
+ pip install --no-index --find-links=./packages robotframework-env-manage-library
49
+ ```
50
+
51
+ ## .env Naming Convention
52
+
53
+ The dot (`.`) separator distinguishes nested dict keys from flat variable names containing underscores.
54
+
55
+ | .env Key | RF Variable | Type |
56
+ |---|---|---|
57
+ | `USER=value` | `${user}` | Flat |
58
+ | `DB_HOST=value` | `${db_host}` | Flat |
59
+ | `AUTHEN.USER=value` | `${authen}[user]` | Nested dict |
60
+ | `DB.CONN.HOST=value` | `${db}[conn][host]` | Deep nested dict |
61
+
62
+ ## Usage
63
+
64
+ ### YAML data files
65
+
66
+ ```yaml
67
+ # data/credentials.yaml
68
+ user: testuser
69
+ pass: "default_pass"
70
+ ```
71
+
72
+ ```yaml
73
+ # data/config.yaml
74
+ db:
75
+ host: localhost
76
+ port: "5432"
77
+ ```
78
+
79
+ ### .env file
80
+
81
+ ```env
82
+ # Flat overrides
83
+ USER=admin
84
+ PASS=S3cretK3y!
85
+
86
+ # Nested dict overrides (use dot separator)
87
+ DB.HOST=prod-db.example.com
88
+ DB.PORT=5433
89
+ ```
90
+
91
+ ### Robot Framework test
92
+
93
+ ```robot
94
+ *** Settings ***
95
+ Library EnvManageLibrary
96
+ Variables ${CURDIR}/data/credentials.yaml
97
+ Variables ${CURDIR}/data/config.yaml
98
+
99
+ *** Test Cases ***
100
+ Verify Credentials Override
101
+ Override Variables From Env path/to/.env
102
+ Should Be Equal ${user} admin
103
+ Should Be Equal ${pass} S3cretK3y!
104
+
105
+ Verify Config Override
106
+ Override Variables From Env path/to/.env
107
+ Should Be Equal ${db}[host] prod-db.example.com
108
+ Should Be Equal ${db}[port] 5433
109
+
110
+ Values Reset Between Tests
111
+ # No override called - original YAML values are intact
112
+ Should Be Equal ${user} testuser
113
+ Should Be Equal ${db}[host] localhost
114
+ ```
115
+
116
+ ## How It Works
117
+
118
+ 1. Robot Framework loads YAML files via `Variables`, creating RF variables
119
+ 2. `Override Variables From Env` parses the `.env` file
120
+ 3. For each RF variable, it checks for a matching `.env` key:
121
+ - Flat string variables: YAML key `user` matches env key `USER`
122
+ - Dict variables: YAML key `db` with child `host` matches env key `DB.HOST`
123
+ 4. Matched values are overridden using `Set Test Variable` (scoped to current test only)
124
+ 5. Original YAML values remain intact for other test cases
125
+
126
+ ## Key Behaviors
127
+
128
+ - Override scope is per test case - original values reset between tests
129
+ - If `.env` file doesn't exist, all variables keep their YAML values
130
+ - Case insensitive - YAML `user` matches env `USER`
131
+ - Flat keys with underscores (`DB_HOST`) only override flat RF variables (`${db_host}`), not nested dicts
132
+ - Dict values are deep-copied before override to prevent mutation
133
+
134
+ ## Development
135
+
136
+ ```bash
137
+ git clone https://github.com/Non-Spk/robotframework-env-manage-library.git
138
+ cd robotframework-env-manage-library
139
+ python -m venv venv
140
+
141
+ # Windows
142
+ venv\Scripts\pip install -e ".[dev]"
143
+ venv\Scripts\pytest -v
144
+
145
+ # macOS / Linux
146
+ source venv/bin/activate
147
+ pip install -e ".[dev]"
148
+ pytest -v
149
+ ```
150
+
151
+ ## Building
152
+
153
+ ```bash
154
+ pip install build
155
+ python -m build
156
+ ```
157
+
158
+ This produces `dist/robotframework_env_manage-0.1.0-py3-none-any.whl` and `dist/robotframework_env_manage-0.1.0.tar.gz`.
159
+
160
+ ## License
161
+
162
+ MIT
@@ -0,0 +1,138 @@
1
+ # EnvManageLibrary
2
+
3
+ Robot Framework keyword library that overrides test variables with values from `.env` files.
4
+
5
+ Variables loaded via Robot Framework's `Variables` directive (from YAML files) are automatically matched against `.env` entries and overridden at test-case scope.
6
+
7
+ ## Installation
8
+
9
+ ### Option 1: From PyPI
10
+
11
+ ```bash
12
+ pip install robotframework-env-manage-library
13
+ ```
14
+
15
+ ### Option 2: From local .whl / .tar.gz file
16
+
17
+ ```bash
18
+ pip install robotframework_env_manage_library-0.1.0-py3-none-any.whl
19
+ ```
20
+
21
+ ### Option 3: From local packages directory (offline)
22
+
23
+ ```bash
24
+ pip install --no-index --find-links=./packages robotframework-env-manage-library
25
+ ```
26
+
27
+ ## .env Naming Convention
28
+
29
+ The dot (`.`) separator distinguishes nested dict keys from flat variable names containing underscores.
30
+
31
+ | .env Key | RF Variable | Type |
32
+ |---|---|---|
33
+ | `USER=value` | `${user}` | Flat |
34
+ | `DB_HOST=value` | `${db_host}` | Flat |
35
+ | `AUTHEN.USER=value` | `${authen}[user]` | Nested dict |
36
+ | `DB.CONN.HOST=value` | `${db}[conn][host]` | Deep nested dict |
37
+
38
+ ## Usage
39
+
40
+ ### YAML data files
41
+
42
+ ```yaml
43
+ # data/credentials.yaml
44
+ user: testuser
45
+ pass: "default_pass"
46
+ ```
47
+
48
+ ```yaml
49
+ # data/config.yaml
50
+ db:
51
+ host: localhost
52
+ port: "5432"
53
+ ```
54
+
55
+ ### .env file
56
+
57
+ ```env
58
+ # Flat overrides
59
+ USER=admin
60
+ PASS=S3cretK3y!
61
+
62
+ # Nested dict overrides (use dot separator)
63
+ DB.HOST=prod-db.example.com
64
+ DB.PORT=5433
65
+ ```
66
+
67
+ ### Robot Framework test
68
+
69
+ ```robot
70
+ *** Settings ***
71
+ Library EnvManageLibrary
72
+ Variables ${CURDIR}/data/credentials.yaml
73
+ Variables ${CURDIR}/data/config.yaml
74
+
75
+ *** Test Cases ***
76
+ Verify Credentials Override
77
+ Override Variables From Env path/to/.env
78
+ Should Be Equal ${user} admin
79
+ Should Be Equal ${pass} S3cretK3y!
80
+
81
+ Verify Config Override
82
+ Override Variables From Env path/to/.env
83
+ Should Be Equal ${db}[host] prod-db.example.com
84
+ Should Be Equal ${db}[port] 5433
85
+
86
+ Values Reset Between Tests
87
+ # No override called - original YAML values are intact
88
+ Should Be Equal ${user} testuser
89
+ Should Be Equal ${db}[host] localhost
90
+ ```
91
+
92
+ ## How It Works
93
+
94
+ 1. Robot Framework loads YAML files via `Variables`, creating RF variables
95
+ 2. `Override Variables From Env` parses the `.env` file
96
+ 3. For each RF variable, it checks for a matching `.env` key:
97
+ - Flat string variables: YAML key `user` matches env key `USER`
98
+ - Dict variables: YAML key `db` with child `host` matches env key `DB.HOST`
99
+ 4. Matched values are overridden using `Set Test Variable` (scoped to current test only)
100
+ 5. Original YAML values remain intact for other test cases
101
+
102
+ ## Key Behaviors
103
+
104
+ - Override scope is per test case - original values reset between tests
105
+ - If `.env` file doesn't exist, all variables keep their YAML values
106
+ - Case insensitive - YAML `user` matches env `USER`
107
+ - Flat keys with underscores (`DB_HOST`) only override flat RF variables (`${db_host}`), not nested dicts
108
+ - Dict values are deep-copied before override to prevent mutation
109
+
110
+ ## Development
111
+
112
+ ```bash
113
+ git clone https://github.com/Non-Spk/robotframework-env-manage-library.git
114
+ cd robotframework-env-manage-library
115
+ python -m venv venv
116
+
117
+ # Windows
118
+ venv\Scripts\pip install -e ".[dev]"
119
+ venv\Scripts\pytest -v
120
+
121
+ # macOS / Linux
122
+ source venv/bin/activate
123
+ pip install -e ".[dev]"
124
+ pytest -v
125
+ ```
126
+
127
+ ## Building
128
+
129
+ ```bash
130
+ pip install build
131
+ python -m build
132
+ ```
133
+
134
+ This produces `dist/robotframework_env_manage-0.1.0-py3-none-any.whl` and `dist/robotframework_env_manage-0.1.0.tar.gz`.
135
+
136
+ ## License
137
+
138
+ MIT
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "robotframework-env-manage-library"
7
+ version = "0.1.0"
8
+ description = "Robot Framework library to override test variables from .env files"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.8"
12
+ dependencies = ["robotframework>=5.0"]
13
+ keywords = ["robotframework", "env", "testing", "yaml", "dotenv"]
14
+ classifiers = [
15
+ "Framework :: Robot Framework",
16
+ "Framework :: Robot Framework :: Library",
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: OS Independent",
19
+ "Operating System :: Microsoft :: Windows",
20
+ "Operating System :: POSIX :: Linux",
21
+ "Operating System :: MacOS",
22
+ "Topic :: Software Development :: Testing",
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ dev = ["pytest>=7.0"]
27
+
28
+ [project.urls]
29
+ Repository = "https://github.com/Non-Spk/robotframework-env-manage-library"
30
+ Issues = "https://github.com/Non-Spk/robotframework-env-manage-library/issues"
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
34
+
35
+ [tool.pytest.ini_options]
36
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """EnvManageLibrary - Robot Framework library for .env variable overrides."""
2
+
3
+ from .env_manage_library import EnvManageLibrary
4
+
5
+ __all__ = ["EnvManageLibrary"]
6
+ __version__ = "0.1.0"
@@ -0,0 +1,140 @@
1
+ """
2
+ EnvManageLibrary - Robot Framework keyword library.
3
+
4
+ Scans existing RF variables and overrides their values with matching
5
+ entries from a .env file, scoped to the current test case.
6
+
7
+ .env naming convention:
8
+ - Flat variable ``${user}`` -> ``USER=value``
9
+ - Flat variable ``${authen_key_user}`` -> ``AUTHEN_KEY_USER=value``
10
+ - Nested dict ``${authen_key}[user]`` -> ``AUTHEN_KEY.USER=value``
11
+
12
+ The dot (``.``) separator distinguishes nested dict keys from flat
13
+ variable names that contain underscores.
14
+
15
+ Usage in Robot Framework:
16
+ Library EnvManageLibrary
17
+ Variables ${CURDIR}/data/authen.yaml
18
+
19
+ EnvManageLibrary.Override Variables From Env .env
20
+ """
21
+
22
+ import copy
23
+ from pathlib import Path
24
+ from robot.api.deco import keyword
25
+ from robot.libraries.BuiltIn import BuiltIn
26
+
27
+ _SKIP_VARS = frozenset((
28
+ "None", "True", "False", "null", "true", "false",
29
+ "EMPTY", "SPACE", "\\n",
30
+ ))
31
+
32
+
33
+ def _load_dotenv(env_path):
34
+ """Parse a .env file into a dict."""
35
+ env_vars = {}
36
+ path = Path(env_path)
37
+ if not path.is_file():
38
+ return env_vars
39
+ for line in path.read_text(encoding="utf-8").splitlines():
40
+ line = line.strip()
41
+ if not line or line.startswith("#"):
42
+ continue
43
+ if "=" not in line:
44
+ continue
45
+ key, _, value = line.partition("=")
46
+ env_vars[key.strip()] = value.strip().strip("'\"")
47
+ return env_vars
48
+
49
+
50
+ def _build_nested_overrides(env_vars):
51
+ """Build a lookup for nested dict overrides from dot-notation env keys.
52
+
53
+ Returns a dict like: ``{"authen_key": {"user": "val", "pass": "val"}}``
54
+ from env keys like ``AUTHEN_KEY.USER=val``, ``AUTHEN_KEY.PASS=val``.
55
+ """
56
+ nested = {}
57
+ for env_key, env_value in env_vars.items():
58
+ if "." not in env_key:
59
+ continue
60
+ parts = env_key.split(".")
61
+ var_name = parts[0].lower()
62
+ remaining = [p.lower() for p in parts[1:]]
63
+
64
+ current = nested.setdefault(var_name, {})
65
+ for part in remaining[:-1]:
66
+ current = current.setdefault(part, {})
67
+ current[remaining[-1]] = env_value
68
+ return nested
69
+
70
+
71
+ def _apply_nested_overrides(data, overrides):
72
+ """Apply nested overrides to a dict. Returns True if anything changed."""
73
+ changed = False
74
+ for key in data:
75
+ if key not in overrides:
76
+ continue
77
+ override = overrides[key]
78
+ if isinstance(data[key], dict) and isinstance(override, dict):
79
+ if _apply_nested_overrides(data[key], override):
80
+ changed = True
81
+ else:
82
+ data[key] = override
83
+ changed = True
84
+ return changed
85
+
86
+
87
+ class EnvManageLibrary:
88
+ """Robot Framework library for overriding variables from .env files."""
89
+
90
+ ROBOT_LIBRARY_SCOPE = "GLOBAL"
91
+
92
+ @keyword("Override Variables From Env")
93
+ def override_variables_from_env(self, env_path=".env"):
94
+ """Override current RF variables with values from a .env file.
95
+
96
+ Naming convention in .env:
97
+ - ``USER=value`` -> overrides flat ``${user}``
98
+ - ``AUTHEN_KEY_USER=value`` -> overrides flat ``${authen_key_user}``
99
+ - ``AUTHEN_KEY.USER=value`` -> overrides nested ``${authen_key}[user]``
100
+
101
+ Overrides are scoped to the current test case (``Set Test Variable``).
102
+ If .env file is missing, nothing changes.
103
+ """
104
+ builtin = BuiltIn()
105
+ dot_env = _load_dotenv(env_path)
106
+
107
+ if not dot_env:
108
+ return
109
+
110
+ flat_env = {k: v for k, v in dot_env.items() if "." not in k}
111
+ nested_overrides = _build_nested_overrides(dot_env)
112
+
113
+ rf_vars = builtin.get_variables()
114
+
115
+ seen = set()
116
+ for rf_name, rf_value in rf_vars.items():
117
+ if len(rf_name) < 4 or rf_name[1] != "{" or not rf_name.endswith("}"):
118
+ continue
119
+
120
+ var_name = rf_name[2:-1]
121
+ if var_name in seen:
122
+ continue
123
+ seen.add(var_name)
124
+
125
+ if var_name.startswith((" ", "/", "{")):
126
+ continue
127
+ if var_name in _SKIP_VARS:
128
+ continue
129
+ if var_name.upper() == var_name and var_name not in flat_env:
130
+ continue
131
+
132
+ if isinstance(rf_value, dict):
133
+ if var_name in nested_overrides:
134
+ new_value = copy.deepcopy(rf_value)
135
+ if _apply_nested_overrides(new_value, nested_overrides[var_name]):
136
+ builtin.set_test_variable(f"${{{var_name}}}", new_value)
137
+ elif isinstance(rf_value, str):
138
+ env_value = flat_env.get(var_name.upper())
139
+ if env_value is not None:
140
+ builtin.set_test_variable(f"${{{var_name}}}", env_value)
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: robotframework-env-manage-library
3
+ Version: 0.1.0
4
+ Summary: Robot Framework library to override test variables from .env files
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/Non-Spk/robotframework-env-manage-library
7
+ Project-URL: Issues, https://github.com/Non-Spk/robotframework-env-manage-library/issues
8
+ Keywords: robotframework,env,testing,yaml,dotenv
9
+ Classifier: Framework :: Robot Framework
10
+ Classifier: Framework :: Robot Framework :: Library
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Topic :: Software Development :: Testing
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: robotframework>=5.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # EnvManageLibrary
26
+
27
+ Robot Framework keyword library that overrides test variables with values from `.env` files.
28
+
29
+ Variables loaded via Robot Framework's `Variables` directive (from YAML files) are automatically matched against `.env` entries and overridden at test-case scope.
30
+
31
+ ## Installation
32
+
33
+ ### Option 1: From PyPI
34
+
35
+ ```bash
36
+ pip install robotframework-env-manage-library
37
+ ```
38
+
39
+ ### Option 2: From local .whl / .tar.gz file
40
+
41
+ ```bash
42
+ pip install robotframework_env_manage_library-0.1.0-py3-none-any.whl
43
+ ```
44
+
45
+ ### Option 3: From local packages directory (offline)
46
+
47
+ ```bash
48
+ pip install --no-index --find-links=./packages robotframework-env-manage-library
49
+ ```
50
+
51
+ ## .env Naming Convention
52
+
53
+ The dot (`.`) separator distinguishes nested dict keys from flat variable names containing underscores.
54
+
55
+ | .env Key | RF Variable | Type |
56
+ |---|---|---|
57
+ | `USER=value` | `${user}` | Flat |
58
+ | `DB_HOST=value` | `${db_host}` | Flat |
59
+ | `AUTHEN.USER=value` | `${authen}[user]` | Nested dict |
60
+ | `DB.CONN.HOST=value` | `${db}[conn][host]` | Deep nested dict |
61
+
62
+ ## Usage
63
+
64
+ ### YAML data files
65
+
66
+ ```yaml
67
+ # data/credentials.yaml
68
+ user: testuser
69
+ pass: "default_pass"
70
+ ```
71
+
72
+ ```yaml
73
+ # data/config.yaml
74
+ db:
75
+ host: localhost
76
+ port: "5432"
77
+ ```
78
+
79
+ ### .env file
80
+
81
+ ```env
82
+ # Flat overrides
83
+ USER=admin
84
+ PASS=S3cretK3y!
85
+
86
+ # Nested dict overrides (use dot separator)
87
+ DB.HOST=prod-db.example.com
88
+ DB.PORT=5433
89
+ ```
90
+
91
+ ### Robot Framework test
92
+
93
+ ```robot
94
+ *** Settings ***
95
+ Library EnvManageLibrary
96
+ Variables ${CURDIR}/data/credentials.yaml
97
+ Variables ${CURDIR}/data/config.yaml
98
+
99
+ *** Test Cases ***
100
+ Verify Credentials Override
101
+ Override Variables From Env path/to/.env
102
+ Should Be Equal ${user} admin
103
+ Should Be Equal ${pass} S3cretK3y!
104
+
105
+ Verify Config Override
106
+ Override Variables From Env path/to/.env
107
+ Should Be Equal ${db}[host] prod-db.example.com
108
+ Should Be Equal ${db}[port] 5433
109
+
110
+ Values Reset Between Tests
111
+ # No override called - original YAML values are intact
112
+ Should Be Equal ${user} testuser
113
+ Should Be Equal ${db}[host] localhost
114
+ ```
115
+
116
+ ## How It Works
117
+
118
+ 1. Robot Framework loads YAML files via `Variables`, creating RF variables
119
+ 2. `Override Variables From Env` parses the `.env` file
120
+ 3. For each RF variable, it checks for a matching `.env` key:
121
+ - Flat string variables: YAML key `user` matches env key `USER`
122
+ - Dict variables: YAML key `db` with child `host` matches env key `DB.HOST`
123
+ 4. Matched values are overridden using `Set Test Variable` (scoped to current test only)
124
+ 5. Original YAML values remain intact for other test cases
125
+
126
+ ## Key Behaviors
127
+
128
+ - Override scope is per test case - original values reset between tests
129
+ - If `.env` file doesn't exist, all variables keep their YAML values
130
+ - Case insensitive - YAML `user` matches env `USER`
131
+ - Flat keys with underscores (`DB_HOST`) only override flat RF variables (`${db_host}`), not nested dicts
132
+ - Dict values are deep-copied before override to prevent mutation
133
+
134
+ ## Development
135
+
136
+ ```bash
137
+ git clone https://github.com/Non-Spk/robotframework-env-manage-library.git
138
+ cd robotframework-env-manage-library
139
+ python -m venv venv
140
+
141
+ # Windows
142
+ venv\Scripts\pip install -e ".[dev]"
143
+ venv\Scripts\pytest -v
144
+
145
+ # macOS / Linux
146
+ source venv/bin/activate
147
+ pip install -e ".[dev]"
148
+ pytest -v
149
+ ```
150
+
151
+ ## Building
152
+
153
+ ```bash
154
+ pip install build
155
+ python -m build
156
+ ```
157
+
158
+ This produces `dist/robotframework_env_manage-0.1.0-py3-none-any.whl` and `dist/robotframework_env_manage-0.1.0.tar.gz`.
159
+
160
+ ## License
161
+
162
+ MIT
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/EnvManageLibrary/__init__.py
5
+ src/EnvManageLibrary/env_manage_library.py
6
+ src/robotframework_env_manage_library.egg-info/PKG-INFO
7
+ src/robotframework_env_manage_library.egg-info/SOURCES.txt
8
+ src/robotframework_env_manage_library.egg-info/dependency_links.txt
9
+ src/robotframework_env_manage_library.egg-info/requires.txt
10
+ src/robotframework_env_manage_library.egg-info/top_level.txt
11
+ tests/test_env_manage_library.py
@@ -0,0 +1,124 @@
1
+ """Unit tests for env_manage_library helper functions."""
2
+
3
+ import sys
4
+ import os
5
+ import pytest
6
+
7
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
8
+
9
+ from EnvManageLibrary.env_manage_library import (
10
+ _load_dotenv,
11
+ _build_nested_overrides,
12
+ _apply_nested_overrides,
13
+ )
14
+
15
+
16
+ # ---------------------------------------------------------------------------
17
+ # _load_dotenv
18
+ # ---------------------------------------------------------------------------
19
+ class TestLoadDotenv:
20
+ def test_basic_key_value(self, tmp_path):
21
+ env_file = tmp_path / ".env"
22
+ env_file.write_text("USER=admin\nPASS=secret\n")
23
+ result = _load_dotenv(str(env_file))
24
+ assert result == {"USER": "admin", "PASS": "secret"}
25
+
26
+ def test_strips_quotes(self, tmp_path):
27
+ env_file = tmp_path / ".env"
28
+ env_file.write_text("A='single'\nB=\"double\"\n")
29
+ result = _load_dotenv(str(env_file))
30
+ assert result == {"A": "single", "B": "double"}
31
+
32
+ def test_skips_comments_and_blanks(self, tmp_path):
33
+ env_file = tmp_path / ".env"
34
+ env_file.write_text("# comment\n\n \nKEY=val\n")
35
+ result = _load_dotenv(str(env_file))
36
+ assert result == {"KEY": "val"}
37
+
38
+ def test_skips_lines_without_equals(self, tmp_path):
39
+ env_file = tmp_path / ".env"
40
+ env_file.write_text("NOEQUALS\nKEY=val\n")
41
+ result = _load_dotenv(str(env_file))
42
+ assert result == {"KEY": "val"}
43
+
44
+ def test_missing_file_returns_empty(self):
45
+ result = _load_dotenv("nonexistent.env")
46
+ assert result == {}
47
+
48
+ def test_value_with_equals_sign(self, tmp_path):
49
+ env_file = tmp_path / ".env"
50
+ env_file.write_text("URL=https://example.com?a=1&b=2\n")
51
+ result = _load_dotenv(str(env_file))
52
+ assert result == {"URL": "https://example.com?a=1&b=2"}
53
+
54
+ def test_strips_whitespace(self, tmp_path):
55
+ env_file = tmp_path / ".env"
56
+ env_file.write_text(" KEY = value \n")
57
+ result = _load_dotenv(str(env_file))
58
+ assert result == {"KEY": "value"}
59
+
60
+
61
+ # ---------------------------------------------------------------------------
62
+ # _build_nested_overrides
63
+ # ---------------------------------------------------------------------------
64
+ class TestBuildNestedOverrides:
65
+ def test_single_level_dot(self):
66
+ env = {"AUTHEN_KEY.USER": "admin", "AUTHEN_KEY.PASS": "secret"}
67
+ result = _build_nested_overrides(env)
68
+ assert result == {"authen_key": {"user": "admin", "pass": "secret"}}
69
+
70
+ def test_multi_level_dot(self):
71
+ env = {"DB.CONN.HOST": "localhost", "DB.CONN.PORT": "5432"}
72
+ result = _build_nested_overrides(env)
73
+ assert result == {"db": {"conn": {"host": "localhost", "port": "5432"}}}
74
+
75
+ def test_ignores_flat_keys(self):
76
+ env = {"USER": "admin", "AUTHEN_KEY.USER": "nested"}
77
+ result = _build_nested_overrides(env)
78
+ assert "user" not in result
79
+ assert result == {"authen_key": {"user": "nested"}}
80
+
81
+ def test_empty_dict(self):
82
+ assert _build_nested_overrides({}) == {}
83
+
84
+ def test_case_insensitive_lowered(self):
85
+ env = {"MyVar.SubKey": "val"}
86
+ result = _build_nested_overrides(env)
87
+ assert result == {"myvar": {"subkey": "val"}}
88
+
89
+
90
+ # ---------------------------------------------------------------------------
91
+ # _apply_nested_overrides
92
+ # ---------------------------------------------------------------------------
93
+ class TestApplyNestedOverrides:
94
+ def test_overrides_matching_keys(self):
95
+ data = {"user": "old", "pass": "old"}
96
+ overrides = {"user": "new", "pass": "new"}
97
+ changed = _apply_nested_overrides(data, overrides)
98
+ assert changed is True
99
+ assert data == {"user": "new", "pass": "new"}
100
+
101
+ def test_no_match_returns_false(self):
102
+ data = {"user": "old"}
103
+ overrides = {"other": "val"}
104
+ changed = _apply_nested_overrides(data, overrides)
105
+ assert changed is False
106
+ assert data == {"user": "old"}
107
+
108
+ def test_nested_dict_override(self):
109
+ data = {"conn": {"host": "old", "port": "old"}}
110
+ overrides = {"conn": {"host": "new"}}
111
+ changed = _apply_nested_overrides(data, overrides)
112
+ assert changed is True
113
+ assert data == {"conn": {"host": "new", "port": "old"}}
114
+
115
+ def test_partial_override_keeps_others(self):
116
+ data = {"user": "old", "email": "old"}
117
+ overrides = {"user": "new"}
118
+ _apply_nested_overrides(data, overrides)
119
+ assert data == {"user": "new", "email": "old"}
120
+
121
+ def test_empty_overrides(self):
122
+ data = {"user": "old"}
123
+ changed = _apply_nested_overrides(data, {})
124
+ assert changed is False