// Copyright 2021 Cloud Native Foundation.
// Licensed under the Apache 2.0 license.
// See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using Xunit;
namespace CloudNative.CloudEvents.UnitTests
{
///
/// Helpers for test code, e.g. common non-trivial assertions.
///
internal static class TestHelpers
{
internal static IEqualityComparer InstantOnlyTimestampComparer => EqualityComparer.Default;
internal static IEqualityComparer StrictTimestampComparer => StrictTimestampComparerImpl.Instance;
internal static CloudEventAttribute[] EmptyExtensionArray { get; } = new CloudEventAttribute[0];
internal static IEnumerable EmptyExtensionSequence { get; } = new List().AsReadOnly();
///
/// A set of extension attributes covering all attributes types.
/// The name of each attribute is the lower-cased form of the attribute type
/// name, with all punctuation removed (e.g. "urireference").
/// The attributes do not have any extra validation.
///
internal static IEnumerable AllTypesExtensions { get; } = new List
{
CloudEventAttribute.CreateExtension("binary", CloudEventAttributeType.Binary),
CloudEventAttribute.CreateExtension("boolean", CloudEventAttributeType.Boolean),
CloudEventAttribute.CreateExtension("integer", CloudEventAttributeType.Integer),
CloudEventAttribute.CreateExtension("string", CloudEventAttributeType.String),
CloudEventAttribute.CreateExtension("timestamp", CloudEventAttributeType.Timestamp),
CloudEventAttribute.CreateExtension("uri", CloudEventAttributeType.Uri),
CloudEventAttribute.CreateExtension("urireference", CloudEventAttributeType.UriReference)
}.AsReadOnly();
///
/// Arbitrary binary data to be used for testing.
/// Calling code should not rely on the exact value.
///
internal static byte[] SampleBinaryData { get; } = new byte[] { 1, 2, 3 };
///
/// The base64 representation of .
///
internal static string SampleBinaryDataBase64 { get; } = Convert.ToBase64String(SampleBinaryData); // AQID
///
/// Arbitrary timestamp to be used for testing.
/// Calling code should not rely on the exact value.
///
internal static DateTimeOffset SampleTimestamp { get; } = new DateTimeOffset(2021, 2, 19, 12, 34, 56, 789, TimeSpan.FromHours(1));
///
/// The RFC 3339 text representation of .
///
internal static string SampleTimestampText { get; } = "2021-02-19T12:34:56.789+01:00";
///
/// Arbitrary absolute URI to be used for testing.
/// Calling code should not rely on the exact value.
///
internal static Uri SampleUri { get; } = new Uri("https://absoluteuri/path");
///
/// The textual representation of .
///
internal static string SampleUriText { get; } = "https://absoluteuri/path";
///
/// Arbitrary absolute URI to be used for testing.
/// Calling code should not rely on the exact value.
///
internal static Uri SampleUriReference { get; } = new Uri("//urireference/path", UriKind.RelativeOrAbsolute);
///
/// The textual representation of .
///
internal static string SampleUriReferenceText { get; } = "//urireference/path";
///
/// Populates a CloudEvent with minimal valid attribute values.
/// Calling code should not take a dependency on the exact values used.
///
/// The original CloudEvent reference, for method chaining purposes.
internal static CloudEvent PopulateRequiredAttributes(this CloudEvent cloudEvent)
{
cloudEvent.Id = "test-id";
cloudEvent.Source = new Uri("//test", UriKind.RelativeOrAbsolute);
cloudEvent.Type = "test-type";
return cloudEvent;
}
///
/// Creates a batch of two CloudEvents, one of which has (plain text) content.
///
internal static List CreateSampleBatch()
{
var event1 = new CloudEvent().PopulateRequiredAttributes();
event1.Id = "event1";
event1.Data = "simple text";
event1.DataContentType = "text/plain";
var event2 = new CloudEvent().PopulateRequiredAttributes();
event2.Id = "event2";
return new List { event1, event2 };
}
///
/// Asserts that two timestamp values are equal, expressing the expected value as a
/// string for compact testing.
///
/// The expected value, as a string
/// The value to test against
internal static void AssertTimestampsEqual(string expected, DateTimeOffset actual)
{
// TODO: Use common RFC-3339 parsing code when we have it.
DateTimeOffset expectedDto = DateTimeOffset.ParseExact(expected, "yyyy-MM-dd'T'HH:mm:ss.FFFFFFFK", CultureInfo.InvariantCulture);
AssertTimestampsEqual(expectedDto, actual);
}
///
/// Asserts that two timestamp values are equal, in both "instant being represented"
/// and "UTC offset".
///
/// The expected value
/// The value to test against
internal static void AssertTimestampsEqual(DateTimeOffset expected, DateTimeOffset actual)
{
Assert.Equal(expected.UtcDateTime, actual.UtcDateTime);
Assert.Equal(expected.Offset, actual.Offset);
}
///
/// Asserts that two timestamp values are equal, in both "instant being represented"
/// and "UTC offset". This overload accepts nullable values, and requires that both
/// values are null or neither is.
///
/// The expected value
/// The value to test against
internal static void AssertTimestampsEqual(DateTimeOffset? expected, DateTimeOffset? actual)
{
if (expected is null && actual is null)
{
return;
}
if (expected is null || actual is null)
{
Assert.True(false, "Expected both values to be null, or neither to be null");
}
AssertTimestampsEqual(expected!.Value, actual!.Value);
}
// TODO: Use this more widely
// TODO: Document handling of timestamps, and potentially parameterize it.
internal static void AssertCloudEventsEqual(CloudEvent expected, CloudEvent actual,
IEqualityComparer? timestampComparer = null,
IEqualityComparer