MdpOutboundGateTests.cs 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using Admin.NET.Plugin.AiDOP.DataPlatform.Executors;
  2. using Admin.NET.Plugin.AiDOP.Entity.DataPlatform;
  3. using Microsoft.Extensions.Logging.Abstractions;
  4. using Microsoft.Extensions.Options;
  5. using Xunit;
  6. namespace Admin.NET.Plugin.AiDOP.Tests.DataPlatform;
  7. public class MdpOutboundGateTests
  8. {
  9. [Fact]
  10. public async Task Disabled_DbExecutor_DoesNotSend()
  11. {
  12. var gate = Gate(false);
  13. var executor = new MdpDbPushExecutor(null!, gate);
  14. var result = await executor.PushAsync(new MdpSource { SourceType = "DB", SourceCode = "DOPDEMORQ_SQLSERVER" }, new MdpOutbox());
  15. Assert.True(result.Held);
  16. Assert.False(result.Success);
  17. Assert.Equal(1, gate.RefusedCount);
  18. }
  19. [Fact]
  20. public async Task Disabled_ApiExecutor_DoesNotSend()
  21. {
  22. var gate = Gate(false);
  23. var executor = new MdpApiPushExecutor(null!, gate, NullLoggerFactory.Instance);
  24. var result = await executor.PushAsync(new MdpSource { SourceType = "API", SourceCode = "WMS_API", ApiBaseUrl = "http://127.0.0.1" }, new MdpOutbox());
  25. Assert.True(result.Held);
  26. Assert.False(result.Success);
  27. Assert.Equal(1, gate.RefusedCount);
  28. }
  29. [Fact]
  30. public void Enabled_DoesNotHold()
  31. {
  32. var gate = Gate(true);
  33. Assert.Null(gate.TryHold());
  34. Assert.Equal(0, gate.RefusedCount);
  35. }
  36. [Fact]
  37. public void Callers_DoNotMutateOutbox_WhenHeld()
  38. {
  39. var admin = Read("DataPlatform/MdpOutboxAdminService.cs");
  40. var pushNow = admin[(admin.IndexOf("push-now", StringComparison.Ordinal))..];
  41. var heldAt = pushNow.IndexOf("result.Held", StringComparison.Ordinal);
  42. var updateAt = pushNow.IndexOf("Updateable<MdpOutbox>", StringComparison.Ordinal);
  43. Assert.True(heldAt >= 0 && updateAt > heldAt, "手工重投必须先判断 Held,再写 outbox");
  44. var dispatcher = Read("DataPlatform/Executors/MdpTargetPushDispatcher.cs");
  45. var gateAt = dispatcher.IndexOf("!_gate.IsEnabled", StringComparison.Ordinal);
  46. var queryAt = dispatcher.IndexOf("Queryable<MdpOutbox>", StringComparison.Ordinal);
  47. Assert.True(gateAt >= 0 && gateAt < queryAt, "批量推送关闭时不得先取出消息");
  48. Assert.Contains("result.Held", dispatcher, StringComparison.Ordinal);
  49. var pick = Read("DataPlatform/Wms/PickBillNbrSyncService.cs");
  50. var pickGate = pick.IndexOf("_gate.TryHold()", StringComparison.Ordinal);
  51. var pickSend = pick.IndexOf("GetScopeAsync", StringComparison.Ordinal);
  52. Assert.True(pickGate >= 0 && pickGate < pickSend, "领料单号同步必须先过闸门再连 165");
  53. }
  54. private static MdpOutboundGate Gate(bool enabled) =>
  55. new(new FixedMonitor(enabled), NullLoggerFactory.Instance);
  56. private static string Read(string relative)
  57. {
  58. var dir = new DirectoryInfo(AppContext.BaseDirectory);
  59. while (dir != null)
  60. {
  61. var candidate = Path.Combine(dir.FullName, "server", "Plugins", "Admin.NET.Plugin.AiDOP", relative);
  62. if (File.Exists(candidate)) return File.ReadAllText(candidate);
  63. dir = dir.Parent;
  64. }
  65. throw new FileNotFoundException(relative);
  66. }
  67. private sealed class FixedMonitor(bool enabled) : IOptionsMonitor<AidopOutboundOptions>
  68. {
  69. public AidopOutboundOptions CurrentValue { get; } = new() { Enabled = enabled };
  70. public AidopOutboundOptions Get(string? name) => CurrentValue;
  71. public IDisposable OnChange(Action<AidopOutboundOptions, string?> listener) => new Noop();
  72. private sealed class Noop : IDisposable { public void Dispose() { } }
  73. }
  74. }