Skip to content

Commit e0b7890

Browse files
authored
Allow controlling the disposal of provided transport and HTTP client instances (#487)
* Add parameter to control disposal of provided HTTP client when creating transport. * Add a parameter to control disposal of provided instance when creating DB client. * Simplify tests by using Mock instances.
1 parent 97f621d commit e0b7890

File tree

5 files changed

+206
-30
lines changed

5 files changed

+206
-30
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using ArangoDBNetStandard;
2+
using ArangoDBNetStandard.Transport;
3+
using Moq;
4+
using Moq.Protected;
5+
using System.Net.Http;
6+
using Xunit;
7+
8+
namespace ArangoDBNetStandardTest
9+
{
10+
public class ArangoDBClientTest
11+
{
12+
public ArangoDBClientTest()
13+
{
14+
}
15+
16+
[Fact]
17+
public void Dispose_ShouldDisposeTransport_WhenTransportDisposalIsNotSuppressed()
18+
{
19+
var mockTransport = new Mock<IApiClientTransport>();
20+
21+
var dbClient = new ArangoDBClient(
22+
mockTransport.Object,
23+
suppressTransportDisposal: false);
24+
25+
// Act
26+
dbClient.Dispose();
27+
28+
// Assert
29+
mockTransport.Verify(client => client.Dispose(), Times.Once);
30+
}
31+
32+
[Fact]
33+
public void Dispose_ShouldNotDisposeTransport_WhenTransportDisposalIsSuppressed()
34+
{
35+
var mockTransport = new Mock<IApiClientTransport>();
36+
37+
var dbClient = new ArangoDBClient(
38+
mockTransport.Object,
39+
suppressTransportDisposal: true);
40+
41+
// Act
42+
dbClient.Dispose();
43+
44+
// Assert
45+
mockTransport.Verify(transport => transport.Dispose(), Times.Never);
46+
}
47+
48+
[Fact]
49+
public void Dispose_ShouldDisposeHttpClient_WhenClientDisposalIsNotSuppressed()
50+
{
51+
var mockMessageHandler = new Mock<HttpMessageHandler>();
52+
53+
var httpClient = new HttpClient(mockMessageHandler.Object);
54+
55+
var dbClient = new ArangoDBClient(
56+
httpClient,
57+
suppressClientDisposal: false);
58+
59+
// Act
60+
dbClient.Dispose();
61+
62+
// Assert
63+
mockMessageHandler.Protected().Verify("Dispose", Times.Once(), true, true);
64+
}
65+
66+
[Fact]
67+
public void Dispose_ShouldNotDisposeHttpClient_WhenClientDisposalIsSuppressed()
68+
{
69+
var mockMessageHandler = new Mock<HttpMessageHandler>();
70+
71+
var httpClient = new HttpClient(mockMessageHandler.Object);
72+
73+
var dbClient = new ArangoDBClient(
74+
httpClient,
75+
suppressClientDisposal: true);
76+
77+
// Act
78+
dbClient.Dispose();
79+
80+
// Assert
81+
mockMessageHandler.Protected().Verify("Dispose", Times.Never(), true, true);
82+
}
83+
}
84+
}

arangodb-net-standard.Test/Transport/Http/HttpApiTransportTest.cs

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,43 @@
33
using ArangoDBNetStandard.AuthApi.Models;
44
using ArangoDBNetStandard.DatabaseApi;
55
using ArangoDBNetStandard.Transport.Http;
6+
using Moq;
7+
using Moq.Protected;
68
using System;
9+
using System.Net.Http;
710
using System.Threading.Tasks;
811
using Xunit;
912

1013
namespace ArangoDBNetStandardTest.Transport.Http
1114
{
12-
public class HttpApiTransportTest: IClassFixture<HttpApiTransportTestFixture>
15+
public class HttpApiTransportTest : IClassFixture<HttpApiTransportTestFixture>
1316
{
14-
private HttpApiTransportTestFixture _fixture;
17+
private readonly HttpApiTransportTestFixture _fixture;
18+
19+
private readonly Uri _hostUri;
1520

1621
public HttpApiTransportTest(HttpApiTransportTestFixture fixture)
1722
{
1823
_fixture = fixture;
24+
_hostUri = new Uri($"http://{fixture.ArangoDbHost}:{fixture.ArangoDbPort}/");
1925
}
2026

2127
[Fact]
2228
public async Task UseVpack_ShouldSucceed()
2329
{
24-
string arangodbBaseUrl = $"http://{_fixture.ArangoDbHost}:{_fixture.ArangoDbPort}/";
2530
using (var transport = HttpApiTransport.UsingBasicAuth(
26-
new Uri(arangodbBaseUrl),
27-
nameof(HttpApiTransportTest),
28-
"root",
29-
"root"))
31+
_hostUri,
32+
_fixture.DatabaseName,
33+
_fixture.Username,
34+
_fixture.Password))
3035
{
3136
transport.UseVPackContentType();
32-
string docUrl = arangodbBaseUrl + "_db/" + nameof(HttpApiTransportTest) + $"/_admin/echo";
33-
using (var response = await transport.GetAsync(docUrl))
37+
using (var response = await transport.GetAsync("_admin/echo"))
3438
{
39+
Assert.True(
40+
response.IsSuccessStatusCode,
41+
$"Error response. Status code: {response.StatusCode}");
42+
3543
Assert.Equal("application/x-velocypack", response.Content.Headers.ContentType.MediaType);
3644
}
3745
}
@@ -41,9 +49,8 @@ public async Task UseVpack_ShouldSucceed()
4149
public async Task SetJwt_ShouldSucceed()
4250
{
4351
string jwtToken = null;
44-
string arangodbBaseUrl = $"http://{_fixture.ArangoDbHost}:{_fixture.ArangoDbPort}/";
4552
using (var transport = HttpApiTransport.UsingNoAuth(
46-
new Uri(arangodbBaseUrl),
53+
_hostUri,
4754
nameof(HttpApiTransportTest)))
4855
{
4956
var authClient = new AuthApiClient(transport);
@@ -70,9 +77,8 @@ public async Task SetJwt_ShouldSucceed()
7077
public async Task UsingJwtAuth_ShouldSucceed()
7178
{
7279
string jwtToken = null;
73-
string arangodbBaseUrl = $"http://{_fixture.ArangoDbHost}:{_fixture.ArangoDbPort}/";
7480
using (var transport = HttpApiTransport.UsingNoAuth(
75-
new Uri(arangodbBaseUrl),
81+
_hostUri,
7682
nameof(HttpApiTransportTest)))
7783
{
7884
var authClient = new AuthApiClient(transport);
@@ -95,7 +101,7 @@ public async Task UsingJwtAuth_ShouldSucceed()
95101

96102
// Use token in a new transport created via `UsingJwtAuth`.
97103
using (var transport = HttpApiTransport.UsingJwtAuth(
98-
new Uri(arangodbBaseUrl),
104+
_hostUri,
99105
nameof(HttpApiTransportTest),
100106
jwtToken))
101107
{
@@ -104,5 +110,43 @@ public async Task UsingJwtAuth_ShouldSucceed()
104110
Assert.NotEmpty(userDatabasesResponse.Result);
105111
}
106112
}
113+
114+
[Fact]
115+
public void Dispose_ShouldDisposeHttpClient_WhenDisposalIsNotSuppressed()
116+
{
117+
var mockMessageHandler = new Mock<HttpMessageHandler>();
118+
119+
var httpClient = new HttpClient(mockMessageHandler.Object);
120+
121+
var transport = new HttpApiTransport(
122+
httpClient,
123+
HttpContentType.Json,
124+
suppressClientDisposal: false);
125+
126+
// Act
127+
transport.Dispose();
128+
129+
// Assert
130+
mockMessageHandler.Protected().Verify("Dispose", Times.Once(), true, true);
131+
}
132+
133+
[Fact]
134+
public void Dispose_ShouldNotDisposeHttpClient_WhenDisposalIsSuppressed()
135+
{
136+
var mockMessageHandler = new Mock<HttpMessageHandler>();
137+
138+
var httpClient = new HttpClient(mockMessageHandler.Object);
139+
140+
var transport = new HttpApiTransport(
141+
httpClient,
142+
HttpContentType.Json,
143+
suppressClientDisposal: true);
144+
145+
// Act
146+
transport.Dispose();
147+
148+
// Assert
149+
mockMessageHandler.Protected().Verify("Dispose", Times.Never(), true, true);
150+
}
107151
}
108152
}

arangodb-net-standard.Test/Transport/Http/HttpApiTransportTestFixture.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44

55
namespace ArangoDBNetStandardTest.Transport.Http
66
{
7-
public class HttpApiTransportTestFixture: ApiClientTestFixtureBase
7+
public class HttpApiTransportTestFixture : ApiClientTestFixtureBase
88
{
9+
public string DatabaseName { get; } = nameof(HttpApiTransportTest);
10+
911
public string Username { get; } = "xyzabc";
1012
public string Password { get; } = "abcxyz";
1113

1214
public override async Task InitializeAsync()
1315
{
1416
await base.InitializeAsync();
1517
await CreateDatabase(
16-
nameof(HttpApiTransportTest),
18+
DatabaseName,
1719
new List<DatabaseUser>
1820
{
1921
new DatabaseUser

arangodb-net-standard/ArangoDBClient.cs

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Net.Http;
2-
using ArangoDBNetStandard.AdminApi;
1+
using ArangoDBNetStandard.AdminApi;
32
using ArangoDBNetStandard.AnalyzerApi;
43
using ArangoDBNetStandard.AqlFunctionApi;
54
using ArangoDBNetStandard.AuthApi;
@@ -17,6 +16,7 @@
1716
using ArangoDBNetStandard.Transport.Http;
1817
using ArangoDBNetStandard.UserApi;
1918
using ArangoDBNetStandard.ViewApi;
19+
using System.Net.Http;
2020

2121
namespace ArangoDBNetStandard
2222
{
@@ -25,6 +25,8 @@ namespace ArangoDBNetStandard
2525
/// </summary>
2626
public class ArangoDBClient : IArangoDBClient
2727
{
28+
protected readonly bool _suppressTransportDisposal;
29+
2830
/// <summary>
2931
/// The transport client used to communicate with the ArangoDB host.
3032
/// </summary>
@@ -79,7 +81,7 @@ public class ArangoDBClient : IArangoDBClient
7981
/// Index management API
8082
/// </summary>
8183
public IIndexApiClient Index { get; private set; }
82-
84+
8385
/// <summary>
8486
/// Bulk Operations API.
8587
/// </summary>
@@ -89,12 +91,12 @@ public class ArangoDBClient : IArangoDBClient
8991
/// View management API.
9092
/// </summary>
9193
public IViewApiClient View { get; private set; }
92-
94+
9395
/// <summary>
9496
/// Analyzer management API.
9597
/// </summary>
96-
public IAnalyzerApiClient Analyzer { get; private set; }
97-
98+
public IAnalyzerApiClient Analyzer { get; private set; }
99+
98100
/// <summary>
99101
/// Admin management API
100102
/// </summary>
@@ -110,9 +112,18 @@ public class ArangoDBClient : IArangoDBClient
110112
/// <see cref="HttpClient"/> instance, using the default JSON serialization.
111113
/// </summary>
112114
/// <param name="client"></param>
113-
public ArangoDBClient(HttpClient client)
115+
/// <param name="suppressClientDisposal">
116+
/// True to prevent disposal of the provided <see cref="HttpClient"/> instance
117+
/// when <see cref="ArangoDBClient"/> is disposed.
118+
/// Default is false, to avoid a breaking change.
119+
/// </param>
120+
public ArangoDBClient(HttpClient client, bool suppressClientDisposal = false)
114121
{
115-
_transport = new HttpApiTransport(client, HttpContentType.Json);
122+
_transport = new HttpApiTransport(
123+
client,
124+
HttpContentType.Json,
125+
suppressClientDisposal);
126+
_suppressTransportDisposal = false;
116127

117128
var serialization = new JsonNetApiClientSerialization();
118129

@@ -124,9 +135,17 @@ public ArangoDBClient(HttpClient client)
124135
/// using the provided transport layer and the default JSON serialization.
125136
/// </summary>
126137
/// <param name="transport">The ArangoDB transport layer implementation.</param>
127-
public ArangoDBClient(IApiClientTransport transport)
138+
/// <param name="suppressTransportDisposal">
139+
/// True to prevent disposal of the provided <see cref="IApiClientTransport"/> instance
140+
/// when <see cref="ArangoDBClient"/> is disposed.
141+
/// Default is false, to avoid a breaking change.
142+
/// </param>
143+
public ArangoDBClient(
144+
IApiClientTransport transport,
145+
bool suppressTransportDisposal = false)
128146
{
129147
_transport = transport;
148+
_suppressTransportDisposal = suppressTransportDisposal;
130149

131150
var serialization = new JsonNetApiClientSerialization();
132151

@@ -139,9 +158,18 @@ public ArangoDBClient(IApiClientTransport transport)
139158
/// </summary>
140159
/// <param name="transport">The ArangoDB transport layer implementation.</param>
141160
/// <param name="serialization">The serialization layer implementation.</param>
142-
public ArangoDBClient(IApiClientTransport transport, IApiClientSerialization serialization)
161+
/// <param name="suppressTransportDisposal">
162+
/// True to prevent disposal of the provided <see cref="IApiClientTransport"/> instance
163+
/// when <see cref="ArangoDBClient"/> is disposed.
164+
/// Default is false, to avoid a breaking change.
165+
/// </param>
166+
public ArangoDBClient(
167+
IApiClientTransport transport,
168+
IApiClientSerialization serialization,
169+
bool suppressTransportDisposal = false)
143170
{
144171
_transport = transport;
172+
_suppressTransportDisposal = suppressTransportDisposal;
145173

146174
InitializeApis(_transport, serialization);
147175
}
@@ -151,6 +179,10 @@ public ArangoDBClient(IApiClientTransport transport, IApiClientSerialization ser
151179
/// </summary>
152180
public void Dispose()
153181
{
182+
if (_suppressTransportDisposal)
183+
{
184+
return;
185+
}
154186
_transport.Dispose();
155187
}
156188

@@ -168,9 +200,9 @@ private void InitializeApis(
168200
Graph = new GraphApiClient(transport, serialization);
169201
User = new UserApiClient(transport, serialization);
170202
Index = new IndexApiClient(transport, serialization);
171-
BulkOperations = new BulkOperationsApiClient(transport, serialization);
203+
BulkOperations = new BulkOperationsApiClient(transport, serialization);
172204
View = new ViewApiClient(transport, serialization);
173-
Analyzer = new AnalyzerApiClient(transport, serialization);
205+
Analyzer = new AnalyzerApiClient(transport, serialization);
174206
Admin = new AdminApiClient(transport, serialization);
175207
Pregel = new PregelApiClient(transport, serialization);
176208
}

0 commit comments

Comments
 (0)