| name | nuclia-functional-test |
| description | This skill file provides guidelines for creating functional tests for the Progress Nuclia .NET SDK. |
Overview
Functional tests verify that the SDK can successfully interact with the real Nuclia API using actual credentials. These tests ensure end-to-end functionality of SDK methods.
Test Structure Pattern
1. File Naming Convention
- Name:
{MethodName}FunctionalTest.cs
- Example:
ListResourcesFunctionalTest.cs, CreateResourceFunctionalTest.cs
2. Class Documentation
Include a comprehensive XML summary that explains:
- What the test verifies
- What API/SDK functionality is being tested
- Expected results
Example:
3. Required Using Statements
using System;
using System.Threading.Tasks;
using Xunit;
using Progress.Nuclia;
using System.Collections.Generic;
4. Test Class Setup
Client Initialization
- Initialize
NucliaDbClient in the constructor
- Load credentials from environment variables:
NUCLIA_ZONE_ID
NUCLIA_KB_ID
NUCLIA_API_KEY
- Throw
InvalidOperationException if credentials are missing
private readonly NucliaDbClient _client;
public {TestClassName}()
{
var zoneId = Environment.GetEnvironmentVariable("NUCLIA_ZONE_ID");
var kbId = Environment.GetEnvironmentVariable("NUCLIA_KB_ID");
var apiKey = Environment.GetEnvironmentVariable("NUCLIA_API_KEY");
if (string.IsNullOrEmpty(zoneId) || string.IsNullOrEmpty(kbId) || string.IsNullOrEmpty(apiKey))
{
throw new InvalidOperationException("Missing Nuclia API credentials. Set NUCLIA_ZONE_ID, NUCLIA_KB_ID, NUCLIA_API_KEY.");
}
var config = new NucliaDbConfig(zoneId, kbId, apiKey);
_client = new NucliaDbClient(config);
}
5. Resource Cleanup (Teardown)
For tests that create resources (POST/PUT operations), implement IAsyncLifetime to ensure proper cleanup:
When to Use Cleanup
- Tests that create resources (CreateResourceAsync)
- Tests that modify Knowledge Box state
- Any test that leaves persistent data after execution
Cleanup Pattern
public class {TestClassName} : IAsyncLifetime
{
private readonly NucliaDbClient _client;
private readonly List<string> _createdResourceIds = new();
public Task InitializeAsync() => Task.CompletedTask;
public async Task DisposeAsync()
{
foreach (var resourceId in _createdResourceIds)
{
try
{
await _client.Resources.DeleteResourceByIdAsync(resourceId);
}
catch
{
}
}
}
}
Tracking Resources for Cleanup
After creating a resource, add its ID to the tracking list:
var createResponse = await _client.Resources.CreateResourceAsync(payload);
var resourceId = createResponse.Data.Uuid;
_createdResourceIds.Add(resourceId);
6. Test Method Pattern
Attributes
[Fact(DisplayName = "Descriptive test name")]
[Trait("Category", "Functional")]
Method Signature
public async Task {MethodDescription}()
- Use descriptive names starting with "Can" (e.g.,
CanListResources, CanCreateResource)
Documentation
7. Assertions Pattern
For read operations (GET/LIST):
var response = await _client.{Service}.{Method}Async();
Assert.NotNull(response);
Assert.True(response.Success, $"API call failed: {response.Error}");
Assert.NotNull(response.Data);
For write operations (POST/PUT/DELETE):
var response = await _client.{Service}.{Method}Async({parameters});
Assert.NotNull(response);
Assert.True(response.Success, $"API call failed: {response.Error}");
Assert.NotNull(response.Data);
Best Practices
- Keep tests focused: Each test should verify one specific SDK method or operation
- Avoid unnecessary setup: Only create test data if the test requires it
- Clean up test resources: Implement
IAsyncLifetime for tests that create resources
- Track created resources: Maintain a list of resource IDs for cleanup in teardown
- Use meaningful assertions: Validate both success and data structure
- Provide clear error messages: Include response.Error in failure messages
- Document expected behavior: Use XML comments to explain what should happen
- Use async/await: All API calls should be asynchronous
- Environment variables: Always load credentials from environment variables, never hardcode
- Ignore cleanup errors: Don't let cleanup failures cause test failures
Example Templates
Read-Only Functional Test
using System;
using System.Threading.Tasks;
using Xunit;
using Progress.Nuclia;
public class {MethodName}FunctionalTest
{
private readonly NucliaDbClient _client;
public {MethodName}FunctionalTest()
{
var zoneId = Environment.GetEnvironmentVariable("NUCLIA_ZONE_ID");
var kbId = Environment.GetEnvironmentVariable("NUCLIA_KB_ID");
var apiKey = Environment.GetEnvironmentVariable("NUCLIA_API_KEY");
if (string.IsNullOrEmpty(zoneId) || string.IsNullOrEmpty(kbId) || string.IsNullOrEmpty(apiKey))
{
throw new InvalidOperationException("Missing Nuclia API credentials. Set NUCLIA_ZONE_ID, NUCLIA_KB_ID, NUCLIA_API_KEY.");
}
var config = new NucliaDbConfig(zoneId, kbId, apiKey);
_client = new NucliaDbClient(config);
}
[Fact(DisplayName = "{Descriptive test name}")]
[Trait("Category", "Functional")]
public async Task Can{ActionDescription}()
{
var response = await _client.{Service}.{Method}Async();
Assert.NotNull(response);
Assert.True(response.Success, $"API call failed: {response.Error}");
Assert.NotNull(response.Data);
}
}
Write Operation Functional Test (with Cleanup)
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
using Progress.Nuclia;
using Progress.Nuclia.Model;
public class {MethodName}FunctionalTest : IAsyncLifetime
{
private readonly NucliaDbClient _client;
private readonly List<string> _createdResourceIds = new();
public {MethodName}FunctionalTest()
{
var zoneId = Environment.GetEnvironmentVariable("NUCLIA_ZONE_ID");
var kbId = Environment.GetEnvironmentVariable("NUCLIA_KB_ID");
var apiKey = Environment.GetEnvironmentVariable("NUCLIA_API_KEY");
if (string.IsNullOrEmpty(zoneId) || string.IsNullOrEmpty(kbId) || string.IsNullOrEmpty(apiKey))
{
throw new InvalidOperationException("Missing Nuclia API credentials. Set NUCLIA_ZONE_ID, NUCLIA_KB_ID, NUCLIA_API_KEY.");
}
var config = new NucliaDbConfig(zoneId, kbId, apiKey);
_client = new NucliaDbClient(config);
}
public Task InitializeAsync() => Task.CompletedTask;
public async Task DisposeAsync()
{
foreach (var resourceId in _createdResourceIds)
{
try
{
await _client.Resources.DeleteResourceByIdAsync(resourceId);
}
catch
{
}
}
}
[Fact(DisplayName = "{Descriptive test name}")]
[Trait("Category", "Functional")]
public async Task Can{ActionDescription}()
{
var payload = new CreateResourcePayload
{
Title = "test-resource",
Summary = "Test resource description"
};
var response = await _client.Resources.CreateResourceAsync(payload);
Assert.NotNull(response);
Assert.True(response.Success, $"API call failed: {response.Error}");
Assert.NotNull(response.Data);
var resourceId = response.Data.Uuid;
_createdResourceIds.Add(resourceId);
Assert.False(string.IsNullOrEmpty(resourceId));
}
}
Running Functional Tests
Functional tests require valid Nuclia API credentials set as environment variables:
$env:NUCLIA_ZONE_ID = "your-zone-id"
$env:NUCLIA_KB_ID = "your-kb-id"
$env:NUCLIA_API_KEY = "your-api-key"
Run tests:
dotnet test --filter "Category=Functional"