Skip to content

Commit 6da41bd

Browse files
authored
test: Extracted IResourceWatcherMetrics interface for mocking in unit tests (#526)
This PR enables testability for the `ResourceWatcher<TEntity>` class see also #523 by extracting a new interface `IResourceWatcherMetrics<TEntity>`.
1 parent 79646b3 commit 6da41bd

File tree

5 files changed

+75
-8
lines changed

5 files changed

+75
-8
lines changed

src/KubeOps/Operator/Builder/OperatorBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ internal IOperatorBuilder AddOperatorBase(OperatorSettings settings)
196196
Services.AddScoped(typeof(IEventQueue<>), typeof(EventQueue<>));
197197

198198
// Support all the metrics
199-
Services.AddSingleton(typeof(ResourceWatcherMetrics<>));
199+
Services.AddSingleton(typeof(IResourceWatcherMetrics<>), typeof(ResourceWatcherMetrics<>));
200200
Services.AddSingleton(typeof(ResourceCacheMetrics<>));
201201
Services.AddSingleton(typeof(ResourceControllerMetrics<>));
202202

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using k8s;
2+
using k8s.Models;
3+
using Prometheus;
4+
5+
namespace KubeOps.Operator.DevOps;
6+
7+
internal interface IResourceWatcherMetrics<TEntity> : IResourceWatcherMetrics
8+
where TEntity : IKubernetesObject<V1ObjectMeta>
9+
{
10+
}
11+
12+
internal interface IResourceWatcherMetrics
13+
{
14+
IGauge Running { get; }
15+
16+
ICounter WatchedEvents { get; }
17+
18+
ICounter WatcherExceptions { get; }
19+
20+
ICounter WatcherClosed { get; }
21+
}

src/KubeOps/Operator/DevOps/ResourceWatcherMetrics{TEntity}.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace KubeOps.Operator.DevOps;
77

8-
internal class ResourceWatcherMetrics<TEntity>
8+
internal class ResourceWatcherMetrics<TEntity> : IResourceWatcherMetrics<TEntity>
99
where TEntity : IKubernetesObject<V1ObjectMeta>
1010
{
1111
private static readonly string[] Labels = { "operator", "kind", "group", "version", "scope", };
@@ -44,11 +44,11 @@ public ResourceWatcherMetrics(OperatorSettings settings)
4444
.WithLabels(labelValues);
4545
}
4646

47-
public Gauge.Child Running { get; }
47+
public IGauge Running { get; }
4848

49-
public Counter.Child WatchedEvents { get; }
49+
public ICounter WatchedEvents { get; }
5050

51-
public Counter.Child WatcherExceptions { get; }
51+
public ICounter WatcherExceptions { get; }
5252

53-
public Counter.Child WatcherClosed { get; }
53+
public ICounter WatcherClosed { get; }
5454
}

src/KubeOps/Operator/Kubernetes/ResourceWatcher{TEntity}.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ internal class ResourceWatcher<TEntity> : IDisposable, IResourceWatcher<TEntity>
1717
private readonly Subject<WatchEvent> _watchEvents = new();
1818
private readonly IKubernetesClient _client;
1919
private readonly ILogger<ResourceWatcher<TEntity>> _logger;
20-
private readonly ResourceWatcherMetrics<TEntity> _metrics;
20+
private readonly IResourceWatcherMetrics<TEntity> _metrics;
2121
private readonly OperatorSettings _settings;
2222
private readonly Subject<TimeSpan> _reconnectHandler = new();
2323
private readonly IDisposable _reconnectSubscription;
@@ -30,7 +30,7 @@ internal class ResourceWatcher<TEntity> : IDisposable, IResourceWatcher<TEntity>
3030
public ResourceWatcher(
3131
IKubernetesClient client,
3232
ILogger<ResourceWatcher<TEntity>> logger,
33-
ResourceWatcherMetrics<TEntity> metrics,
33+
IResourceWatcherMetrics<TEntity> metrics,
3434
OperatorSettings settings)
3535
{
3636
_client = client;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using FluentAssertions;
2+
using k8s;
3+
using k8s.Models;
4+
using KubeOps.KubernetesClient;
5+
using KubeOps.Operator;
6+
using KubeOps.Operator.DevOps;
7+
using KubeOps.Operator.Kubernetes;
8+
using KubeOps.Testing;
9+
using Microsoft.Extensions.Logging.Abstractions;
10+
using Moq;
11+
using Prometheus;
12+
using Xunit;
13+
14+
namespace KubeOps.Test.Operator.Kubernetes;
15+
16+
public class ResourceWatcherTest
17+
{
18+
[KubernetesEntity]
19+
public class TestResource : IKubernetesObject<V1ObjectMeta>
20+
{
21+
public string ApiVersion { get; set; } = null!;
22+
public string Kind { get; set; } = null!;
23+
public V1ObjectMeta Metadata { get; set; } = null!;
24+
}
25+
26+
private readonly IKubernetesClient _client = new MockKubernetesClient();
27+
private readonly Mock<IResourceWatcherMetrics<TestResource>> _metrics = new();
28+
29+
[Fact]
30+
public async Task Should_Not_Dispose_Reconnect_Subject_Or_Throw_Exception_After_Restarts()
31+
{
32+
var settings = new OperatorSettings();
33+
34+
_metrics.Setup(c => c.Running).Returns(Mock.Of<IGauge>());
35+
36+
using var resourceWatcher = new ResourceWatcher<TestResource>(_client, new NullLogger<ResourceWatcher<TestResource>>(), _metrics.Object, settings);
37+
38+
await resourceWatcher.StartAsync();
39+
40+
await resourceWatcher.StopAsync();
41+
42+
await resourceWatcher.StartAsync();
43+
44+
resourceWatcher.WatchEvents.Should().NotBeNull();
45+
}
46+
}

0 commit comments

Comments
 (0)