Files
Khwezi Mngoma a98adea8f3
continuous-integration/drone/pr Build is passing
Implemented LiteCharms Security TokenService
2026-06-12 16:09:51 +02:00

113 lines
4.1 KiB
C#

using LiteCharms.Features.MidrandBooks.Payments;
using LiteCharms.Features.MidrandBooks.Payments.Models;
using LiteCharms.Features.Tests.Common;
namespace LiteCharms.Features.MidrandBooks.Tests;
public sealed class PayfastServiceFeatureTests(Fixture fixture) : IClassFixture<Fixture>
{
private readonly PayfastService payfastService = fixture.Services.GetRequiredService<PayfastService>();
[IntegrationFact]
public async Task WriteLedgerEntryAsync_ShouldReturn_ResultWithGatewayLedgerId()
{
var request = new CreateGatewayLedgerEntry
{
OrderId = 1,
PaymentId = 1,
MerchantPaymentId = "M_REF_TEST_99",
PayfastPaymentId = "PF_SYS_ID_10023",
CustomerEmail = "buyer@litecharms.co.za",
AmountGross = 350.00m,
AmountFee = 12.50m,
AmountNet = 337.50m,
PaymentStatus = "COMPLETE"
};
var result = await payfastService.WriteLedgerEntryAsync(request, fixture.CancellationToken);
Assert.True(result.IsSuccess);
Assert.True(result.Value > 0);
}
[IntegrationFact]
public async Task ValidateReferrerIpAsync_WithValidPayfastHostIp_ShouldReturnTrue()
{
var addresses = await Dns.GetHostAddressesAsync("sandbox.payfast.co.za", fixture.CancellationToken);
string liveTargetIp = addresses.First().ToString();
var result = await payfastService.ValidateReferrerIpAsync(liveTargetIp, true, fixture.CancellationToken);
Assert.True(result.IsSuccess);
Assert.True(result.Value);
}
[IntegrationFact]
public async Task ValidateReferrerIpAsync_WithUntrustedIp_ShouldReturnFalse()
{
string rogueIp = "8.8.8.8";
var result = await payfastService.ValidateReferrerIpAsync(rogueIp, true, fixture.CancellationToken);
Assert.True(result.IsSuccess);
Assert.False(result.Value);
}
[IntegrationFact]
public void ValidatePaymentAmount_WhenWithinAllowableDelta_ShouldReturnTrue()
{
decimal systemExpectedTotal = 199.99m;
string gatewayClearedGross = "200.00"; // Variance is exactly R0.01
var result = payfastService.ValidatePaymentAmount(systemExpectedTotal, gatewayClearedGross);
Assert.True(result.IsSuccess);
Assert.True(result.Value);
}
[IntegrationFact]
public void ValidatePaymentAmount_WhenVarianceBreachesDeltaBounds_ShouldReturnFalse()
{
decimal systemExpectedTotal = 199.99m;
string gatewayClearedGross = "150.00";
var result = payfastService.ValidatePaymentAmount(systemExpectedTotal, gatewayClearedGross);
Assert.True(result.IsSuccess);
Assert.False(result.Value);
}
[IntegrationFact]
public async Task ValidateServerConfirmationAsync_WithUnrecognizedPayload_ShouldReturnFalseFromCentralGateway()
{
// Arrange - Execute against actual Payfast servers using raw mock parameters.
// The server handshake will return 200 OK with string payload 'INVALID'
string arbitraryParameters = "merchant_id=10000000&payment_status=COMPLETE";
var result = await payfastService.ValidateServerConfirmationAsync(arbitraryParameters, isSandbox: true, fixture.CancellationToken);
Assert.True(result.IsSuccess);
Assert.False(result.Value); // Handshake data rejected as fraudulent/unrecognized
}
[IntegrationFact]
public void GenerateSignature_WithStandardTelemetryData_ShouldSucceedAndHashString()
{
var telemetryPayload = new Dictionary<string, string?>
{
{ "merchant_id", "10049307" },
{ "merchant_key", "ju6navn0jcbf0" },
{ "amount_gross", "250.00" },
{ "item_name", "Midrand School Textbook Variant A" }
};
string passphrase = "oauth_test_signature_pass";
var result = PayfastService.GenerateSignature(telemetryPayload, passphrase);
Assert.True(result.IsSuccess);
Assert.False(string.IsNullOrWhiteSpace(result.Value));
Assert.Equal(32, result.Value.Length); // MD5 outputs hex representations totaling 32 characters
}
}