Skip to content

Commit 12b7ee1

Browse files
committed
more tests
1 parent a52d510 commit 12b7ee1

6 files changed

Lines changed: 1142 additions & 0 deletions

File tree

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
using NServiceBus.Logging;
2+
3+
/// <summary>
4+
/// Tests for the DeferredLoggerFactory pattern using a local test implementation.
5+
/// This tests the deferred logging pattern used for capturing logs before endpoint startup.
6+
/// </summary>
7+
public class DeferredLoggerFactoryTests
8+
{
9+
[Fact]
10+
public void GetLogger_with_type_returns_NamedLogger()
11+
{
12+
var factory = new TestDeferredLoggerFactory(LogLevel.Debug);
13+
14+
var log = factory.GetLogger(typeof(DeferredLoggerFactoryTests));
15+
16+
Assert.NotNull(log);
17+
Assert.IsType<TestNamedLogger>(log);
18+
}
19+
20+
[Fact]
21+
public void GetLogger_with_string_returns_NamedLogger()
22+
{
23+
var factory = new TestDeferredLoggerFactory(LogLevel.Debug);
24+
25+
var log = factory.GetLogger("TestLogger");
26+
27+
Assert.NotNull(log);
28+
Assert.IsType<TestNamedLogger>(log);
29+
}
30+
31+
[Fact]
32+
public void Write_adds_message_to_deferred_logs()
33+
{
34+
var factory = new TestDeferredLoggerFactory(LogLevel.Debug);
35+
36+
factory.Write("TestLogger", LogLevel.Info, "test message");
37+
38+
Assert.Single(factory.DeferredLogs);
39+
Assert.True(factory.DeferredLogs.ContainsKey("TestLogger"));
40+
Assert.Single(factory.DeferredLogs["TestLogger"]);
41+
var (level, message) = factory.DeferredLogs["TestLogger"].First();
42+
Assert.Equal(LogLevel.Info, level);
43+
Assert.Equal("test message", message);
44+
}
45+
46+
[Fact]
47+
public void Write_filters_messages_below_configured_level()
48+
{
49+
var factory = new TestDeferredLoggerFactory(LogLevel.Warn);
50+
51+
factory.Write("TestLogger", LogLevel.Debug, "debug message");
52+
factory.Write("TestLogger", LogLevel.Info, "info message");
53+
54+
Assert.Empty(factory.DeferredLogs);
55+
}
56+
57+
[Fact]
58+
public void Write_accepts_messages_at_or_above_configured_level()
59+
{
60+
var factory = new TestDeferredLoggerFactory(LogLevel.Warn);
61+
62+
factory.Write("TestLogger", LogLevel.Warn, "warn message");
63+
factory.Write("TestLogger", LogLevel.Error, "error message");
64+
factory.Write("TestLogger", LogLevel.Fatal, "fatal message");
65+
66+
Assert.Single(factory.DeferredLogs);
67+
Assert.Equal(3, factory.DeferredLogs["TestLogger"].Count);
68+
}
69+
70+
[Fact]
71+
public void Write_groups_messages_by_logger_name()
72+
{
73+
var factory = new TestDeferredLoggerFactory(LogLevel.Debug);
74+
75+
factory.Write("Logger1", LogLevel.Info, "message 1");
76+
factory.Write("Logger2", LogLevel.Info, "message 2");
77+
factory.Write("Logger1", LogLevel.Info, "message 3");
78+
79+
Assert.Equal(2, factory.DeferredLogs.Count);
80+
Assert.Equal(2, factory.DeferredLogs["Logger1"].Count);
81+
Assert.Single(factory.DeferredLogs["Logger2"]);
82+
}
83+
84+
[Theory]
85+
[InlineData(LogLevel.Debug, true, true, true, true, true)]
86+
[InlineData(LogLevel.Info, false, true, true, true, true)]
87+
[InlineData(LogLevel.Warn, false, false, true, true, true)]
88+
[InlineData(LogLevel.Error, false, false, false, true, true)]
89+
[InlineData(LogLevel.Fatal, false, false, false, false, true)]
90+
public void GetLogger_sets_enabled_levels_correctly(
91+
LogLevel configuredLevel,
92+
bool expectedDebug,
93+
bool expectedInfo,
94+
bool expectedWarn,
95+
bool expectedError,
96+
bool expectedFatal)
97+
{
98+
var factory = new TestDeferredLoggerFactory(configuredLevel);
99+
100+
var log = factory.GetLogger("TestLogger");
101+
102+
Assert.Equal(expectedDebug, log.IsDebugEnabled);
103+
Assert.Equal(expectedInfo, log.IsInfoEnabled);
104+
Assert.Equal(expectedWarn, log.IsWarnEnabled);
105+
Assert.Equal(expectedError, log.IsErrorEnabled);
106+
Assert.Equal(expectedFatal, log.IsFatalEnabled);
107+
}
108+
109+
[Fact]
110+
public void Logger_writes_through_factory()
111+
{
112+
var factory = new TestDeferredLoggerFactory(LogLevel.Debug);
113+
var logger = factory.GetLogger("TestLogger");
114+
115+
logger.Info("test info message");
116+
logger.Error("test error message");
117+
118+
Assert.Single(factory.DeferredLogs);
119+
Assert.Equal(2, factory.DeferredLogs["TestLogger"].Count);
120+
}
121+
}
122+
123+
/// <summary>
124+
/// Test implementation of DeferredLoggerFactory that mirrors the production code.
125+
/// </summary>
126+
class TestDeferredLoggerFactory : NServiceBus.Logging.ILoggerFactory
127+
{
128+
LogLevel level;
129+
bool isDebugEnabled;
130+
bool isInfoEnabled;
131+
bool isWarnEnabled;
132+
bool isErrorEnabled;
133+
bool isFatalEnabled;
134+
135+
public ConcurrentDictionary<string, ConcurrentQueue<(LogLevel level, string message)>> DeferredLogs { get; } = [];
136+
137+
public TestDeferredLoggerFactory(LogLevel level)
138+
{
139+
this.level = level;
140+
isDebugEnabled = LogLevel.Debug >= level;
141+
isInfoEnabled = LogLevel.Info >= level;
142+
isWarnEnabled = LogLevel.Warn >= level;
143+
isErrorEnabled = LogLevel.Error >= level;
144+
isFatalEnabled = LogLevel.Fatal >= level;
145+
}
146+
147+
public ILog GetLogger(Type type) =>
148+
GetLogger(type.FullName!);
149+
150+
public ILog GetLogger(string name) =>
151+
new TestNamedLogger(name, this)
152+
{
153+
IsDebugEnabled = isDebugEnabled,
154+
IsInfoEnabled = isInfoEnabled,
155+
IsWarnEnabled = isWarnEnabled,
156+
IsErrorEnabled = isErrorEnabled,
157+
IsFatalEnabled = isFatalEnabled
158+
};
159+
160+
public void Write(string name, LogLevel messageLevel, string message)
161+
{
162+
if (messageLevel < level)
163+
{
164+
return;
165+
}
166+
var logQueue = DeferredLogs.GetOrAdd(name, _ => new ConcurrentQueue<(LogLevel level, string message)>());
167+
logQueue.Enqueue((messageLevel, message));
168+
}
169+
}
170+
171+
/// <summary>
172+
/// Test implementation of NamedLogger that mirrors the production code.
173+
/// </summary>
174+
class TestNamedLogger : ILog
175+
{
176+
string name;
177+
TestDeferredLoggerFactory factory;
178+
179+
public TestNamedLogger(string name, TestDeferredLoggerFactory factory)
180+
{
181+
this.name = name;
182+
this.factory = factory;
183+
}
184+
185+
public bool IsDebugEnabled { get; internal set; }
186+
public bool IsInfoEnabled { get; internal set; }
187+
public bool IsWarnEnabled { get; internal set; }
188+
public bool IsErrorEnabled { get; internal set; }
189+
public bool IsFatalEnabled { get; internal set; }
190+
191+
public void Debug(string message) =>
192+
factory.Write(name, LogLevel.Debug, message);
193+
194+
public void Debug(string message, Exception exception) =>
195+
factory.Write(name, LogLevel.Debug, message + Environment.NewLine + exception);
196+
197+
public void DebugFormat(string format, params object[] args) =>
198+
factory.Write(name, LogLevel.Debug, string.Format(format, args));
199+
200+
public void Info(string message) =>
201+
factory.Write(name, LogLevel.Info, message);
202+
203+
public void Info(string message, Exception exception) =>
204+
factory.Write(name, LogLevel.Info, message + Environment.NewLine + exception);
205+
206+
public void InfoFormat(string format, params object[] args) =>
207+
factory.Write(name, LogLevel.Info, string.Format(format, args));
208+
209+
public void Warn(string message) =>
210+
factory.Write(name, LogLevel.Warn, message);
211+
212+
public void Warn(string message, Exception exception) =>
213+
factory.Write(name, LogLevel.Warn, message + Environment.NewLine + exception);
214+
215+
public void WarnFormat(string format, params object[] args) =>
216+
factory.Write(name, LogLevel.Warn, string.Format(format, args));
217+
218+
public void Error(string message) =>
219+
factory.Write(name, LogLevel.Error, message);
220+
221+
public void Error(string message, Exception exception) =>
222+
factory.Write(name, LogLevel.Error, message + Environment.NewLine + exception);
223+
224+
public void ErrorFormat(string format, params object[] args) =>
225+
factory.Write(name, LogLevel.Error, string.Format(format, args));
226+
227+
public void Fatal(string message) =>
228+
factory.Write(name, LogLevel.Fatal, message);
229+
230+
public void Fatal(string message, Exception exception) =>
231+
factory.Write(name, LogLevel.Fatal, message + Environment.NewLine + exception);
232+
233+
public void FatalFormat(string format, params object[] args) =>
234+
factory.Write(name, LogLevel.Fatal, string.Format(format, args));
235+
}

