Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions Kepware.Api.Test/ApiClient/GenericHandleTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Kepware.Api.Model;
using Kepware.Api.Test.ApiClient;
using Microsoft.Extensions.Logging;
using Moq;
using Moq.Contrib.HttpClient;
using Xunit;

namespace Kepware.Api.Test.ApiClient
{
public class GenericHandler : TestApiClientBase
{
[Fact]
public void AppendQueryString_PrivateMethod_EncodesAndSkipsNullsAndAppendsCorrectly()
{
// Arrange
var method = typeof(Kepware.Api.ClientHandler.GenericApiHandler)
.GetMethod("AppendQueryString", BindingFlags.NonPublic | BindingFlags.Static);
Assert.NotNull(method);

var query = new[]
{
new KeyValuePair<string, string?>("a", "b"),
new KeyValuePair<string, string?>("space", "x y"),
new KeyValuePair<string, string?>("skip", null) // should be skipped
};

// Act
var result1 = (string)method!.Invoke(null, new object[] { "https://api/config", query })!;
var result2 = (string)method!.Invoke(null, new object[] { "https://api/config?existing=1", query })!;

// Assert
Assert.Equal("https://api/config?a=b&space=x%20y", result1);
Assert.Equal("https://api/config?existing=1&a=b&space=x%20y", result2);
}

[Fact]
public async Task LoadCollectionAsync_AppendsQueryAndReturnsCollection()
{
// Arrange
var channelsJson = """
[
{
"PROJECT_ID": 676550906,
"common.ALLTYPES_NAME": "Channel1",
"common.ALLTYPES_DESCRIPTION": "Example Simulator Channel",
"servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Simulator"
},
{
"PROJECT_ID": 676550906,
"common.ALLTYPES_NAME": "Data Type Examples",
"common.ALLTYPES_DESCRIPTION": "Example Simulator Channel",
"servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Simulator"
}
]
""";

var query = new[]
{
new KeyValuePair<string, string?>("status", "active"),
new KeyValuePair<string, string?>("name", "John Doe"),
new KeyValuePair<string, string?>("skip", null)
};

// Expect encoded space in "John Doe" and null entry skipped
_httpMessageHandlerMock.SetupRequest(HttpMethod.Get, TEST_ENDPOINT + "/config/v1/project/channels?status=active&name=John%20Doe")
.ReturnsResponse(channelsJson, "application/json");

// Act
var result = await _kepwareApiClient.GenericConfig.LoadCollectionAsync<ChannelCollection, Channel>((string?)null, query);

// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Count);
Assert.Contains(result, c => c.Name == "Channel1");
Assert.Contains(result, c => c.Name == "Data Type Examples");
}

[Fact]
public async Task LoadEntityAsync_AppendsQueryAndReturnsEntity()
{
// Arrange
var channelJson = """
{
"PROJECT_ID": 676550906,
"common.ALLTYPES_NAME": "Channel1",
"common.ALLTYPES_DESCRIPTION": "Example Simulator Channel",
"servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Simulator"
}
""";

var query = new[]
{
new KeyValuePair<string, string?>("status", "active"),
new KeyValuePair<string, string?>("name", "John Doe"),
new KeyValuePair<string, string?>("skip", null)
};

// Expect encoded space in "John Doe" and null entry skipped
_httpMessageHandlerMock.SetupRequest(HttpMethod.Get, TEST_ENDPOINT + "/config/v1/project/channels/Channel1?status=active&name=John%20Doe")
.ReturnsResponse(channelJson, "application/json");

// Act
var result = await _kepwareApiClient.GenericConfig.LoadEntityAsync<Channel>("Channel1", query);

// Assert
Assert.NotNull(result);
Assert.Equal("Channel1", result.Name);
Assert.Equal("Example Simulator Channel", result.Description);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ public async Task GetProductInfoAsync_ShouldReturnProductInfo_WhenApiRespondsSuc
Assert.Equal(240, result.ProductVersionBuild);
Assert.Equal(0, result.ProductVersionPatch);

// Also verify that the ProductInfo property on the client is populated correctly
Assert.NotNull(_kepwareApiClient.ProductInfo);
Assert.Equal("012", _kepwareApiClient.ProductInfo.ProductId);
Assert.Equal("KEPServerEX", _kepwareApiClient.ProductInfo.ProductName);
Assert.Equal("V6.17.240.0", _kepwareApiClient.ProductInfo.ProductVersion);
Assert.Equal(6, _kepwareApiClient.ProductInfo.ProductVersionMajor);
Assert.Equal(17, _kepwareApiClient.ProductInfo.ProductVersionMinor);
Assert.Equal(240, _kepwareApiClient.ProductInfo.ProductVersionBuild);
Assert.Equal(0, _kepwareApiClient.ProductInfo.ProductVersionPatch);

}

#region GetProductInfoAsync - SupportsJsonProjectLoadService
Expand All @@ -57,6 +67,10 @@ public async Task GetProductInfoAsync_ShouldReturnCorrect_SupportsJsonProjectLoa
// Assert
Assert.NotNull(result);
Assert.Equal(expectedResult, result.SupportsJsonProjectLoadService);

// Also verify that the ProductInfo property on the client is populated correctly
Assert.NotNull(_kepwareApiClient.ProductInfo);
Assert.Equal(expectedResult, _kepwareApiClient.ProductInfo.SupportsJsonProjectLoadService);
}

#endregion
Expand Down Expand Up @@ -99,6 +113,9 @@ public async Task GetProductInfoAsync_ShouldReturnNull_WhenApiReturnsError()

// Assert
Assert.Null(result);

// ProductInfo property should also be null on error
Assert.Null(_kepwareApiClient.ProductInfo);
}

[Fact]
Expand All @@ -114,6 +131,9 @@ public async Task GetProductInfoAsync_ShouldReturnNull_WhenApiReturnsInvalidJson

// Assert
Assert.Null(result);

// ProductInfo property should also be null on error
Assert.Null(_kepwareApiClient.ProductInfo);
}

[Fact]
Expand All @@ -128,6 +148,9 @@ public async Task GetProductInfoAsync_ShouldReturnNull_OnHttpRequestException()

// Assert
Assert.Null(result);

// ProductInfo property should also be null on error
Assert.Null(_kepwareApiClient.ProductInfo);
}
#endregion
}
Expand Down
2 changes: 1 addition & 1 deletion Kepware.Api.Test/ApiClient/LoadEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ public async Task LoadEntityAsync_ShouldThrowInvalidOperationException_WhenLoadR

#endregion

#region LoadEntityAsync - Single Tag mit DynamicProperties
#region LoadEntityAsync - Single Tag with DynamicProperties

[Fact]
public async Task LoadEntityAsync_ShouldReturnTag_WithCorrectDynamicProperties()
Expand Down
Loading
Loading