diff --git a/lib/screens/envvar/editor_pane/variables_pane.dart b/lib/screens/envvar/editor_pane/variables_pane.dart index af3a1e398..4d1a2913c 100644 --- a/lib/screens/envvar/editor_pane/variables_pane.dart +++ b/lib/screens/envvar/editor_pane/variables_pane.dart @@ -69,6 +69,11 @@ class EditEnvironmentVariablesState DataColumn2( label: Text("Variable value"), ), + DataColumn2( + label: Text('OS'), + fixedWidth: 50, + tooltip: 'Use OS environment variable', + ), DataColumn2( label: Text(''), fixedWidth: 32, @@ -148,6 +153,27 @@ class EditEnvironmentVariablesState colorScheme: Theme.of(context).colorScheme, ), ), + DataCell( + Tooltip( + message: 'Fetch value from OS environment', + child: ADCheckBox( + keyId: "$selectedId-$index-variables-os-$seed", + value: variableRows[index].fromOS, + onChanged: isLast + ? null + : (value) { + if (value != null) { + setState(() { + variableRows[index] = + variableRows[index].copyWith(fromOS: value); + }); + } + _onFieldChange(selectedId!); + }, + colorScheme: Theme.of(context).colorScheme, + ), + ), + ), DataCell( InkWell( onTap: isLast diff --git a/lib/services/os_env_service.dart b/lib/services/os_env_service.dart new file mode 100644 index 000000000..ccefd29f7 --- /dev/null +++ b/lib/services/os_env_service.dart @@ -0,0 +1,38 @@ +import 'dart:io'; + +/// Service to handle OS environment variable operations +class OSEnvironmentService { + /// Get the value of an environment variable from the OS + /// Returns null if variable is not found + String? getOSEnvironmentVariable(String key) { + try { + + final envVars = Platform.environment; + + final matchingKey = envVars.keys + .firstWhere((k) => k.toUpperCase() == key.toUpperCase(), orElse: () => ''); + + if (matchingKey.isNotEmpty) { + final value = envVars[matchingKey]; + return value; + } + + return null; + } catch (e) { + return null; + } + } + + /// Check if an environment variable exists in the OS + bool hasOSEnvironmentVariable(String key) { + try { + final exists = Platform.environment.keys + .any((k) => k.toUpperCase() == key.toUpperCase()); + return exists; + } catch (e) { + return false; + } + } +} + +final osEnvironmentService = OSEnvironmentService(); diff --git a/lib/services/services.dart b/lib/services/services.dart index 7fa8128d6..321671185 100644 --- a/lib/services/services.dart +++ b/lib/services/services.dart @@ -2,3 +2,4 @@ export 'hive_services.dart'; export 'history_service.dart'; export 'window_services.dart'; export 'shared_preferences_services.dart'; +export 'os_env_service.dart'; diff --git a/lib/utils/envvar_utils.dart b/lib/utils/envvar_utils.dart index c94f6eebc..0b2b37e5d 100644 --- a/lib/utils/envvar_utils.dart +++ b/lib/utils/envvar_utils.dart @@ -1,6 +1,8 @@ import 'package:apidash_core/apidash_core.dart'; import 'package:apidash/consts.dart'; +import '../services/services.dart'; + String getEnvironmentTitle(String? name) { if (name == null || name.trim() == "") { return kUntitled; @@ -39,6 +41,7 @@ List getEnvironmentSecrets( String? substituteVariables( String? input, Map envVarMap, + String? activeEnvironmentId, ) { if (input == null) return null; if (envVarMap.keys.isEmpty) { @@ -54,6 +57,14 @@ String? substituteVariables( return result; } +String getEnvironmentVariableValue(EnvironmentVariableModel variable, String? activeEnvironmentId, Map combinedEnvVarMap) { + if (variable.fromOS) { + final osValue = osEnvironmentService.getOSEnvironmentVariable(variable.key); + return osValue ?? variable.value; + } + return combinedEnvVarMap[variable.key] ?? variable.value; +} + HttpRequestModel substituteHttpRequestModel( HttpRequestModel httpRequestModel, Map> envMap, @@ -64,33 +75,43 @@ HttpRequestModel substituteHttpRequestModel( final globalEnv = envMap[kGlobalEnvironmentId] ?? []; for (var variable in globalEnv) { - combinedEnvVarMap[variable.key] = variable.value; + combinedEnvVarMap[variable.key] = getEnvironmentVariableValue(variable, activeEnvironmentId, combinedEnvVarMap); } for (var variable in activeEnv) { - combinedEnvVarMap[variable.key] = variable.value; + combinedEnvVarMap[variable.key] = getEnvironmentVariableValue(variable, activeEnvironmentId, combinedEnvVarMap); } var newRequestModel = httpRequestModel.copyWith( - url: substituteVariables(httpRequestModel.url, combinedEnvVarMap)!, + url: substituteVariables( + httpRequestModel.url, + combinedEnvVarMap, + activeEnvironmentId, + )!, headers: httpRequestModel.headers?.map((header) { return header.copyWith( - name: substituteVariables(header.name, combinedEnvVarMap) ?? "", - value: substituteVariables(header.value, combinedEnvVarMap), + name: + substituteVariables(header.name, combinedEnvVarMap, activeEnvironmentId) ?? "", + value: substituteVariables(header.value, combinedEnvVarMap, activeEnvironmentId), ); }).toList(), params: httpRequestModel.params?.map((param) { return param.copyWith( - name: substituteVariables(param.name, combinedEnvVarMap) ?? "", - value: substituteVariables(param.value, combinedEnvVarMap), + name: + substituteVariables(param.name, combinedEnvVarMap, activeEnvironmentId) ?? "", + value: substituteVariables(param.value, combinedEnvVarMap, activeEnvironmentId), ); }).toList(), formData: httpRequestModel.formData?.map((formData) { return formData.copyWith( - name: substituteVariables(formData.name, combinedEnvVarMap) ?? "", - value: substituteVariables(formData.value, combinedEnvVarMap) ?? "", + name: substituteVariables(formData.name, combinedEnvVarMap, activeEnvironmentId) ?? "", + value: substituteVariables(formData.value, combinedEnvVarMap, activeEnvironmentId) ?? "", ); }).toList(), - body: substituteVariables(httpRequestModel.body, combinedEnvVarMap), + body: substituteVariables( + httpRequestModel.body, + combinedEnvVarMap, + activeEnvironmentId, + ), ); return newRequestModel; } @@ -156,4 +177,4 @@ EnvironmentVariableSuggestion getVariableStatus( environmentId: "unknown", variable: EnvironmentVariableModel( key: key, type: EnvironmentVariableType.variable, value: "unknown")); -} +} \ No newline at end of file diff --git a/packages/apidash_core/lib/models/environment_model.dart b/packages/apidash_core/lib/models/environment_model.dart index 9242d4d59..9d5c59950 100644 --- a/packages/apidash_core/lib/models/environment_model.dart +++ b/packages/apidash_core/lib/models/environment_model.dart @@ -32,6 +32,7 @@ class EnvironmentVariableModel with _$EnvironmentVariableModel { required String value, @Default(EnvironmentVariableType.variable) EnvironmentVariableType type, @Default(false) bool enabled, + @Default(false) bool fromOS, }) = _EnvironmentVariableModel; factory EnvironmentVariableModel.fromJson(Map json) => @@ -39,9 +40,9 @@ class EnvironmentVariableModel with _$EnvironmentVariableModel { } const kEnvironmentVariableEmptyModel = - EnvironmentVariableModel(key: "", value: ""); + EnvironmentVariableModel(key: "", value: "", fromOS: false); const kEnvironmentSecretEmptyModel = EnvironmentVariableModel( - key: "", value: "", type: EnvironmentVariableType.secret); + key: "", value: "", type: EnvironmentVariableType.secret, fromOS: false); class EnvironmentVariableSuggestion { final String environmentId; diff --git a/packages/apidash_core/lib/models/environment_model.freezed.dart b/packages/apidash_core/lib/models/environment_model.freezed.dart index a21c814cc..e035fbcbd 100644 --- a/packages/apidash_core/lib/models/environment_model.freezed.dart +++ b/packages/apidash_core/lib/models/environment_model.freezed.dart @@ -224,6 +224,7 @@ mixin _$EnvironmentVariableModel { String get value => throw _privateConstructorUsedError; EnvironmentVariableType get type => throw _privateConstructorUsedError; bool get enabled => throw _privateConstructorUsedError; + bool get fromOS => throw _privateConstructorUsedError; /// Serializes this EnvironmentVariableModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -242,7 +243,11 @@ abstract class $EnvironmentVariableModelCopyWith<$Res> { _$EnvironmentVariableModelCopyWithImpl<$Res, EnvironmentVariableModel>; @useResult $Res call( - {String key, String value, EnvironmentVariableType type, bool enabled}); + {String key, + String value, + EnvironmentVariableType type, + bool enabled, + bool fromOS}); } /// @nodoc @@ -265,6 +270,7 @@ class _$EnvironmentVariableModelCopyWithImpl<$Res, Object? value = null, Object? type = null, Object? enabled = null, + Object? fromOS = null, }) { return _then(_value.copyWith( key: null == key @@ -283,6 +289,10 @@ class _$EnvironmentVariableModelCopyWithImpl<$Res, ? _value.enabled : enabled // ignore: cast_nullable_to_non_nullable as bool, + fromOS: null == fromOS + ? _value.fromOS + : fromOS // ignore: cast_nullable_to_non_nullable + as bool, ) as $Val); } } @@ -297,7 +307,11 @@ abstract class _$$EnvironmentVariableModelImplCopyWith<$Res> @override @useResult $Res call( - {String key, String value, EnvironmentVariableType type, bool enabled}); + {String key, + String value, + EnvironmentVariableType type, + bool enabled, + bool fromOS}); } /// @nodoc @@ -319,6 +333,7 @@ class __$$EnvironmentVariableModelImplCopyWithImpl<$Res> Object? value = null, Object? type = null, Object? enabled = null, + Object? fromOS = null, }) { return _then(_$EnvironmentVariableModelImpl( key: null == key @@ -337,6 +352,10 @@ class __$$EnvironmentVariableModelImplCopyWithImpl<$Res> ? _value.enabled : enabled // ignore: cast_nullable_to_non_nullable as bool, + fromOS: null == fromOS + ? _value.fromOS + : fromOS // ignore: cast_nullable_to_non_nullable + as bool, )); } } @@ -349,7 +368,8 @@ class _$EnvironmentVariableModelImpl implements _EnvironmentVariableModel { {required this.key, required this.value, this.type = EnvironmentVariableType.variable, - this.enabled = false}); + this.enabled = false, + this.fromOS = false}); factory _$EnvironmentVariableModelImpl.fromJson(Map json) => _$$EnvironmentVariableModelImplFromJson(json); @@ -364,10 +384,13 @@ class _$EnvironmentVariableModelImpl implements _EnvironmentVariableModel { @override @JsonKey() final bool enabled; + @override + @JsonKey() + final bool fromOS; @override String toString() { - return 'EnvironmentVariableModel(key: $key, value: $value, type: $type, enabled: $enabled)'; + return 'EnvironmentVariableModel(key: $key, value: $value, type: $type, enabled: $enabled, fromOS: $fromOS)'; } @override @@ -378,12 +401,14 @@ class _$EnvironmentVariableModelImpl implements _EnvironmentVariableModel { (identical(other.key, key) || other.key == key) && (identical(other.value, value) || other.value == value) && (identical(other.type, type) || other.type == type) && - (identical(other.enabled, enabled) || other.enabled == enabled)); + (identical(other.enabled, enabled) || other.enabled == enabled) && + (identical(other.fromOS, fromOS) || other.fromOS == fromOS)); } @JsonKey(includeFromJson: false, includeToJson: false) @override - int get hashCode => Object.hash(runtimeType, key, value, type, enabled); + int get hashCode => + Object.hash(runtimeType, key, value, type, enabled, fromOS); /// Create a copy of EnvironmentVariableModel /// with the given fields replaced by the non-null parameter values. @@ -407,7 +432,8 @@ abstract class _EnvironmentVariableModel implements EnvironmentVariableModel { {required final String key, required final String value, final EnvironmentVariableType type, - final bool enabled}) = _$EnvironmentVariableModelImpl; + final bool enabled, + final bool fromOS}) = _$EnvironmentVariableModelImpl; factory _EnvironmentVariableModel.fromJson(Map json) = _$EnvironmentVariableModelImpl.fromJson; @@ -420,6 +446,8 @@ abstract class _EnvironmentVariableModel implements EnvironmentVariableModel { EnvironmentVariableType get type; @override bool get enabled; + @override + bool get fromOS; /// Create a copy of EnvironmentVariableModel /// with the given fields replaced by the non-null parameter values. diff --git a/packages/apidash_core/lib/models/environment_model.g.dart b/packages/apidash_core/lib/models/environment_model.g.dart index dd6b98e91..b958bf32a 100644 --- a/packages/apidash_core/lib/models/environment_model.g.dart +++ b/packages/apidash_core/lib/models/environment_model.g.dart @@ -30,10 +30,10 @@ _$EnvironmentVariableModelImpl _$$EnvironmentVariableModelImplFromJson( _$EnvironmentVariableModelImpl( key: json['key'] as String, value: json['value'] as String, - type: - $enumDecodeNullable(_$EnvironmentVariableTypeEnumMap, json['type']) ?? - EnvironmentVariableType.variable, + type: $enumDecodeNullable(_$EnvironmentVariableTypeEnumMap, json['type']) ?? + EnvironmentVariableType.variable, enabled: json['enabled'] as bool? ?? false, + fromOS: json['fromOS'] as bool? ?? false, ); Map _$$EnvironmentVariableModelImplToJson( @@ -43,6 +43,7 @@ Map _$$EnvironmentVariableModelImplToJson( 'value': instance.value, 'type': _$EnvironmentVariableTypeEnumMap[instance.type]!, 'enabled': instance.enabled, + 'fromOS': instance.fromOS, }; const _$EnvironmentVariableTypeEnumMap = { diff --git a/test/models/environment_models.dart b/test/models/environment_models.dart index e23f659bd..9a07dc381 100644 --- a/test/models/environment_models.dart +++ b/test/models/environment_models.dart @@ -97,12 +97,14 @@ const environmentModel1Json = { 'value': 'value1', 'type': 'variable', 'enabled': true, + 'fromOS': false, }, { 'key': 'key2', 'value': 'value2', 'type': 'variable', 'enabled': false, + 'fromOS': false, }, ], }; @@ -116,12 +118,14 @@ const environmentModel2Json = { 'value': 'value1', 'type': 'secret', 'enabled': true, + 'fromOS': false, }, { 'key': 'key2', 'value': 'value2', 'type': 'secret', 'enabled': false, + 'fromOS': false, }, ], }; @@ -131,6 +135,7 @@ const environmentVariableModel1Json = { 'value': 'value1', 'type': 'variable', 'enabled': true, + 'fromOS': false, }; const environmentVariableModel2Json = { @@ -138,4 +143,5 @@ const environmentVariableModel2Json = { 'value': 'value1', 'type': 'secret', 'enabled': true, + 'fromOS': false, }; diff --git a/test/models/environment_models_test.dart b/test/models/environment_models_test.dart index f4ab1e313..d42e17dc8 100644 --- a/test/models/environment_models_test.dart +++ b/test/models/environment_models_test.dart @@ -87,6 +87,40 @@ void main() { expect(environmentVariableModel.enabled, true); }); + test("Testing EnvironmentVariableModel copyWith with fromOS", () { + var environmentVariableModel = environmentVariableModel1; + final environmentVariableModelcopyWith = environmentVariableModel + .copyWith(fromOS: true); + expect(environmentVariableModelcopyWith.fromOS, true); + expect(environmentVariableModel.fromOS, false); + }); + + test("Testing EnvironmentVariableModel toJson with fromOS", () { + var environmentVariable = EnvironmentVariableModel( + key: 'key1', + value: 'value1', + type: EnvironmentVariableType.variable, + enabled: true, + fromOS: true, + ); + + final jsonMap = environmentVariable.toJson(); + expect(jsonMap['fromOS'], true); + }); + + test("Testing EnvironmentVariableModel fromJson with fromOS", () { + var json = { + 'key': 'key1', + 'value': 'value1', + 'type': 'variable', + 'enabled': true, + 'fromOS': true, + }; + + final modelFromJson = EnvironmentVariableModel.fromJson(json); + expect(modelFromJson.fromOS, true); + }); + test("Testing EnvironmentVariableModel toJson", () { var environmentVariable = environmentVariableModel1; expect(environmentVariable.toJson(), environmentVariableModel1Json); diff --git a/test/services/os_env_service_test.dart b/test/services/os_env_service_test.dart new file mode 100644 index 000000000..23048945a --- /dev/null +++ b/test/services/os_env_service_test.dart @@ -0,0 +1,89 @@ +import 'dart:io'; +import 'package:test/test.dart'; +import 'package:apidash/services/os_env_service.dart'; + +void main() { + late OSEnvironmentService osEnvironmentService; + + setUp(() { + osEnvironmentService = OSEnvironmentService(); + }); + + group('OSEnvironmentService', () { + test('getOSEnvironmentVariable returns the value for existing variable', () { + final testKey = Platform.environment.keys.firstWhere( + (key) => Platform.environment[key]?.isNotEmpty ?? false, + orElse: () => '', + ); + + if (testKey.isNotEmpty) { + final expectedValue = Platform.environment[testKey]; + final actualValue = osEnvironmentService.getOSEnvironmentVariable(testKey); + + expect(actualValue, expectedValue); + } + }); + + test('getOSEnvironmentVariable returns null for non-existent variable', () { + const nonExistentKey = 'NON_EXISTENT_ENVIRONMENT_VARIABLE_12345'; + final value = osEnvironmentService.getOSEnvironmentVariable(nonExistentKey); + + expect(value, isNull); + }); + + test('getOSEnvironmentVariable is case-insensitive', () { + final testKey = Platform.environment.keys.firstWhere( + (key) => Platform.environment[key]?.isNotEmpty ?? false, + orElse: () => '', + ); + + if (testKey.isNotEmpty) { + final expectedValue = Platform.environment[testKey]; + + // Try with uppercase + final upperCaseValue = osEnvironmentService.getOSEnvironmentVariable(testKey.toUpperCase()); + expect(upperCaseValue, expectedValue); + + // Try with lowercase + final lowerCaseValue = osEnvironmentService.getOSEnvironmentVariable(testKey.toLowerCase()); + expect(lowerCaseValue, expectedValue); + } + }); + + test('hasOSEnvironmentVariable returns true for existing variable', () { + final testKey = Platform.environment.keys.firstWhere( + (key) => Platform.environment[key]?.isNotEmpty ?? false, + orElse: () => '', + ); + + if (testKey.isNotEmpty) { + final exists = osEnvironmentService.hasOSEnvironmentVariable(testKey); + expect(exists, isTrue); + } + }); + + test('hasOSEnvironmentVariable returns false for non-existent variable', () { + const nonExistentKey = 'NON_EXISTENT_ENVIRONMENT_VARIABLE_12345'; + final exists = osEnvironmentService.hasOSEnvironmentVariable(nonExistentKey); + + expect(exists, isFalse); + }); + + test('hasOSEnvironmentVariable is case-insensitive', () { + final testKey = Platform.environment.keys.firstWhere( + (key) => Platform.environment[key]?.isNotEmpty ?? false, + orElse: () => '', + ); + + if (testKey.isNotEmpty) { + // Try with uppercase + final existsUpper = osEnvironmentService.hasOSEnvironmentVariable(testKey.toUpperCase()); + expect(existsUpper, isTrue); + + // Try with lowercase + final existsLower = osEnvironmentService.hasOSEnvironmentVariable(testKey.toLowerCase()); + expect(existsLower, isTrue); + } + }); + }); +} \ No newline at end of file diff --git a/test/utils/envvar_utils_test.dart b/test/utils/envvar_utils_test.dart index 48fc77356..492a58f11 100644 --- a/test/utils/envvar_utils_test.dart +++ b/test/utils/envvar_utils_test.dart @@ -1,6 +1,9 @@ +import 'dart:io'; + import 'package:apidash/utils/envvar_utils.dart'; import 'package:apidash/consts.dart'; import 'package:apidash_core/apidash_core.dart'; +import 'package:flutter/foundation.dart'; import 'package:test/test.dart'; const envVars = [ @@ -129,44 +132,44 @@ void main() { test("Testing substituteVariables with null", () { String? input; Map envMap = {}; - expect(substituteVariables(input, envMap), null); + expect(substituteVariables(input, envMap, null), null); }); test("Testing substituteVariables with empty input", () { String input = ""; Map envMap = {}; - expect(substituteVariables(input, envMap), ""); + expect(substituteVariables(input, envMap, null), ""); }); test("Testing substituteVariables with empty envMap", () { String input = "{{url}}/humanize/social?num={{num}}"; Map envMap = {}; String expected = "{{url}}/humanize/social?num={{num}}"; - expect(substituteVariables(input, envMap), expected); + expect(substituteVariables(input, envMap, null), expected); }); test("Testing substituteVariables with empty activeEnvironmentId", () { String input = "{{url}}/humanize/social?num={{num}}"; String expected = "api.foss42.com/humanize/social?num=5670000"; - expect(substituteVariables(input, globalVarsMap), expected); + expect(substituteVariables(input, globalVarsMap, null), expected); }); test("Testing substituteVariables with non-empty activeEnvironmentId", () { String input = "{{url}}/humanize/social?num={{num}}"; String expected = "api.apidash.dev/humanize/social?num=8940000"; - expect(substituteVariables(input, combinedEnvVarsMap), expected); + expect(substituteVariables(input, combinedEnvVarsMap, "activeEnvId"), expected); }); test("Testing substituteVariables with incorrect paranthesis", () { String input = "{{url}}}/humanize/social?num={{num}}"; String expected = "api.apidash.dev}/humanize/social?num=8940000"; - expect(substituteVariables(input, combinedEnvVarsMap), expected); + expect(substituteVariables(input, combinedEnvVarsMap, "activeEnvId"), expected); }); test("Testing substituteVariables function with unavailable variables", () { String input = "{{url1}}/humanize/social?num={{num}}"; String expected = "{{url1}}/humanize/social?num=8940000"; - expect(substituteVariables(input, combinedEnvVarsMap), expected); + expect(substituteVariables(input, combinedEnvVarsMap, "activeEnvId"), expected); }); }); @@ -194,8 +197,13 @@ void main() { ); Map> envMap = { - kGlobalEnvironmentId: globalVars, - "activeEnvId": activeEnvVars, + kGlobalEnvironmentId: [ + const EnvironmentVariableModel(key: "token", value: "token"), + ], + "activeEnvId": [ + const EnvironmentVariableModel(key: "url", value: "api.apidash.dev"), + const EnvironmentVariableModel(key: "num", value: "8940000"), + ], }; String? activeEnvironmentId = "activeEnvId"; @@ -227,10 +235,14 @@ void main() { NameValueModel(name: "num", value: "{{num}}"), ], ); + Map> envMap = { - kGlobalEnvironmentId: globalVars, - "activeEnvId": activeEnvVars, + kGlobalEnvironmentId: [], + "activeEnvId": [ + const EnvironmentVariableModel(key: "num", value: "8940000"), + ], }; + String? activeEnvironmentId = "activeEnvId"; const expected = HttpRequestModel( url: "{{url1}}/humanize/social", @@ -260,10 +272,17 @@ void main() { ], body: "The API key is {{token}} and the number is {{num}}", ); + Map> envMap = { - kGlobalEnvironmentId: globalVars, - "activeEnvId": activeEnvVars, + kGlobalEnvironmentId: [ + const EnvironmentVariableModel(key: "token", value: "token"), + ], + "activeEnvId": [ + const EnvironmentVariableModel(key: "url", value: "api.apidash.dev"), + const EnvironmentVariableModel(key: "num", value: "8940000"), + ], }; + String? activeEnvironmentId = "activeEnvId"; const expected = HttpRequestModel( url: "api.apidash.dev/humanize/social", @@ -357,4 +376,136 @@ void main() { expect(getVariableStatus(query, envMap, activeEnvironmentId), expected); }); }); + + group('getEnvironmentVariableValue tests', () { + test('returns original value when fromOS is false', () { + const variable = EnvironmentVariableModel( + key: 'TEST_KEY', + value: 'test_value', + fromOS: false, + ); + + final combinedEnvVarMap = {'TEST_KEY': 'mapped_value'}; + final result = getEnvironmentVariableValue(variable, null, combinedEnvVarMap); + expect(result, 'mapped_value'); + }); + + test('returns original value when fromOS is false and not in map', () { + const variable = EnvironmentVariableModel( + key: 'TEST_KEY', + value: 'test_value', + fromOS: false, + ); + + final combinedEnvVarMap = {}; + final result = getEnvironmentVariableValue(variable, null, combinedEnvVarMap); + expect(result, 'test_value'); + }); + + test('returns OS value when fromOS is true and OS variable exists', () { + final testKey = Platform.environment.keys.firstWhere( + (key) => Platform.environment[key]?.isNotEmpty ?? false, + orElse: () => '', + ); + + if (testKey.isNotEmpty) { + final osValue = Platform.environment[testKey]; + + final variable = EnvironmentVariableModel( + key: testKey, + value: 'fallback_value', + fromOS: true, + ); + + final combinedEnvVarMap = {}; + final result = getEnvironmentVariableValue(variable, null, combinedEnvVarMap); + expect(result, osValue); + } else { + if (kDebugMode) { + print('Skipped test: No environment variables available for testing'); + } + } + }); + + test('returns fallback value when fromOS is true but OS variable does not exist', () { + const nonExistentKey = 'NON_EXISTENT_ENVIRONMENT_VARIABLE_12345'; + + const variable = EnvironmentVariableModel( + key: nonExistentKey, + value: 'fallback_value', + fromOS: true, + ); + + final combinedEnvVarMap = {}; + final result = getEnvironmentVariableValue(variable, null, combinedEnvVarMap); + expect(result, 'fallback_value'); + }); + }); + + group('substituteHttpRequestModel with OS environment variables', () { + test('correctly substitutes OS environment variables', () { + const osKey = 'osVar'; + + const httpRequestModel = HttpRequestModel( + url: "{{osVar}}/test", + ); + + final osVariable = EnvironmentVariableModel( + key: osKey, + value: 'fallback_value', + fromOS: true, + ); + + final Map> envMap = { + 'activeEnvId': [osVariable], + }; + + final result = substituteHttpRequestModel( + httpRequestModel, + envMap, + 'activeEnvId', + ); + + expect(result.url, 'fallback_value/test'); + }); + }); + + group('substituteVariables with OS environment variables', () { + test('substitutes OS environment variables when fromOS is true', () { + final testKey = Platform.environment.keys.firstWhere( + (key) => Platform.environment[key]?.isNotEmpty ?? false, + orElse: () => '', + ); + + if (testKey.isNotEmpty) { + final osValue = Platform.environment[testKey]; + + final input = 'Value from OS: {{$testKey}}'; + + Map combinedEnvVarMap = {testKey: osValue ?? 'fallback_value'}; + + final result = substituteVariables(input, combinedEnvVarMap, 'env1'); + final expected = 'Value from OS: ${osValue ?? 'fallback_value'}'; + + expect(result, expected); + } else { + if (kDebugMode) { + print('Skipped test: No environment variables available for testing'); + } + } + }); + + test('falls back to stored value when OS variable not found and fromOS is true', () { + const nonExistentKey = 'NON_EXISTENT_ENVIRONMENT_VARIABLE_12345'; + const input = 'Fallback value: {{NON_EXISTENT_ENVIRONMENT_VARIABLE_12345}}'; + + Map combinedEnvVarMap = {nonExistentKey: 'fallback_value'}; + + final result = substituteVariables(input, combinedEnvVarMap, 'env1'); + const expected = 'Fallback value: fallback_value'; + + expect(result, expected); + }); + }); + }