From 29fb8cffdbf48cb52a3d7c9135da43d9b1ad24df Mon Sep 17 00:00:00 2001
From: Henrique Graca <999396+hjgraca@users.noreply.github.com>
Date: Mon, 21 Jul 2025 20:20:14 +0100
Subject: [PATCH 1/3] refactor: simplify console output handling and remove
unnecessary state management
---
.../Core/ConsoleWrapper.cs | 71 +++-
.../ConsoleWrapperTests.cs | 339 +++++++++++-------
2 files changed, 258 insertions(+), 152 deletions(-)
diff --git a/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs b/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs
index 869d2780..dc245fb5 100644
--- a/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs
@@ -8,7 +8,6 @@ public class ConsoleWrapper : IConsoleWrapper
{
private static bool _override;
private static TextWriter _testOutputStream;
- private static bool _outputResetPerformed = false;
private static bool _inTestMode = false;
///
@@ -20,7 +19,7 @@ public void WriteLine(string message)
}
else
{
- EnsureConsoleOutputOnce();
+ EnsureConsoleOutput();
Console.WriteLine(message);
}
}
@@ -34,7 +33,7 @@ public void Debug(string message)
}
else
{
- EnsureConsoleOutputOnce();
+ EnsureConsoleOutput();
System.Diagnostics.Debug.WriteLine(message);
}
}
@@ -50,9 +49,9 @@ public void Error(string message)
{
if (!_override)
{
- var errordOutput = new StreamWriter(Console.OpenStandardError());
- errordOutput.AutoFlush = true;
- Console.SetError(errordOutput);
+ var errorOutput = new StreamWriter(Console.OpenStandardError());
+ errorOutput.AutoFlush = true;
+ Console.SetError(errorOutput);
}
Console.Error.WriteLine(message);
}
@@ -70,23 +69,59 @@ public static void SetOut(TextWriter consoleOut)
Console.SetOut(consoleOut);
}
- private static void EnsureConsoleOutputOnce()
+ private static void EnsureConsoleOutput()
{
- if (_outputResetPerformed) return;
- OverrideLambdaLogger();
- _outputResetPerformed = true;
+ // Check if we need to override console output for Lambda environment
+ if (ShouldOverrideConsole())
+ {
+ OverrideLambdaLogger();
+ }
+ }
+
+ private static bool ShouldOverrideConsole()
+ {
+ // Don't override if we're in test mode
+ if (_inTestMode) return false;
+
+ // Always override in Lambda environment to prevent Lambda's log wrapping
+ var isLambda = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME"));
+
+ return isLambda && (!_override || HasLambdaReInterceptedConsole());
+ }
+
+ private static bool HasLambdaReInterceptedConsole()
+ {
+ // Lambda might re-intercept console between init and handler execution
+ try
+ {
+ var currentOut = Console.Out;
+ // Check if current output stream looks like it might be Lambda's wrapper
+ var typeName = currentOut.GetType().FullName ?? "";
+ return typeName.Contains("Lambda") || typeName == "System.IO.TextWriter+SyncTextWriter";
+ }
+ catch
+ {
+ return true; // Assume re-interception if we can't determine
+ }
}
private static void OverrideLambdaLogger()
{
- if (_override)
+ try
+ {
+ // Force override of LambdaLogger
+ var standardOutput = new StreamWriter(Console.OpenStandardOutput())
+ {
+ AutoFlush = true
+ };
+ Console.SetOut(standardOutput);
+ _override = true;
+ }
+ catch (Exception)
{
- return;
+ // Log the failure but don't throw - degraded functionality is better than crash
+ _override = false;
}
- // Force override of LambdaLogger
- var standardOutput = new StreamWriter(Console.OpenStandardOutput());
- standardOutput.AutoFlush = true;
- Console.SetOut(standardOutput);
}
internal static void WriteLine(string logLevel, string message)
@@ -102,7 +137,6 @@ public static void ResetForTest()
_override = false;
_inTestMode = false;
_testOutputStream = null;
- _outputResetPerformed = false;
}
///
@@ -110,6 +144,7 @@ public static void ResetForTest()
///
public static void ClearOutputResetFlag()
{
- _outputResetPerformed = false;
+ // This method is kept for backward compatibility but no longer needed
+ // since we removed the _outputResetPerformed flag
}
}
\ No newline at end of file
diff --git a/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs b/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs
index 1fa0f7b5..8245a7f0 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs
+++ b/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs
@@ -6,112 +6,239 @@ namespace AWS.Lambda.Powertools.Common.Tests;
public class ConsoleWrapperTests : IDisposable
{
- private StringWriter _writer;
+ private readonly TextWriter _originalOut;
+ private readonly TextWriter _originalError;
+ private readonly StringWriter _testWriter;
public ConsoleWrapperTests()
{
- // Setup a new StringWriter for each test
- _writer = new StringWriter();
- // Reset static state for clean testing
+ // Store original console outputs
+ _originalOut = Console.Out;
+ _originalError = Console.Error;
+
+ // Setup test writer
+ _testWriter = new StringWriter();
+
+ // Reset ConsoleWrapper state before each test
+ ConsoleWrapper.ResetForTest();
+
+ // Clear any Lambda environment variables
+ Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", null);
+ }
+
+ public void Dispose()
+ {
+ // Restore original console outputs
+ Console.SetOut(_originalOut);
+ Console.SetError(_originalError);
+
+ // Reset ConsoleWrapper state after each test
ConsoleWrapper.ResetForTest();
+
+ // Clear any test environment variables
+ Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", null);
+
+ _testWriter?.Dispose();
+ }
+
+ [Fact]
+ public void WriteLine_GivenInTestMode_WhenCalled_ThenWritesToTestOutputStream()
+ {
+ // Given
+ ConsoleWrapper.SetOut(_testWriter);
+ var wrapper = new ConsoleWrapper();
+ const string message = "test message";
+
+ // When
+ wrapper.WriteLine(message);
+
+ // Then
+ Assert.Equal($"{message}{Environment.NewLine}", _testWriter.ToString());
+ }
+
+ [Fact]
+ public void WriteLine_GivenNotInLambdaEnvironment_WhenCalled_ThenWritesToConsoleDirectly()
+ {
+ // Given
+ var wrapper = new ConsoleWrapper();
+ var consoleOutput = new StringWriter();
+ Console.SetOut(consoleOutput);
+ const string message = "test message";
+
+ // When
+ wrapper.WriteLine(message);
+
+ // Then
+ Assert.Equal($"{message}{Environment.NewLine}", consoleOutput.ToString());
+ consoleOutput.Dispose();
+ }
+
+ [Fact]
+ public void WriteLine_GivenInLambdaEnvironment_WhenCalled_ThenOverridesConsoleOutput()
+ {
+ // Given
+ Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", "test-function");
+ var wrapper = new ConsoleWrapper();
+ const string message = "test message";
+
+ // When
+ wrapper.WriteLine(message);
+
+ // Then
+ // Should not throw and should have attempted to override console
+ Assert.NotNull(Console.Out);
}
[Fact]
- public void WriteLine_Should_Write_To_Console()
+ public void WriteLine_GivenMultipleCallsInLambda_WhenConsoleIsReIntercepted_ThenReOverridesConsole()
{
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
- ConsoleWrapper.SetOut(_writer);
+ // Given
+ Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", "test-function");
+ var wrapper = new ConsoleWrapper();
+
+ // When - First call should override console
+ wrapper.WriteLine("First message");
+
+ // Simulate Lambda re-intercepting console by setting it to a wrapped writer
+ var lambdaInterceptedWriter = new StringWriter();
+ Console.SetOut(lambdaInterceptedWriter);
+
+ // Second call should detect and re-override
+ wrapper.WriteLine("Second message");
- // Act
- consoleWrapper.WriteLine("test message");
+ // Then
+ // Should not throw and console should be overridden again
+ Assert.NotNull(Console.Out);
+ lambdaInterceptedWriter.Dispose();
+ }
- // Assert
- Assert.Equal($"test message{Environment.NewLine}", _writer.ToString());
+ [Fact]
+ public void WriteLine_GivenLambdaEnvironmentWithConsoleOverrideFailing_WhenCalled_ThenDoesNotThrow()
+ {
+ // Given
+ Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", "test-function");
+ var wrapper = new ConsoleWrapper();
+
+ // When & Then - Should not throw even if console override fails
+ var exception = Record.Exception(() => wrapper.WriteLine("Test message"));
+ Assert.Null(exception);
}
[Fact]
- public void Error_Should_Write_To_Error_Console()
+ public void Debug_GivenInTestMode_WhenCalled_ThenWritesToTestOutputStream()
{
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
- ConsoleWrapper.SetOut(_writer);
- Console.SetError(_writer);
+ // Given
+ ConsoleWrapper.SetOut(_testWriter);
+ var wrapper = new ConsoleWrapper();
+ const string message = "debug message";
- // Act
- consoleWrapper.Error("error message");
- _writer.Flush();
+ // When
+ wrapper.Debug(message);
+
+ // Then
+ Assert.Equal($"{message}{Environment.NewLine}", _testWriter.ToString());
+ }
+
+ [Fact]
+ public void Debug_GivenNotInTestMode_WhenCalled_ThenDoesNotThrow()
+ {
+ // Given
+ var wrapper = new ConsoleWrapper();
+ ConsoleWrapper.ResetForTest(); // Ensure we're not in test mode
- // Assert
- Assert.Equal($"error message{Environment.NewLine}", _writer.ToString());
+ // When & Then - Just verify it doesn't throw
+ var exception = Record.Exception(() => wrapper.Debug("debug message"));
+ Assert.Null(exception);
}
[Fact]
- public void SetOut_Should_Override_Console_Output()
+ public void Error_GivenInTestMode_WhenCalled_ThenWritesToTestOutputStream()
{
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
- ConsoleWrapper.SetOut(_writer);
+ // Given
+ ConsoleWrapper.SetOut(_testWriter);
+ var wrapper = new ConsoleWrapper();
+ const string message = "error message";
- // Act
- consoleWrapper.WriteLine("test message");
+ // When
+ wrapper.Error(message);
- // Assert
- Assert.Equal($"test message{Environment.NewLine}", _writer.ToString());
+ // Then
+ Assert.Equal($"{message}{Environment.NewLine}", _testWriter.ToString());
}
[Fact]
- public void OverrideLambdaLogger_Should_Override_Console_Out()
+ public void Error_GivenNotInTestMode_WhenCalled_ThenDoesNotThrow()
{
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
- ConsoleWrapper.SetOut(_writer);
+ // Given
+ var wrapper = new ConsoleWrapper();
+ ConsoleWrapper.ResetForTest(); // Ensure we're not in test mode
+
+ // When & Then - The Error method creates its own StreamWriter,
+ // so we just verify it doesn't throw
+ var exception = Record.Exception(() => wrapper.Error("error message"));
+ Assert.Null(exception);
+ }
- // Act
- consoleWrapper.WriteLine("test message");
+ [Fact]
+ public void Error_GivenNotOverridden_WhenCalled_ThenDoesNotThrow()
+ {
+ // Given
+ var wrapper = new ConsoleWrapper();
+ ConsoleWrapper.ResetForTest(); // Reset to ensure _override is false
- // Assert
- Assert.Equal($"test message{Environment.NewLine}", _writer.ToString());
+ // When & Then - Just verify it doesn't throw
+ var exception = Record.Exception(() => wrapper.Error("error without override"));
+ Assert.Null(exception);
}
[Fact]
- public void WriteLine_WritesMessageToConsole()
+ public void SetOut_GivenTextWriter_WhenCalled_ThenEnablesTestMode()
{
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
- ConsoleWrapper.SetOut(_writer);
+ // Given
+ var testOutput = new StringWriter();
- // Act
- consoleWrapper.WriteLine("Test message");
+ // When
+ ConsoleWrapper.SetOut(testOutput);
- // Assert
- var output = _writer.ToString();
- Assert.Contains("Test message", output);
+ // Then
+ var wrapper = new ConsoleWrapper();
+ wrapper.WriteLine("test");
+ Assert.Equal($"test{Environment.NewLine}", testOutput.ToString());
+ testOutput.Dispose();
}
[Fact]
- public void SetOut_OverridesConsoleOutput()
+ public void ResetForTest_GivenTestModeEnabled_WhenCalled_ThenResetsToNormalMode()
{
- // Act
- ConsoleWrapper.SetOut(_writer);
- Console.WriteLine("Test override");
+ // Given
+ var testOutput = new StringWriter();
+ ConsoleWrapper.SetOut(testOutput);
- // Assert
- var output = _writer.ToString();
- Assert.Contains("Test override", output);
+ // When
+ ConsoleWrapper.ResetForTest();
+
+ // Then
+ var wrapper = new ConsoleWrapper();
+ var consoleOutput = new StringWriter();
+ Console.SetOut(consoleOutput);
+ wrapper.WriteLine("test");
+ Assert.Equal($"test{Environment.NewLine}", consoleOutput.ToString());
+ Assert.Empty(testOutput.ToString());
+ testOutput.Dispose();
+ consoleOutput.Dispose();
}
[Fact]
- public void StaticWriteLine_FormatsLogMessageCorrectly()
+ public void WriteLineStatic_GivenLogLevelAndMessage_WhenCalled_ThenFormatsWithTimestamp()
{
- // Arrange
- ConsoleWrapper.SetOut(_writer);
- var logLevel = "INFO";
- var message = "Test log message";
+ // Given
+ ConsoleWrapper.SetOut(_testWriter);
+ const string logLevel = "INFO";
+ const string message = "Test log message";
try
{
- // Act - Using reflection to call internal static method
+ // When - Using reflection to call internal static method
var method = typeof(ConsoleWrapper)
.GetMethod("WriteLine", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
@@ -124,21 +251,18 @@ public void StaticWriteLine_FormatsLogMessageCorrectly()
method.Invoke(null, new object[] { logLevel, message });
- // Assert
- var output = _writer.ToString();
-
- // Simple assertions that always work
+ // Then
+ var output = _testWriter.ToString();
Assert.Contains(logLevel, output);
Assert.Contains(message, output);
- // Verify basic structure without parsing timestamp
var lines = output.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
Assert.True(lines.Length > 0, "Output should contain at least one line");
var parts = lines[0].Split('\t');
Assert.True(parts.Length >= 3, "Output should contain at least 3 tab-separated parts");
-
- // Check that parts[0] contains a timestamp-like string (contains numbers, colons, etc.)
+
+ // Check that parts[0] contains a timestamp-like string
Assert.Matches(@"[\d\-:TZ.]", parts[0]);
Assert.Equal(logLevel, parts[1]);
Assert.Equal(message, parts[2]);
@@ -151,81 +275,28 @@ public void StaticWriteLine_FormatsLogMessageCorrectly()
}
[Fact]
- public void ClearOutputResetFlag_ResetsFlag()
- {
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
- ConsoleWrapper.SetOut(_writer);
-
- // Act
- consoleWrapper.WriteLine("First message"); // Should set the reset flag
- ConsoleWrapper.ClearOutputResetFlag();
- consoleWrapper.WriteLine("Second message"); // Should set it again
-
- // Assert
- Assert.Equal($"First message{Environment.NewLine}Second message{Environment.NewLine}", _writer.ToString());
- }
-
- [Fact]
- public void Debug_InTestMode_WritesToTestOutputStream()
- {
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
- ConsoleWrapper.SetOut(_writer);
-
- // Act
- consoleWrapper.Debug("debug message");
-
- // Assert
- Assert.Equal($"debug message{Environment.NewLine}", _writer.ToString());
- }
-
- [Fact]
- public void Debug_NotInTestMode_WritesToDebugConsole()
+ public void ClearOutputResetFlag_GivenAnyState_WhenCalled_ThenDoesNotThrow()
{
- // Since capturing Debug output is difficult in a unit test
- // We'll use a mock or just verify the path doesn't throw
-
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
- ConsoleWrapper.ResetForTest(); // Ensure we're not in test mode
-
- // Act & Assert - Just verify it doesn't throw
- var exception = Record.Exception(() => consoleWrapper.Debug("debug message"));
- Assert.Null(exception);
- }
+ // Given - any state
- [Fact]
- public void Error_DoesNotThrowWhenNotOverridden()
- {
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
- ConsoleWrapper.ResetForTest(); // Reset to ensure _override is false
-
- // Act & Assert - Just verify it doesn't throw
- var exception = Record.Exception(() => consoleWrapper.Error("error without override"));
+ // When & Then - Should not throw (kept for backward compatibility)
+ var exception = Record.Exception(() => ConsoleWrapper.ClearOutputResetFlag());
Assert.Null(exception);
}
[Fact]
- public void Error_UsesTestOutputStreamWhenInTestMode()
+ public void ClearOutputResetFlag_GivenMultipleCalls_WhenCalled_ThenAllowsRepeatedWrites()
{
- // Arrange
- var consoleWrapper = new ConsoleWrapper();
-
- // Set test mode
- ConsoleWrapper.SetOut(_writer);
-
- // Act
- consoleWrapper.Error("error in test mode");
+ // Given
+ var wrapper = new ConsoleWrapper();
+ ConsoleWrapper.SetOut(_testWriter);
- // Assert
- Assert.Contains("error in test mode", _writer.ToString());
- }
+ // When
+ wrapper.WriteLine("First message");
+ ConsoleWrapper.ClearOutputResetFlag();
+ wrapper.WriteLine("Second message");
- public void Dispose()
- {
- ConsoleWrapper.ResetForTest();
- _writer?.Dispose();
+ // Then
+ Assert.Equal($"First message{Environment.NewLine}Second message{Environment.NewLine}", _testWriter.ToString());
}
}
\ No newline at end of file
From 2a55568e2f0a30f43de087675a79fc3fb751f675 Mon Sep 17 00:00:00 2001
From: Henrique Graca <999396+hjgraca@users.noreply.github.com>
Date: Tue, 22 Jul 2025 10:08:29 +0100
Subject: [PATCH 2/3] test: add tests for console output handling and exception
scenarios in Lambda environment
---
.../ConsoleWrapperTests.cs | 60 +++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs b/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs
index 8245a7f0..00566594 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs
+++ b/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs
@@ -299,4 +299,64 @@ public void ClearOutputResetFlag_GivenMultipleCalls_WhenCalled_ThenAllowsRepeate
// Then
Assert.Equal($"First message{Environment.NewLine}Second message{Environment.NewLine}", _testWriter.ToString());
}
+
+ [Fact]
+ public void HasLambdaReInterceptedConsole_GivenConsoleOutThrowsException_WhenCalled_ThenReturnsTrue()
+ {
+ // Given
+ Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", "test-function");
+
+ // Create a mock TextWriter that throws when accessing its type
+ var mockWriter = new ThrowingTextWriter();
+ Console.SetOut(mockWriter);
+
+ var wrapper = new ConsoleWrapper();
+
+ // When - This will trigger HasLambdaReInterceptedConsole which should catch the exception
+ var exception = Record.Exception(() => wrapper.WriteLine("test message"));
+
+ // Then - Should not throw, the catch block should handle it
+ Assert.Null(exception);
+ }
+
+ [Fact]
+ public void OverrideLambdaLogger_GivenConsoleOpenStandardOutputThrows_WhenCalled_ThenDoesNotThrow()
+ {
+ // Given
+ Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", "test-function");
+
+ // Create a scenario where Console.OpenStandardOutput might fail
+ // We'll simulate this by creating conditions that trigger the catch block
+ var wrapper = new ConsoleWrapper();
+
+ // When - Multiple calls to trigger the override logic and potential failures
+ var exception = Record.Exception(() =>
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ wrapper.WriteLine($"test message {i}");
+ }
+ });
+
+ // Then - Should not throw even if StreamWriter creation fails
+ Assert.Null(exception);
+ }
+
+ // Helper class to simulate exceptions in Console.Out.GetType()
+ private class ThrowingTextWriter : TextWriter
+ {
+ public override System.Text.Encoding Encoding => System.Text.Encoding.UTF8;
+
+ public override void Write(char value)
+ {
+ // Simulate the scenario where accessing type information fails
+ // by throwing when any operation is performed
+ throw new InvalidOperationException("Simulated exception during console operation");
+ }
+
+ public override void WriteLine(string value)
+ {
+ throw new InvalidOperationException("Simulated exception during console operation");
+ }
+ }
}
\ No newline at end of file
From 49b66e8b5266f735ed54a33c122853d09f8caf35 Mon Sep 17 00:00:00 2001
From: Henrique Graca <999396+hjgraca@users.noreply.github.com>
Date: Tue, 22 Jul 2025 10:43:54 +0100
Subject: [PATCH 3/3] refactor: enhance console handling in Lambda by allowing
dependency injection for output and logger overrides
---
.../Core/ConsoleWrapper.cs | 18 +++-
.../ConsoleWrapperTests.cs | 83 +++++++------------
2 files changed, 42 insertions(+), 59 deletions(-)
diff --git a/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs b/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs
index dc245fb5..4a124d94 100644
--- a/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs
@@ -89,12 +89,17 @@ private static bool ShouldOverrideConsole()
return isLambda && (!_override || HasLambdaReInterceptedConsole());
}
- private static bool HasLambdaReInterceptedConsole()
+ internal static bool HasLambdaReInterceptedConsole()
+ {
+ return HasLambdaReInterceptedConsole(() => Console.Out);
+ }
+
+ internal static bool HasLambdaReInterceptedConsole(Func consoleOutAccessor)
{
// Lambda might re-intercept console between init and handler execution
try
{
- var currentOut = Console.Out;
+ var currentOut = consoleOutAccessor();
// Check if current output stream looks like it might be Lambda's wrapper
var typeName = currentOut.GetType().FullName ?? "";
return typeName.Contains("Lambda") || typeName == "System.IO.TextWriter+SyncTextWriter";
@@ -105,12 +110,17 @@ private static bool HasLambdaReInterceptedConsole()
}
}
- private static void OverrideLambdaLogger()
+ internal static void OverrideLambdaLogger()
+ {
+ OverrideLambdaLogger(() => Console.OpenStandardOutput());
+ }
+
+ internal static void OverrideLambdaLogger(Func standardOutputOpener)
{
try
{
// Force override of LambdaLogger
- var standardOutput = new StreamWriter(Console.OpenStandardOutput())
+ var standardOutput = new StreamWriter(standardOutputOpener())
{
AutoFlush = true
};
diff --git a/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs b/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs
index 00566594..25cea21d 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs
+++ b/libraries/tests/AWS.Lambda.Powertools.Common.Tests/ConsoleWrapperTests.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using NSubstitute;
using Xunit;
namespace AWS.Lambda.Powertools.Common.Tests;
@@ -15,13 +16,13 @@ public ConsoleWrapperTests()
// Store original console outputs
_originalOut = Console.Out;
_originalError = Console.Error;
-
+
// Setup test writer
_testWriter = new StringWriter();
-
+
// Reset ConsoleWrapper state before each test
ConsoleWrapper.ResetForTest();
-
+
// Clear any Lambda environment variables
Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", null);
}
@@ -31,13 +32,13 @@ public void Dispose()
// Restore original console outputs
Console.SetOut(_originalOut);
Console.SetError(_originalError);
-
+
// Reset ConsoleWrapper state after each test
ConsoleWrapper.ResetForTest();
-
+
// Clear any test environment variables
Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", null);
-
+
_testWriter?.Dispose();
}
@@ -240,7 +241,8 @@ public void WriteLineStatic_GivenLogLevelAndMessage_WhenCalled_ThenFormatsWithTi
{
// When - Using reflection to call internal static method
var method = typeof(ConsoleWrapper)
- .GetMethod("WriteLine", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
+ .GetMethod("WriteLine",
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
if (method == null)
{
@@ -299,64 +301,35 @@ public void ClearOutputResetFlag_GivenMultipleCalls_WhenCalled_ThenAllowsRepeate
// Then
Assert.Equal($"First message{Environment.NewLine}Second message{Environment.NewLine}", _testWriter.ToString());
}
+
+ // from here
[Fact]
- public void HasLambdaReInterceptedConsole_GivenConsoleOutThrowsException_WhenCalled_ThenReturnsTrue()
+ public void HasLambdaReInterceptedConsole_WhenConsoleOutAccessThrows_ThenReturnsTrueFromCatchBlock()
{
- // Given
- Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", "test-function");
-
- // Create a mock TextWriter that throws when accessing its type
- var mockWriter = new ThrowingTextWriter();
- Console.SetOut(mockWriter);
-
- var wrapper = new ConsoleWrapper();
-
- // When - This will trigger HasLambdaReInterceptedConsole which should catch the exception
- var exception = Record.Exception(() => wrapper.WriteLine("test message"));
-
- // Then - Should not throw, the catch block should handle it
- Assert.Null(exception);
+ // Given - A function that throws when called (simulating Console.Out access failure)
+ Func throwingAccessor = () => throw new InvalidOperationException("Console.Out access failed");
+
+ // When - Call the internal method with the throwing accessor
+ var result = ConsoleWrapper.HasLambdaReInterceptedConsole(throwingAccessor);
+
+ // Then - Should return true from the catch block (lines 102-105)
+ Assert.True(result);
}
[Fact]
- public void OverrideLambdaLogger_GivenConsoleOpenStandardOutputThrows_WhenCalled_ThenDoesNotThrow()
+ public void OverrideLambdaLogger_WhenOpenStandardOutputThrows_ThenSetsOverrideToFalse()
{
// Given
- Environment.SetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME", "test-function");
-
- // Create a scenario where Console.OpenStandardOutput might fail
- // We'll simulate this by creating conditions that trigger the catch block
- var wrapper = new ConsoleWrapper();
-
- // When - Multiple calls to trigger the override logic and potential failures
- var exception = Record.Exception(() =>
- {
- for (int i = 0; i < 5; i++)
- {
- wrapper.WriteLine($"test message {i}");
- }
- });
+ ConsoleWrapper.ResetForTest();
- // Then - Should not throw even if StreamWriter creation fails
- Assert.Null(exception);
- }
+ // A function that throws when called (simulating Console.OpenStandardOutput failure)
+ Func throwingOpener = () => throw new UnauthorizedAccessException("Cannot open standard output");
- // Helper class to simulate exceptions in Console.Out.GetType()
- private class ThrowingTextWriter : TextWriter
- {
- public override System.Text.Encoding Encoding => System.Text.Encoding.UTF8;
+ // When - Call the internal method with the throwing opener
+ var exception = Record.Exception(() => ConsoleWrapper.OverrideLambdaLogger(throwingOpener));
- public override void Write(char value)
- {
- // Simulate the scenario where accessing type information fails
- // by throwing when any operation is performed
- throw new InvalidOperationException("Simulated exception during console operation");
- }
-
- public override void WriteLine(string value)
- {
- throw new InvalidOperationException("Simulated exception during console operation");
- }
+ // Then - Should not throw (catch block handles it on lines 120-123)
+ Assert.Null(exception);
}
}
\ No newline at end of file