src/Tests/LoggerFactoryTests.cs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using Microsoft.Extensions.Logging;
2+
using NServiceBus.Logging;
3+
4+
/// <summary>
5+
/// Tests for the LoggerFactory class through the public MicrosoftLogFactory API.
6+
/// </summary>
7+
public class LoggerFactoryTests
8+
{
9+
[Fact]
10+
public void GetLogger_with_type_returns_ILog_implementation()
11+
{
12+
var mockFactory = new MockMsLoggerFactory();
13+
var factory = new MicrosoftLogFactory();
14+
factory.UseMsFactory(mockFactory);
15+
16+
// Use LogManager to get a logger after configuring MicrosoftLogFactory
17+
var loggerFactory = GetLoggingFactory(factory);
18+
var log = loggerFactory.GetLogger(typeof(LoggerFactoryTests));
19+
20+
Assert.NotNull(log);
21+
Assert.IsAssignableFrom<ILog>(log);
22+
}
23+
24+
[Fact]
25+
public void GetLogger_with_string_returns_ILog_implementation()
26+
{
27+
var mockFactory = new MockMsLoggerFactory();
28+
var factory = new MicrosoftLogFactory();
29+
factory.UseMsFactory(mockFactory);
30+
31+
var loggerFactory = GetLoggingFactory(factory);
32+
var log = loggerFactory.GetLogger("MyCustomLogger");
33+
34+
Assert.NotNull(log);
35+
Assert.IsAssignableFrom<ILog>(log);
36+
}
37+
38+
[Fact]
39+
public void GetLogger_with_type_creates_logger_with_type_name()
40+
{
41+
var mockFactory = new MockMsLoggerFactory();
42+
var factory = new MicrosoftLogFactory();
43+
factory.UseMsFactory(mockFactory);
44+
45+
var loggerFactory = GetLoggingFactory(factory);
46+
var log = loggerFactory.GetLogger(typeof(LoggerFactoryTests));
47+
48+
Assert.Single(mockFactory.Loggers);
49+
Assert.True(mockFactory.Loggers.ContainsKey(typeof(LoggerFactoryTests).FullName!));
50+
}
51+
52+
[Fact]
53+
public void GetLogger_with_string_creates_logger_with_given_name()
54+
{
55+
var mockFactory = new MockMsLoggerFactory();
56+
var factory = new MicrosoftLogFactory();
57+
factory.UseMsFactory(mockFactory);
58+
59+
var loggerFactory = GetLoggingFactory(factory);
60+
var log = loggerFactory.GetLogger("MyCustomLogger");
61+
62+
Assert.Single(mockFactory.Loggers);
63+
Assert.True(mockFactory.Loggers.ContainsKey("MyCustomLogger"));
64+
}
65+
66+
[Fact]
67+
public void GetLogger_with_type_logs_to_correct_logger()
68+
{
69+
var mockFactory = new MockMsLoggerFactory();
70+
var factory = new MicrosoftLogFactory();
71+
factory.UseMsFactory(mockFactory);
72+
73+
var loggerFactory = GetLoggingFactory(factory);
74+
var log = loggerFactory.GetLogger(typeof(LoggerFactoryTests));
75+
log.Info("test message");
76+
77+
var mockLogger = mockFactory.Loggers[typeof(LoggerFactoryTests).FullName!];
78+
Assert.Single(mockLogger.LogEntries);
79+
Assert.Equal("test message", mockLogger.LogEntries[0].Message);
80+
}
81+
82+
[Fact]
83+
public void GetLogger_with_string_logs_to_correct_logger()
84+
{
85+
var mockFactory = new MockMsLoggerFactory();
86+
var factory = new MicrosoftLogFactory();
87+
factory.UseMsFactory(mockFactory);
88+
89+
var loggerFactory = GetLoggingFactory(factory);
90+
var log = loggerFactory.GetLogger("CustomLogger");
91+
log.Warn("warning message");
92+
93+
var mockLogger = mockFactory.Loggers["CustomLogger"];
94+
Assert.Single(mockLogger.LogEntries);
95+
Assert.Equal("warning message", mockLogger.LogEntries[0].Message);
96+
}
97+
98+
[Fact]
99+
public void GetLogger_called_multiple_times_with_same_type_returns_separate_wrapper_instances()
100+
{
101+
var mockFactory = new MockMsLoggerFactory();
102+
var factory = new MicrosoftLogFactory();
103+
factory.UseMsFactory(mockFactory);
104+
105+
var loggerFactory = GetLoggingFactory(factory);
106+
var log1 = loggerFactory.GetLogger(typeof(LoggerFactoryTests));
107+
var log2 = loggerFactory.GetLogger(typeof(LoggerFactoryTests));
108+
109+
Assert.NotSame(log1, log2);
110+
}
111+
112+
[Fact]
113+
public void GetLogger_called_multiple_times_with_different_types_creates_different_loggers()
114+
{
115+
var mockFactory = new MockMsLoggerFactory();
116+
var factory = new MicrosoftLogFactory();
117+
factory.UseMsFactory(mockFactory);
118+
119+
var loggerFactory = GetLoggingFactory(factory);
120+
var log1 = loggerFactory.GetLogger(typeof(LoggerFactoryTests));
121+
var log2 = loggerFactory.GetLogger(typeof(string));
122+
123+
Assert.Equal(2, mockFactory.Loggers.Count);
124+
}
125+
126+
// Helper to invoke protected GetLoggingFactory method
127+
static NServiceBus.Logging.ILoggerFactory GetLoggingFactory(MicrosoftLogFactory factory)
128+
{
129+
var method = typeof(MicrosoftLogFactory).GetMethod(
130+
"GetLoggingFactory",
131+
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
132+
try
133+
{
134+
return (NServiceBus.Logging.ILoggerFactory)method!.Invoke(factory, null)!;
135+
}
136+
catch (System.Reflection.TargetInvocationException ex)
137+
{
138+
throw ex.InnerException!;
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)