File: Contents\UriContentTests.cs
Web Access
Project: ..\..\..\test\Libraries\Microsoft.Extensions.AI.Abstractions.Tests\Microsoft.Extensions.AI.Abstractions.Tests.csproj (Microsoft.Extensions.AI.Abstractions.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Text.Json;
using Xunit;
 
namespace Microsoft.Extensions.AI;
 
public sealed class UriContentTests
{
    [Fact]
    public void Ctor_InvalidUriMediaType_Throws()
    {
        Assert.Throws<ArgumentNullException>("uri", () => new UriContent((string)null!, "image/png"));
        Assert.Throws<ArgumentNullException>("uri", () => new UriContent((Uri)null!, "image/png"));
        Assert.Throws<UriFormatException>(() => new UriContent("notauri", "image/png"));
 
        Assert.Throws<ArgumentException>("mediaType", () => new UriContent("data:image/png;base64,aGVsbG8=", ""));
        Assert.Throws<ArgumentException>("mediaType", () => new UriContent("data:image/png;base64,aGVsbG8=", "image"));
 
        Assert.Throws<ArgumentException>("mediaType", () => new UriContent(new Uri("data:image/png;base64,aGVsbG8="), ""));
        Assert.Throws<ArgumentException>("mediaType", () => new UriContent(new Uri("data:image/png;base64,aGVsbG8="), "audio"));
 
        UriContent c = new("http://localhost/something", "image/png");
        Assert.Throws<ArgumentNullException>("value", () => c.Uri = null!);
    }
 
    [Theory]
    [InlineData("type")]
    [InlineData("type//subtype")]
    [InlineData("type/subtype/")]
    [InlineData("type/subtype;key=")]
    [InlineData("type/subtype;=value")]
    [InlineData("type/subtype;key=value;another=")]
    public void Ctor_InvalidMediaType_Throws(string type)
    {
        Assert.Throws<ArgumentException>("mediaType", () => new UriContent("http://localhost/something", type));
 
        UriContent c = new("http://localhost/something", "image/png");
        Assert.Throws<ArgumentException>("value", () => c.MediaType = type);
        Assert.Throws<ArgumentNullException>("value", () => c.MediaType = null!);
    }
 
    [Theory]
    [InlineData("type/subtype")]
    [InlineData("type/subtype;key=value")]
    [InlineData("type/subtype;key=value;another=value")]
    [InlineData("type/subtype;key=value;another=value;yet_another=value")]
    public void Ctor_ValidMediaType_Roundtrips(string mediaType)
    {
        var content = new UriContent("http://localhost/something", mediaType);
        Assert.Equal(mediaType, content.MediaType);
 
        content.MediaType = "image/png";
        Assert.Equal("image/png", content.MediaType);
 
        content.MediaType = mediaType;
        Assert.Equal(mediaType, content.MediaType);
    }
 
    [Theory]
    [InlineData("http://localhost/image.png", "image/png")]
    [InlineData("http://localhost/audio.mp3", "audio/mpeg")]
    [InlineData("http://localhost/document.pdf", "application/pdf")]
    [InlineData("http://localhost/data.json", "application/json")]
    [InlineData("http://localhost/page.html", "text/html")]
    [InlineData("http://localhost/photo.jpg", "image/jpeg")]
    [InlineData("http://localhost/path/to/file.wav", "audio/wav")]
    [InlineData("http://localhost/path/to/file.svg", "image/svg+xml")]
    [InlineData("http://localhost/image.png?width=100&height=100", "image/png")]
    [InlineData("http://localhost/image.png#section", "image/png")]
    [InlineData("http://localhost/image.png?q=1#frag", "image/png")]
    public void Ctor_NullMediaType_InfersFromExtension_StringUri(string uri, string expectedMediaType)
    {
        var content = new UriContent(uri);
        Assert.Equal(expectedMediaType, content.MediaType);
 
        var content2 = new UriContent(uri, null);
        Assert.Equal(expectedMediaType, content2.MediaType);
    }
 
    [Theory]
    [InlineData("http://localhost/image.png", "image/png")]
    [InlineData("http://localhost/audio.mp3", "audio/mpeg")]
    [InlineData("http://localhost/document.pdf", "application/pdf")]
    [InlineData("http://localhost/photo.jpg", "image/jpeg")]
    [InlineData("http://localhost/image.png?width=100", "image/png")]
    [InlineData("http://localhost/image.png#section", "image/png")]
    [InlineData("http://localhost/image.png?q=1#frag", "image/png")]
    public void Ctor_NullMediaType_InfersFromExtension_AbsoluteUri(string uri, string expectedMediaType)
    {
        var content = new UriContent(new Uri(uri));
        Assert.Equal(expectedMediaType, content.MediaType);
 
        var content2 = new UriContent(new Uri(uri), null);
        Assert.Equal(expectedMediaType, content2.MediaType);
    }
 
    [Theory]
    [InlineData("image.png", "image/png")]
    [InlineData("audio.mp3", "audio/mpeg")]
    [InlineData("path/to/document.pdf", "application/pdf")]
    [InlineData("photo.jpg", "image/jpeg")]
    [InlineData("image.png?width=100", "image/png")]
    [InlineData("image.png#section", "image/png")]
    [InlineData("image.png?q=1#frag", "image/png")]
    [InlineData("path/to/file.wav?key=value&other=123", "audio/wav")]
    [InlineData("path/to/file.svg#top", "image/svg+xml")]
    public void Ctor_NullMediaType_InfersFromExtension_RelativeUri(string uri, string expectedMediaType)
    {
        var content = new UriContent(new Uri(uri, UriKind.Relative));
        Assert.Equal(expectedMediaType, content.MediaType);
 
        var content2 = new UriContent(new Uri(uri, UriKind.Relative), null);
        Assert.Equal(expectedMediaType, content2.MediaType);
    }
 
    [Theory]
    [InlineData("http://localhost/noextension")]
    [InlineData("http://localhost/path/to/resource")]
    [InlineData("http://localhost/")]
    [InlineData("http://localhost/file.unknownext")]
    [InlineData("http://localhost/file.xyz123")]
    public void Ctor_NullMediaType_NoOrUnknownExtension_DefaultsToOctetStream(string uri)
    {
        var content = new UriContent(uri);
        Assert.Equal("application/octet-stream", content.MediaType);
    }
 
    [Theory]
    [InlineData("noextension")]
    [InlineData("path/to/resource")]
    [InlineData("file.unknownext")]
    [InlineData("file.xyz123")]
    [InlineData("noext?q=1")]
    [InlineData("noext#frag")]
    public void Ctor_NullMediaType_RelativeUri_NoOrUnknownExtension_DefaultsToOctetStream(string uri)
    {
        var content = new UriContent(new Uri(uri, UriKind.Relative));
        Assert.Equal("application/octet-stream", content.MediaType);
    }
 
    [Fact]
    public void Ctor_ExplicitMediaType_OverridesInference()
    {
        var content = new UriContent("http://localhost/image.png", "application/octet-stream");
        Assert.Equal("application/octet-stream", content.MediaType);
 
        var relContent = new UriContent(new Uri("image.png", UriKind.Relative), "application/octet-stream");
        Assert.Equal("application/octet-stream", relContent.MediaType);
    }
 
    [Fact]
    public void Serialize_MatchesExpectedJson()
    {
        Assert.Equal(
            """{"uri":"http://localhost/something","mediaType":"image/png"}""",
            JsonSerializer.Serialize(
                new UriContent("http://localhost/something", "image/png"),
                TestJsonSerializerContext.Default.Options));
    }
 
    [Theory]
    [InlineData("application/json")]
    [InlineData("application/octet-stream")]
    [InlineData("application/pdf")]
    [InlineData("application/xml")]
    [InlineData("audio/mpeg")]
    [InlineData("audio/ogg")]
    [InlineData("audio/wav")]
    [InlineData("image/apng")]
    [InlineData("image/avif")]
    [InlineData("image/bmp")]
    [InlineData("image/gif")]
    [InlineData("image/jpeg")]
    [InlineData("image/png")]
    [InlineData("image/svg+xml")]
    [InlineData("image/tiff")]
    [InlineData("image/webp")]
    [InlineData("text/css")]
    [InlineData("text/csv")]
    [InlineData("text/html")]
    [InlineData("text/javascript")]
    [InlineData("text/plain")]
    [InlineData("text/plain;charset=UTF-8")]
    [InlineData("text/xml")]
    [InlineData("custom/mediatypethatdoesntexists")]
    public void MediaType_Roundtrips(string mediaType)
    {
        UriContent c = new("http://localhost", mediaType);
        Assert.Equal(mediaType, c.MediaType);
    }
 
    [Theory]
    [InlineData("image/gif", "image")]
    [InlineData("IMAGE/JPEG", "image")]
    [InlineData("image/vnd.microsoft.icon", "imAge")]
    [InlineData("image/svg+xml", "IMAGE")]
    [InlineData("image/nonexistentimagemimetype", "IMAGE")]
    [InlineData("audio/mpeg", "aUdIo")]
    public void HasMediaTypePrefix_ReturnsTrue(string mediaType, string prefix)
    {
        var content = new UriContent("http://localhost", mediaType);
        Assert.True(content.HasTopLevelMediaType(prefix));
    }
 
    [Theory]
    [InlineData("audio/mpeg", "audio/")]
    [InlineData("audio/mpeg", "image")]
    [InlineData("audio/mpeg", "audio/mpeg")]
    [InlineData("text/css", "text/csv")]
    [InlineData("text/css", "/csv")]
    [InlineData("application/json", "application/json!")]
    public void HasMediaTypePrefix_ReturnsFalse(string mediaType, string prefix)
    {
        var content = new UriContent("http://localhost", mediaType);
        Assert.False(content.HasTopLevelMediaType(prefix));
    }
 
    [Fact]
    public void JsonDeserialization_KnownPayload()
    {
        const string Json = """
            {
              "$type": "uri",
              "uri": "http://localhost/something",
              "mediaType": "image/png",
              "additionalProperties": {
                "title": "My Image"
              }
            }
            """;
 
        AIContent? result = JsonSerializer.Deserialize<AIContent>(Json, AIJsonUtilities.DefaultOptions);
 
        Assert.NotNull(result);
        var uriContent = Assert.IsType<UriContent>(result);
        Assert.Equal(new Uri("http://localhost/something"), uriContent.Uri);
        Assert.Equal("image/png", uriContent.MediaType);
        Assert.NotNull(uriContent.AdditionalProperties);
        Assert.Equal("My Image", uriContent.AdditionalProperties["title"]?.ToString());
    }
}