ef15fd6247
Full backend implementation: - Multi-tenant cafe/restaurant management (menus, orders, tables, staff) - POS order flow with ZarinPal and Snappfood payment integration - OTP authentication via Kavenegar SMS - QR digital menu with public discover/finder endpoints - Customer loyalty, coupons, CRM - PostgreSQL via EF Core, Redis for caching/sessions - Background jobs, webhook handlers - Full migration history Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
121 lines
4.1 KiB
C#
121 lines
4.1 KiB
C#
using Meezi.API.Configuration;
|
|
using Meezi.API.Models.Snappfood;
|
|
using Meezi.API.Models.Tap30;
|
|
using Meezi.API.Services.Delivery;
|
|
using Meezi.Core.Delivery;
|
|
using Meezi.Core.Enums;
|
|
using Meezi.Infrastructure.Data;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Options;
|
|
using Xunit;
|
|
|
|
namespace Meezi.API.Tests;
|
|
|
|
public class DeliveryIntegrationTests
|
|
{
|
|
private static DeliveryPlatformsOptions TestOptions() => new()
|
|
{
|
|
DefaultSnappfoodCommissionPercent = 18,
|
|
DefaultTap30CommissionPercent = 15
|
|
};
|
|
|
|
[Fact]
|
|
public void OrderNormalizer_Snappfood_MapsToUnifiedOrder()
|
|
{
|
|
var normalizer = new OrderNormalizer();
|
|
var unified = normalizer.FromSnappfood(new SnappfoodWebhookOrder(
|
|
"sf-100",
|
|
"vendor-1",
|
|
"Ali",
|
|
"09121234567",
|
|
250_000m,
|
|
[new SnappfoodWebhookItem("Espresso", 2, 100_000m)]));
|
|
|
|
Assert.NotNull(unified);
|
|
Assert.Equal("sf-100", unified!.ExternalId);
|
|
Assert.Equal(DeliveryPlatform.Snappfood, unified.Platform);
|
|
Assert.Equal(2, unified.Items[0].Quantity);
|
|
Assert.Equal(UnifiedDeliveryStatus.Confirmed, unified.Status);
|
|
}
|
|
|
|
[Fact]
|
|
public void OrderNormalizer_Tap30_MapsToUnifiedOrder()
|
|
{
|
|
var normalizer = new OrderNormalizer();
|
|
var unified = normalizer.FromTap30(new Tap30WebhookOrder(
|
|
"t30-55",
|
|
"tap-vendor",
|
|
new Tap30Customer("Sara", "09129876543", "Tehran", null, null),
|
|
180_000m,
|
|
"online",
|
|
true,
|
|
null,
|
|
"delivery",
|
|
35,
|
|
null,
|
|
null,
|
|
"confirmed",
|
|
[new Tap30WebhookItem("latte-1", "Latte", 1, 180_000m, null)]));
|
|
|
|
Assert.NotNull(unified);
|
|
Assert.Equal(DeliveryPlatform.Tap30, unified!.Platform);
|
|
Assert.Equal("Sara", unified.Customer.Name);
|
|
Assert.Equal(35, unified.Delivery.EstimatedMinutes);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CommissionCalculator_SnappfoodDefault_Is18Percent()
|
|
{
|
|
var options = new DbContextOptionsBuilder<AppDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
await using var db = new AppDbContext(options);
|
|
var calc = new CommissionCalculator(db, Options.Create(TestOptions()));
|
|
|
|
var commission = await calc.CalculateForOrderAsync(
|
|
"cafe-1",
|
|
new UnifiedDeliveryOrder(
|
|
"x1",
|
|
DeliveryPlatform.Snappfood,
|
|
"v1",
|
|
DateTime.UtcNow,
|
|
new UnifiedDeliveryCustomer("A", "09"),
|
|
[new UnifiedDeliveryItem("1", "Coffee", 1, 100_000m)],
|
|
new UnifiedDeliveryPayment(100_000m, "online", true),
|
|
new UnifiedDeliveryInfo("delivery"),
|
|
UnifiedDeliveryStatus.Confirmed));
|
|
|
|
Assert.Equal(18_000m, commission);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CommissionCalculator_Tap30Default_Is15Percent()
|
|
{
|
|
var options = new DbContextOptionsBuilder<AppDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
await using var db = new AppDbContext(options);
|
|
var calc = new CommissionCalculator(db, Options.Create(TestOptions()));
|
|
|
|
var rate = await calc.ResolveRatePercentAsync("cafe-1", DeliveryPlatform.Tap30);
|
|
Assert.Equal(15m, rate);
|
|
Assert.Equal(15_000m, calc.CalculateCommission(100_000m, rate));
|
|
}
|
|
|
|
[Fact]
|
|
public void WebhookSignature_ValidHmac_Accepts()
|
|
{
|
|
const string secret = "test-secret";
|
|
const string body = """{"orderId":"1"}""";
|
|
var expected = WebhookSignatureService.ComputeHmacSha256Hex(body, secret);
|
|
|
|
var svc = new WebhookSignatureService(Options.Create(new DeliveryPlatformsOptions
|
|
{
|
|
Snappfood = new SnappfoodPlatformOptions { WebhookSecret = secret }
|
|
}));
|
|
|
|
Assert.True(svc.Verify(DeliveryPlatform.Snappfood, body, expected));
|
|
Assert.True(svc.Verify(DeliveryPlatform.Snappfood, body, $"sha256={expected}"));
|
|
}
|
|
}
|