Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e3762db
test: initial integration test set DRAFT.
rlabbeptc Jun 26, 2025
74eb17a
test: updated tests and cleanup
rlabbeptc Jun 27, 2025
6cd773d
test: removed tests not associated with integration tests
rlabbeptc Jun 27, 2025
6cdcf7c
test: updated tests and cleanup
rlabbeptc Jun 27, 2025
bd9e096
test: updated tests and cleanup
rlabbeptc Jun 27, 2025
9e6d37f
test: updated tests and cleanup
rlabbeptc Jun 27, 2025
0fa6c8c
Merge branch 'main' into add-integration-test-config
rlabbeptc Jun 30, 2025
f1e81b1
Merge branch 'main' into add-integration-test-config
rlabbeptc Jun 30, 2025
5034b59
test: updated tests and cleanup
rlabbeptc Jun 30, 2025
42750f0
test: Updated to check productId instead of name.
rlabbeptc Jun 30, 2025
32e3e29
Fix(test): Added Test Connection check prior to connection being used…
rlabbeptc Jun 30, 2025
2b2ce29
chore(test): Added TODO comment for future tests.
rlabbeptc Jun 30, 2025
0175bb3
test: updated tests and cleanup
rlabbeptc Jul 3, 2025
ca8876d
chore(test): Added TODO comment for future tests.
rlabbeptc Jul 3, 2025
0abae40
test: updated tests and cleanup
rlabbeptc Jul 3, 2025
732a4fb
Merge branch 'main' into add-integration-test-config
rlabbeptc Jul 3, 2025
38caa66
test: updated tests and cleanup
rlabbeptc Jul 3, 2025
dcb39b4
test: Added to disable paralleliatin for testing. Impacts integration…
rlabbeptc Jul 3, 2025
47e58f5
test: Include integration testing to visibility
rlabbeptc Jul 3, 2025
d755dc3
test: Removed unit test templates not relevent to integration tests
rlabbeptc Jul 3, 2025
8d065e2
test: updated default appsettings for connection parameters
rlabbeptc Jul 3, 2025
8bc2079
test(workflow): updated github workflow to only execute unit tests.
rlabbeptc Jul 3, 2025
a8059d1
test: Updated with integration testing
rlabbeptc Jul 3, 2025
dc69e6a
chore(test): Removed deleted references
rlabbeptc Jul 3, 2025
cdd4dbf
fix(workflow): fixed test action to point to unit test project
rlabbeptc Jul 3, 2025
2150618
fix(workflow): fixed case sensitive reference
rlabbeptc Jul 3, 2025
5932909
chore(test): removed unused
rlabbeptc Jul 3, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
- name: Build
run: dotnet build --no-restore --configuration release
- name: Test
run: dotnet test --no-build --verbosity normal --configuration release --logger "trx;LogFileName=${{ matrix.platform }}-test-results.trx"
run: dotnet test Kepware.Api.Test/Kepware.Api.Test.csproj --no-build --verbosity normal --configuration Release --logger "trx;LogFileName=${{ matrix.platform }}-test-results.trx"
- name: Publish Test Report (${{ matrix.platform }})
if: ${{ github.event_name == 'pull_request' }}
uses: dorny/test-reporter@v1
Expand Down
6 changes: 6 additions & 0 deletions Kepware-ConfigAPI-SDK-dotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docfx", "docfx", "{EF3D2F75
docs\docfx\toc.yml = docs\docfx\toc.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kepware.Api.TestIntg", "Kepware.Api.TestIntg\Kepware.Api.TestIntg.csproj", "{5FF0BFA8-5429-4823-9B18-05EB2E6779AE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{457CB96C-D60B-4994-B87C-BCDBF4035D58}"
ProjectSection(SolutionItems) = preProject
docs\docfx\docs\toc.yml = docs\docfx\docs\toc.yml
Expand Down Expand Up @@ -77,6 +79,10 @@ Global
{E5F9DE9E-F9F2-4683-A7A9-ACE8501F7566}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5F9DE9E-F9F2-4683-A7A9-ACE8501F7566}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5F9DE9E-F9F2-4683-A7A9-ACE8501F7566}.Release|Any CPU.Build.0 = Release|Any CPU
{5FF0BFA8-5429-4823-9B18-05EB2E6779AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5FF0BFA8-5429-4823-9B18-05EB2E6779AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5FF0BFA8-5429-4823-9B18-05EB2E6779AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5FF0BFA8-5429-4823-9B18-05EB2E6779AE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
1 change: 1 addition & 0 deletions Kepware.Api.Test/ApiClient/GetProductInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public async Task GetProductInfoAsync_ShouldReturnProductInfo_WhenApiRespondsSuc

#region GetProductInfoAsync - SupportsJsonProjectLoadService

//TODO: Add more test cases for TKS versions as well. Different product name
[Theory]
[InlineData("KEPServerEX", "12", 6, 17, true)] // Supports JSON Project Load Service (6.17+)
[InlineData("KEPServerEX", "12", 6, 16, false)] // Does not support it (6.16)
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 @@ -196,7 +196,7 @@ public async Task LoadEntityAsync_ShouldReturnNull_OnHttpRequestException()
#endregion

#region LoadEntityAsync - Device with DynamicProperties

// TODO: Add these types of test for other entities (Device, TagGroup, Tag, etc.)
[Fact]
public async Task LoadEntityAsync_ShouldReturnDevice_WithCorrectDynamicProperties()
{
Expand Down
108 changes: 108 additions & 0 deletions Kepware.Api.TestIntg/ApiClient/AdminModelTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using Kepware.Api.Model;
using Kepware.Api.Serializer;
using Microsoft.Extensions.Logging;
using Moq;
using Moq.Contrib.HttpClient;
using Shouldly;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using Xunit;
using System.Net.Http.Json;
using Kepware.Api.Model.Admin;

namespace Kepware.Api.TestIntg.ApiClient
{
public class AdminModelTests : TestApiClientBase
{

#region GetAdminSettingsAsync Tests

[Fact]
public async Task GetAdminSettingsAsync_ShouldReturnAdminSettings_WhenApiRespondsSuccessfully()
{

// Act
var result = await _kepwareApiClient.Admin.GetAdminSettingsAsync();

// Assert
result.ShouldNotBeNull();
result.EventLogConnectionPort.ShouldNotBe(default(int));
result.EventLogMaxRecords.ShouldNotBe(default(int));
if (_productInfo.ProductId == "013")
{
result.LicenseServer.ShouldNotBeNull();
result.LicenseServer.RecheckIntervalMinutes.ShouldNotBe(default(int));
result.LicenseServer.Enable.ShouldNotBeNull();
}

}

[Fact]
public async Task GetAdminSettingsAsync_ShouldReturnNull_WhenApiReturnsUnauthorized()
{

// Act
var result = await _badCredKepwareApiClient.Admin.GetAdminSettingsAsync();

// Assert
result.ShouldBeNull();

}

#endregion

#region SetAdminSettingsAsync Tests

[Fact]
public async Task SetAdminSettingsAsync_ShouldReturnTrue_WhenUpdateSuccessful()
{
// Arrange
var newSettings = new AdminSettings();
newSettings.EventLogMaxRecords = new Random().Next(10000, 30001);
if (_productInfo.ProductId != "013")
{
newSettings.LicenseServer.Port = new Random().Next(10000, 30001);
}

// Act
var result = await _kepwareApiClient.Admin.SetAdminSettingsAsync(newSettings);

// Assert
result.ShouldBeTrue();

}

[Fact]
public async Task SetAdminSettingsAsync_ShouldReturnFalse_WhenUpdateFails()
{
// Arrange
var newSettings = new AdminSettings();
newSettings.EventLogConnectionPort = 1000;


// Act
var result = await _kepwareApiClient.Admin.SetAdminSettingsAsync(newSettings);

// Assert
result.ShouldBeFalse();
}

[Fact]
public async Task SetAdminSettingsAsync_ShouldThrowException_WhenApiReturnsUnauthorized()
{
// Arrange
var newSettings = new AdminSettings();
newSettings.EventLogMaxRecords = new Random().Next(10000, 30001);

// Act and Assert
await Should.ThrowAsync<InvalidOperationException>(async () =>
{
await _badCredKepwareApiClient.Admin.SetAdminSettingsAsync(newSettings);
});

}

#endregion
}
}
147 changes: 147 additions & 0 deletions Kepware.Api.TestIntg/ApiClient/AutomaticTagGenerationAsyncTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using Kepware.Api.Model;
using Kepware.Api.Model.Services;
using Moq;
using Moq.Contrib.HttpClient;
using Shouldly;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
using Xunit.Extensions.Ordering;

namespace Kepware.Api.TestIntg.ApiClient
{
public class AutomaticTagGenerationAsyncTests : TestApiClientBase
{
private const string UNIT_TEST_CHANNEL = "unitTestChannel";
private const string UNIT_TEST_DEVICE = "unitTestDevice";
private const string ENDPOINT_TAG_GENERATION = $"/config/v1/project/channels/{UNIT_TEST_CHANNEL}/devices/{UNIT_TEST_DEVICE}/services/TagGeneration";
private const string JOB_ENDPOINT = $"/config/v1/project/channels/{UNIT_TEST_CHANNEL}/devices/{UNIT_TEST_DEVICE}/services/TagGeneration/jobs/job123";

[Fact]
public async Task AutomaticTagGenerationAsync_ShouldReturnKepServerJobPromise_WhenApiResponseIsInvalid()
{
// Arrange
// Create channel that doesn't support ATG
var channel = await AddTestChannel();
var device = await AddTestDevice(channel);

// Act
var result = await _kepwareApiClient.ApiServices.AutomaticTagGenerationAsync(channel.Name, device.Name, TimeSpan.FromSeconds(30));
// Assert
result.ShouldNotBeNull();
//result.Endpoint.ShouldBe(ENDPOINT_TAG_GENERATION);
result.JobTimeToLive.ShouldBe(TimeSpan.FromSeconds(30));

// Clean up
await DeleteAllChannelsAsync();
}

// [Fact]
// public async Task AutomaticTagGenerationAsync_ShouldThrowException_WhenHttpClientThrowsException()
// {
// // Arrange
// _httpMessageHandlerMock.SetupRequest(HttpMethod.Put, $"{TEST_ENDPOINT}{ENDPOINT_TAG_GENERATION}")
// .Throws(new HttpRequestException("Network error"));

// // Act & Assert
// await Should.ThrowAsync<HttpRequestException>(async () =>
// {
// await _kepwareApiClient.ApiServices.AutomaticTagGenerationAsync(UNIT_TEST_CHANNEL, UNIT_TEST_DEVICE, TimeSpan.FromSeconds(30));
// });
// }

// [Fact]
// public async Task AutomaticTagGenerationAsync_ShouldReturnSuccess_WhenJobCompletesSuccessfullyAfterFirstGet()
// {
// // Arrange
// var jobResponse = new JobResponseMessage { ResponseStatusCode = (int)ApiResponseCode.Accepted, JobId = JOB_ENDPOINT };
// var jobStatus = new JobStatusMessage { Completed = true };
// _httpMessageHandlerMock.SetupRequest(HttpMethod.Put, $"{TEST_ENDPOINT}{ENDPOINT_TAG_GENERATION}")
// .ReturnsResponse(HttpStatusCode.Accepted, JsonSerializer.Serialize(jobResponse), "application/json");
// _httpMessageHandlerMock.SetupRequest(HttpMethod.Get, $"{TEST_ENDPOINT}{JOB_ENDPOINT}")
// .ReturnsResponse(HttpStatusCode.OK, JsonSerializer.Serialize(jobStatus), "application/json");

// // Act
// var result = await _kepwareApiClient.ApiServices.AutomaticTagGenerationAsync(UNIT_TEST_CHANNEL, UNIT_TEST_DEVICE, TimeSpan.FromSeconds(30));
// var completionResult = await result.AwaitCompletionAsync();

// // Assert
// completionResult.Value.ShouldBeTrue();
// completionResult.IsSuccess.ShouldBeTrue();
// }

[Fact]
public async Task AutomaticTagGenerationAsync_ShouldReturnSuccess_WhenJobCompletesSuccessfully()
{
// Arrange
var channel = await AddTestChannel(driver: "Allen-Bradley ControlLogix Ethernet");
var device = await AddAtgTestDevice(channel);

// Act
var result = await _kepwareApiClient.ApiServices.AutomaticTagGenerationAsync(channel.Name, device.Name, TimeSpan.FromSeconds(30));

// Assert
result.ShouldNotBeNull();
result.JobTimeToLive.ShouldBe(TimeSpan.FromSeconds(30));

// Wait for job completion
var completionResult = await result.AwaitCompletionAsync();

// Assert
completionResult.Value.ShouldBeTrue();
completionResult.IsSuccess.ShouldBeTrue();

// Clean up
await DeleteAllChannelsAsync();
}

// [Fact]
// public async Task AutomaticTagGenerationAsync_ShouldReturnFailure_WhenJobFailsAfterFirstGet()
// {
// // Arrange
// var jobResponse = new JobResponseMessage { ResponseStatusCode = (int)ApiResponseCode.Accepted, JobId = JOB_ENDPOINT };
// var jobStatus = new JobStatusMessage { Completed = false };
// _httpMessageHandlerMock.SetupRequest(HttpMethod.Put, $"{TEST_ENDPOINT}{ENDPOINT_TAG_GENERATION}")
// .ReturnsResponse(HttpStatusCode.Accepted, JsonSerializer.Serialize(jobResponse), "application/json");
// _httpMessageHandlerMock.SetupRequest(HttpMethod.Get, $"{TEST_ENDPOINT}{JOB_ENDPOINT}")
// .ReturnsResponse(HttpStatusCode.OK, JsonSerializer.Serialize(jobStatus), "application/json");

// // Act
// var result = await _kepwareApiClient.ApiServices.AutomaticTagGenerationAsync(UNIT_TEST_CHANNEL, UNIT_TEST_DEVICE, TimeSpan.FromSeconds(1));
// var completionResult = await result.AwaitCompletionAsync(TimeSpan.FromMilliseconds(100));

// // Assert
// completionResult.Value.ShouldBeFalse();
// completionResult.IsSuccess.ShouldBeFalse();
// }

// [Fact]
// public async Task AutomaticTagGenerationAsync_ShouldReturnFailure_WhenJobFailsAfterMultipleGets()
// {
// // Arrange
// var jobResponse = new JobResponseMessage { ResponseStatusCode = (int)ApiResponseCode.Accepted, JobId = JOB_ENDPOINT };
// var jobStatusIncomplete = new JobStatusMessage { Completed = false };

// var jobStatusFailed = new JobStatusMessage { Completed = true, Message = "Job failed" };

// _httpMessageHandlerMock.SetupRequest(HttpMethod.Put, $"{TEST_ENDPOINT}{ENDPOINT_TAG_GENERATION}")
// .ReturnsResponse(HttpStatusCode.Accepted, JsonSerializer.Serialize(jobResponse), "application/json");
// _httpMessageHandlerMock.SetupSequenceRequest(HttpMethod.Get, $"{TEST_ENDPOINT}{JOB_ENDPOINT}")
// .ReturnsResponse(HttpStatusCode.OK, JsonSerializer.Serialize(jobStatusIncomplete), "application/json")
// .ReturnsResponse(HttpStatusCode.OK, JsonSerializer.Serialize(jobStatusIncomplete), "application/json")
// .ReturnsResponse(HttpStatusCode.ServiceUnavailable, JsonSerializer.Serialize(jobStatusFailed), "application/json");

// // Act
// var result = await _kepwareApiClient.ApiServices.AutomaticTagGenerationAsync(UNIT_TEST_CHANNEL, UNIT_TEST_DEVICE, TimeSpan.FromSeconds(5));
// var completionResult = await result.AwaitCompletionAsync(TimeSpan.FromMilliseconds(100));

// // Assert
// completionResult.Value.ShouldBeFalse();
// completionResult.IsSuccess.ShouldBeFalse();
// }
}
}
36 changes: 36 additions & 0 deletions Kepware.Api.TestIntg/ApiClient/GetProductInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Kepware.Api.Model;
using Moq;
using Moq.Contrib.HttpClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Kepware.Api.TestIntg.ApiClient
{
public class GetProductInfo : TestApiClientBase
{

[Fact]
public async Task GetProductInfoAsync_ShouldReturnProductInfo_WhenApiRespondsSuccessfully()
{

// Act
var result = await _kepwareApiClient.GetProductInfoAsync();

// Assert
Assert.NotNull(result);
Assert.Equal(_productInfo.ProductId, result.ProductId);
Assert.Equal(_productInfo.ProductName, result.ProductName);
Assert.Equal(_productInfo.ProductVersion, result.ProductVersion);
Assert.Equal(_productInfo.ProductVersionMajor, result.ProductVersionMajor);
Assert.Equal(_productInfo.ProductVersionMinor, result.ProductVersionMinor);
Assert.Equal(_productInfo.ProductVersionBuild, result.ProductVersionBuild);
Assert.Equal(_productInfo.ProductVersionPatch, result.ProductVersionPatch);

}

}
}
Loading
Loading