513fa1aa16
Comment/XML-doc changes only: - add summaries to ~10k test methods, fixtures, fakes and helpers (+ <param> on theories, <returns> on async Task methods, <inheritdoc/> on interface implementations the checker can resolve) - strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N, #NN, Component-0NN) from comments, keeping the explanatory text; reword false positives in comments only - real docs on fakes implementing BCL/library interfaces (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime) - reword the last 5 src/ false positives (CWE-1236, failures-1, year-0001, M365) Only non-comment edits: four one-line nested classes split across lines so members can carry docs. Build 0 errors / 0 warnings.
106 lines
4.6 KiB
C#
106 lines
4.6 KiB
C#
using System.Xml.Linq;
|
|
using ZB.MOM.WW.ScadaBridge.NotificationService.Ews;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.NotificationService.Tests.Ews;
|
|
|
|
/// <summary>
|
|
/// Tests for the pure EWS <c>CreateItem</c> SOAP envelope builder: the fixed schema
|
|
/// sequence Exchange validates against, BCC-only recipients, and XML escaping of
|
|
/// operator-authored subject/body content.
|
|
/// </summary>
|
|
public class EwsSoapEnvelopeTests
|
|
{
|
|
private static readonly XNamespace Soap = "http://schemas.xmlsoap.org/soap/envelope/";
|
|
private static readonly XNamespace Types = "http://schemas.microsoft.com/exchange/services/2006/types";
|
|
private static readonly XNamespace Messages = "http://schemas.microsoft.com/exchange/services/2006/messages";
|
|
|
|
private static XDocument Build(
|
|
string from = "scada@example.com",
|
|
IReadOnlyList<string>? bcc = null,
|
|
string subject = "Subject",
|
|
string body = "Body")
|
|
=> XDocument.Parse(EwsSoapEnvelope.BuildCreateItem(from, bcc ?? ["one@example.com"], subject, body));
|
|
|
|
/// <summary>Verifies that the envelope sets the send-only disposition and the Exchange 2013 request version.</summary>
|
|
[Fact]
|
|
public void BuildCreateItem_SetsSendOnlyDispositionAndExchange2013RequestVersion()
|
|
{
|
|
var doc = Build();
|
|
|
|
var createItem = doc.Descendants(Messages + "CreateItem").Single();
|
|
Assert.Equal("SendOnly", (string?)createItem.Attribute("MessageDisposition"));
|
|
|
|
var version = doc.Descendants(Types + "RequestServerVersion").Single();
|
|
Assert.Equal("Exchange2013", (string?)version.Attribute("Version"));
|
|
Assert.Single(doc.Descendants(Soap + "Header").Single().Elements());
|
|
}
|
|
|
|
/// <summary>Verifies that markup in the subject and body is escaped.</summary>
|
|
[Fact]
|
|
public void BuildCreateItem_EscapesMarkupInSubjectAndBody()
|
|
{
|
|
const string subject = "Alarm <HIGH> & \"critical\"";
|
|
const string body = "value < 5 & tag=\"A\" > threshold";
|
|
|
|
var doc = Build(subject: subject, body: body);
|
|
|
|
// Round-tripping through the parser proves the content was escaped, not concatenated.
|
|
Assert.Equal(subject, doc.Descendants(Types + "Subject").Single().Value);
|
|
Assert.Equal(body, doc.Descendants(Types + "Body").Single().Value);
|
|
Assert.Equal("Text", (string?)doc.Descendants(Types + "Body").Single().Attribute("BodyType"));
|
|
}
|
|
|
|
/// <summary>Verifies that every recipient is placed in the BCC list and no To recipients are emitted.</summary>
|
|
[Fact]
|
|
public void BuildCreateItem_PutsEveryRecipientInBccAndNeverEmitsToRecipients()
|
|
{
|
|
string[] recipients = ["a@example.com", "b@example.com", "c@example.com"];
|
|
|
|
var doc = Build(bcc: recipients);
|
|
|
|
var bcc = doc.Descendants(Types + "BccRecipients").Single();
|
|
var addresses = bcc.Elements(Types + "Mailbox")
|
|
.Select(m => m.Element(Types + "EmailAddress")!.Value)
|
|
.ToArray();
|
|
Assert.Equal(recipients, addresses);
|
|
|
|
// Recipient privacy is the whole reason for BCC-only: no To/Cc may leak the list.
|
|
Assert.DoesNotContain(doc.Descendants(), e => e.Name.LocalName is "ToRecipients" or "CcRecipients");
|
|
}
|
|
|
|
/// <summary>Verifies that the envelope emits the From mailbox.</summary>
|
|
[Fact]
|
|
public void BuildCreateItem_EmitsFromMailbox()
|
|
{
|
|
var doc = Build(from: "scada-alerts@example.com");
|
|
|
|
var from = doc.Descendants(Types + "From").Single();
|
|
var mailbox = from.Elements(Types + "Mailbox").Single();
|
|
Assert.Equal("scada-alerts@example.com", mailbox.Element(Types + "EmailAddress")!.Value);
|
|
}
|
|
|
|
/// <summary>Verifies that the message child elements follow the EWS schema sequence.</summary>
|
|
[Fact]
|
|
public void BuildCreateItem_OrdersMessageChildrenPerEwsSchemaSequence()
|
|
{
|
|
var doc = Build();
|
|
|
|
// MessageType is an xs:sequence — any other order is ErrorSchemaValidation at the server.
|
|
var message = doc.Descendants(Types + "Message").Single();
|
|
Assert.Equal(
|
|
new[] { "Subject", "Body", "BccRecipients", "From" },
|
|
message.Elements().Select(e => e.Name.LocalName).ToArray());
|
|
}
|
|
|
|
/// <summary>Verifies that the envelope starts with a UTF-8 XML declaration.</summary>
|
|
[Fact]
|
|
public void BuildCreateItem_StartsWithUtf8XmlDeclaration()
|
|
{
|
|
var xml = EwsSoapEnvelope.BuildCreateItem("a@example.com", ["b@example.com"], "s", "b");
|
|
|
|
Assert.StartsWith("<?xml", xml, StringComparison.Ordinal);
|
|
var declaration = xml[..xml.IndexOf("?>", StringComparison.Ordinal)];
|
|
Assert.Contains("utf-8", declaration, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|