Skip to content

Commit 43b5a26

Browse files
authored
Merge pull request #57 from PTCInc/dev-1.0
API Fixes and Test Updates and Refactors
2 parents e18607d + 10c9654 commit 43b5a26

27 files changed

Lines changed: 981 additions & 253 deletions

.github/workflows/nuget-test-and-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ jobs:
9191
publish:
9292
# Publish the NuGet package and create release assets
9393
runs-on: ubuntu-latest
94+
if: ${{ github.event_name != 'pull_request' }}
9495
needs: [ build, draft-release ]
9596
env:
9697
tag_name: ${{ needs.draft-release.outputs.tag_name }}
Lines changed: 385 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
1+
using Kepware.Api.Model;
2+
using Microsoft.Extensions.Logging;
3+
using Moq;
4+
using Moq.Contrib.HttpClient;
5+
using Shouldly;
6+
using System.Net;
7+
8+
namespace Kepware.Api.Test.ApiClient;
9+
10+
public class DeleteTests : TestApiClientBase
11+
{
12+
[Fact]
13+
public async Task Delete_ItemByNamedEntity_WhenSuccessful_ShouldReturnTrue()
14+
{
15+
// Arrange
16+
var channel = new Channel { Name = "TestChannel" };
17+
var endpoint = "/config/v1/project/channels/TestChannel";
18+
19+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
20+
.ReturnsResponse(HttpStatusCode.OK);
21+
22+
// Act
23+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync(channel);
24+
25+
// Assert
26+
result.ShouldBeTrue();
27+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
28+
}
29+
30+
[Fact]
31+
public async Task Delete_ItemByNamedEntity_WithHttpError_ShouldReturnFalse()
32+
{
33+
// Arrange
34+
var channel = new Channel { Name = "TestChannel" };
35+
var endpoint = "/config/v1/project/channels/TestChannel";
36+
37+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
38+
.ReturnsResponse(HttpStatusCode.InternalServerError, "Server Error");
39+
40+
// Act
41+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync(channel);
42+
43+
// Assert
44+
result.ShouldBeFalse();
45+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
46+
_loggerMockGeneric.Verify(logger =>
47+
logger.Log(
48+
LogLevel.Error,
49+
It.IsAny<EventId>(),
50+
It.Is<It.IsAnyType>((v, t) => true),
51+
It.IsAny<Exception>(),
52+
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
53+
Times.Once);
54+
}
55+
56+
[Fact]
57+
public async Task Delete_ItemByNamedEntity_WithConnectionError_ShouldReturnFalse()
58+
{
59+
// Arrange
60+
var channel = new Channel { Name = "TestChannel" };
61+
var endpoint = "/config/v1/project/channels/TestChannel";
62+
63+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
64+
.Throws(new HttpRequestException("Connection error"));
65+
66+
// Act
67+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync(channel);
68+
69+
// Assert
70+
result.ShouldBeFalse();
71+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
72+
_loggerMockGeneric.Verify(logger =>
73+
logger.Log(
74+
LogLevel.Warning,
75+
It.IsAny<EventId>(),
76+
It.Is<It.IsAnyType>((v, t) => true),
77+
It.IsAny<HttpRequestException>(),
78+
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
79+
Times.Once);
80+
}
81+
82+
[Fact]
83+
public async Task Delete_ItemByName_WhenSuccessful_ShouldReturnTrue()
84+
{
85+
// Arrange
86+
var endpoint = "/config/v1/project/channels/TestChannel";
87+
88+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
89+
.ReturnsResponse(HttpStatusCode.OK);
90+
91+
// Act
92+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync<Channel>("TestChannel");
93+
94+
// Assert
95+
result.ShouldBeTrue();
96+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
97+
}
98+
99+
[Fact]
100+
public async Task Delete_ItemByName_WithHttpError_ShouldReturnFalse()
101+
{
102+
// Arrange
103+
var endpoint = "/config/v1/project/channels/TestChannel";
104+
105+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
106+
.ReturnsResponse(HttpStatusCode.NotFound, "Not Found");
107+
108+
// Act
109+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync<Channel>("TestChannel");
110+
111+
// Assert
112+
result.ShouldBeFalse();
113+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
114+
_loggerMockGeneric.Verify(logger =>
115+
logger.Log(
116+
LogLevel.Error,
117+
It.IsAny<EventId>(),
118+
It.Is<It.IsAnyType>((v, t) => true),
119+
It.IsAny<Exception>(),
120+
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
121+
Times.Once);
122+
}
123+
124+
[Fact]
125+
public async Task Delete_ItemByMultipleNames_WhenSuccessful_ShouldReturnTrue()
126+
{
127+
// Arrange
128+
var itemNames = new[] { "Channel1", "Device1" };
129+
var endpoint = "/config/v1/project/channels/Channel1/devices/Device1";
130+
131+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
132+
.ReturnsResponse(HttpStatusCode.OK);
133+
134+
// Act
135+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync<Device>(itemNames);
136+
137+
// Assert
138+
result.ShouldBeTrue();
139+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
140+
}
141+
142+
[Fact]
143+
public async Task Delete_ItemByMultipleNames_WithHttpError_ShouldReturnFalse()
144+
{
145+
// Arrange
146+
var itemNames = new[] { "Channel1", "Device1" };
147+
var endpoint = "/config/v1/project/channels/Channel1/devices/Device1";
148+
149+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
150+
.ReturnsResponse(HttpStatusCode.InternalServerError, "Server Error");
151+
152+
// Act
153+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync<Device>(itemNames);
154+
155+
// Assert
156+
result.ShouldBeFalse();
157+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
158+
_loggerMockGeneric.Verify(logger =>
159+
logger.Log(
160+
LogLevel.Error,
161+
It.IsAny<EventId>(),
162+
It.Is<It.IsAnyType>((v, t) => true),
163+
It.IsAny<Exception>(),
164+
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
165+
Times.Once);
166+
}
167+
168+
[Fact]
169+
public async Task Delete_Item_WhenSuccessful_ShouldReturnTrue()
170+
{
171+
// Arrange
172+
var channel = new Channel { Name = "TestChannel" };
173+
var endpoint = "/config/v1/project/channels/TestChannel";
174+
175+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
176+
.ReturnsResponse(HttpStatusCode.OK);
177+
178+
// Act
179+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync<ChannelCollection, Channel>(channel);
180+
181+
// Assert
182+
result.ShouldBeTrue();
183+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
184+
}
185+
186+
[Fact]
187+
public async Task Delete_Item_WithHttpError_ShouldLogError()
188+
{
189+
// Arrange
190+
var channel = new Channel { Name = "TestChannel" };
191+
var endpoint = "/config/v1/project/channels/TestChannel";
192+
193+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
194+
.ReturnsResponse(HttpStatusCode.InternalServerError, "Server Error");
195+
196+
// Act
197+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync<ChannelCollection, Channel>(channel);
198+
199+
// Assert
200+
result.ShouldBeFalse();
201+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
202+
_loggerMockGeneric.Verify(logger =>
203+
logger.Log(
204+
LogLevel.Error,
205+
It.IsAny<EventId>(),
206+
It.Is<It.IsAnyType>((v, t) => true),
207+
It.IsAny<Exception>(),
208+
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
209+
Times.Once);
210+
}
211+
212+
[Fact]
213+
public async Task Delete_Item_WithConnectionError_ShouldHandleGracefully()
214+
{
215+
// Arrange
216+
var channel = new Channel { Name = "TestChannel" };
217+
var endpoint = "/config/v1/project/channels/TestChannel";
218+
219+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
220+
.Throws(new HttpRequestException("Connection error"));
221+
222+
// Act
223+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync<ChannelCollection, Channel>(channel);
224+
225+
// Assert
226+
result.ShouldBeFalse();
227+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
228+
_loggerMockGeneric.Verify(logger =>
229+
logger.Log(
230+
LogLevel.Warning,
231+
It.IsAny<EventId>(),
232+
It.Is<It.IsAnyType>((v, t) => true),
233+
It.IsAny<HttpRequestException>(),
234+
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
235+
Times.Once);
236+
}
237+
238+
[Fact]
239+
public async Task Delete_Item_WithOwner_WhenSuccessful_ShouldDeleteAll()
240+
{
241+
// Arrange
242+
var channel = new Channel { Name = "TestChannel" };
243+
var device = new Device { Name = "ParentDevice", Owner = channel };
244+
var tag = new Tag { Name = "Tag1", Owner = device };
245+
var endpoint = $"/config/v1/project/channels/{channel.Name}/devices/{device.Name}/tags/{tag.Name}";
246+
247+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
248+
.ReturnsResponse(HttpStatusCode.OK);
249+
250+
// Act
251+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync<DeviceTagCollection, Tag>(tag, owner: device);
252+
253+
// Assert
254+
result.ShouldBeTrue();
255+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
256+
}
257+
258+
[Fact]
259+
public async Task Delete_Item_WithOwner_WithHttpError_ShouldLogError()
260+
{
261+
// Arrange
262+
var channel = new Channel { Name = "TestChannel" };
263+
var device = new Device { Name = "ParentDevice", Owner = channel };
264+
var tag = new Tag { Name = "Tag1", Owner = device };
265+
var endpoint = $"/config/v1/project/channels/{channel.Name}/devices/{device.Name}/tags/{tag.Name}";
266+
267+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
268+
.ReturnsResponse(HttpStatusCode.NotFound, "Not Found");
269+
270+
// Act
271+
var result = await _kepwareApiClient.GenericConfig.DeleteItemAsync<DeviceTagCollection, Tag>(tag, owner: device);
272+
273+
// Assert
274+
result.ShouldBeFalse();
275+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
276+
_loggerMockGeneric.Verify(logger =>
277+
logger.Log(
278+
LogLevel.Error,
279+
It.IsAny<EventId>(),
280+
It.Is<It.IsAnyType>((v, t) => true),
281+
It.IsAny<Exception>(),
282+
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
283+
Times.Once);
284+
}
285+
286+
[Fact]
287+
public async Task Delete_MultipleItems_WhenSuccessful_ShouldDeleteAll()
288+
{
289+
// Arrange
290+
var channel = new Channel { Name = "TestChannel" };
291+
var device = new Device { Name = "ParentDevice", Owner = channel };
292+
var tags = CreateTestTags();
293+
294+
foreach (var tag in tags)
295+
{
296+
var endpoint = $"/config/v1/project/channels/{channel.Name}/devices/{device.Name}/tags/{tag.Name}";
297+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
298+
.ReturnsResponse(HttpStatusCode.OK);
299+
}
300+
301+
// Act
302+
var result = await _kepwareApiClient.GenericConfig.DeleteItemsAsync<DeviceTagCollection, Tag>(tags, owner: device);
303+
304+
// Assert
305+
result.ShouldBeTrue();
306+
foreach (var tag in tags)
307+
{
308+
var endpoint = $"/config/v1/project/channels/{channel.Name}/devices/{device.Name}/tags/{tag.Name}";
309+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
310+
}
311+
}
312+
313+
[Fact]
314+
public async Task Delete_MultipleItems_WithHttpError_ShouldLogError()
315+
{
316+
// Arrange
317+
var channel = new Channel { Name = "TestChannel" };
318+
var device = new Device { Name = "ParentDevice", Owner = channel };
319+
var tags = CreateTestTags(1);
320+
var endpoint = $"/config/v1/project/channels/{channel.Name}/devices/{device.Name}/tags/{tags[0].Name}";
321+
322+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
323+
.ReturnsResponse(HttpStatusCode.InternalServerError, "Server Error");
324+
325+
// Act
326+
var result = await _kepwareApiClient.GenericConfig.DeleteItemsAsync<DeviceTagCollection, Tag>(tags, owner: device);
327+
328+
// Assert
329+
result.ShouldBeFalse();
330+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
331+
_loggerMockGeneric.Verify(logger =>
332+
logger.Log(
333+
LogLevel.Error,
334+
It.IsAny<EventId>(),
335+
It.Is<It.IsAnyType>((v, t) => true),
336+
It.IsAny<Exception>(),
337+
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
338+
Times.Once);
339+
}
340+
341+
[Fact]
342+
public async Task Delete_MultipleItems_WithConnectionError_ShouldHandleGracefully()
343+
{
344+
// Arrange
345+
var channel = new Channel { Name = "TestChannel" };
346+
var device = new Device { Name = "ParentDevice", Owner = channel };
347+
var tags = CreateTestTags(1);
348+
var endpoint = $"/config/v1/project/channels/{channel.Name}/devices/{device.Name}/tags/{tags[0].Name}";
349+
350+
_httpMessageHandlerMock.SetupRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}")
351+
.Throws(new HttpRequestException("Connection error"));
352+
353+
// Act
354+
var result = await _kepwareApiClient.GenericConfig.DeleteItemsAsync<DeviceTagCollection, Tag>(tags, owner: device);
355+
356+
// Assert
357+
result.ShouldBeFalse();
358+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Once());
359+
_loggerMockGeneric.Verify(logger =>
360+
logger.Log(
361+
LogLevel.Warning,
362+
It.IsAny<EventId>(),
363+
It.Is<It.IsAnyType>((v, t) => true),
364+
It.IsAny<HttpRequestException>(),
365+
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
366+
Times.Once);
367+
}
368+
369+
[Fact]
370+
public async Task Delete_MultipleItems_WithEmptyList_ShouldNoop()
371+
{
372+
// Arrange
373+
var channel = new Channel { Name = "TestChannel" };
374+
var device = new Device { Name = "ParentDevice", Owner = channel };
375+
var tags = new List<Tag>();
376+
var endpoint = $"/config/v1/project/channels/{channel.Name}/devices/{device.Name}/tags/Tag1";
377+
378+
// Act
379+
var result = await _kepwareApiClient.GenericConfig.DeleteItemsAsync<DeviceTagCollection, Tag>(tags, owner: device);
380+
381+
// Assert
382+
result.ShouldBeTrue();
383+
_httpMessageHandlerMock.VerifyRequest(HttpMethod.Delete, $"{TEST_ENDPOINT}{endpoint}", Times.Never());
384+
}
385+
}

0 commit comments

Comments
 (0)