Browse Source

Add end-to-end ClientProxy tests for ContentTypes scenarios

Refs abpframework/abp#23732
pull/25639/head
maliming 3 months ago
parent
commit
a765cc036c
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 19
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs
  2. 84
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PeopleAppServiceClientProxy_ReturnContentTypes_Tests.cs
  3. 72
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs
  4. 102
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_ReturnContentTypes_Tests.cs
  5. 8
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs
  6. 25
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs

19
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs

@ -1,6 +1,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Content;
namespace Volo.Abp.Http.DynamicProxying;
@ -8,6 +9,24 @@ public interface IRegularTestController
{
Task<int> IncrementValueAsync(int value);
Task<string> GetPlainStringAsync();
Task<string> GetProducesJsonStringAsync();
Task<string> GetProducesTextStringAsync();
Task<string> GetNullStringAsync();
Task<string> GetProducesJsonNullStringAsync();
Task<string> GetEmptyStringAsync();
Task<string> GetEscapedStringAsync();
Task<IRemoteStreamContent> DownloadIconAsync();
Task<byte[]> GetByteArrayAsync();
Task GetException1Async();
Task GetException2Async();

84
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PeopleAppServiceClientProxy_ReturnContentTypes_Tests.cs

@ -0,0 +1,84 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Http.Client;
using Volo.Abp.TestApp.Application;
using Xunit;
namespace Volo.Abp.Http.DynamicProxying;
public class PeopleAppServiceClientProxy_ReturnContentTypes_Tests : AbpHttpClientTestBase
{
private readonly IPeopleAppService _peopleAppService;
public PeopleAppServiceClientProxy_ReturnContentTypes_Tests()
{
_peopleAppService = ServiceProvider.GetRequiredService<IPeopleAppService>();
}
[Fact]
public async Task EchoStatusAsync_Should_Return_Plain_String_Without_Quotes()
{
var status = await _peopleAppService.EchoStatusAsync();
status.ShouldBe("Open");
status.StartsWith("\"").ShouldBeFalse();
status.EndsWith("\"").ShouldBeFalse();
}
[Fact]
public async Task EchoStatusWithProducesJsonAsync_Should_Return_Plain_String_Without_Quotes()
{
var status = await _peopleAppService.EchoStatusWithProducesJsonAsync();
status.ShouldBe("Open");
status.StartsWith("\"").ShouldBeFalse();
status.EndsWith("\"").ShouldBeFalse();
}
[Fact]
public async Task GetBinaryImageAsync_Should_Return_Real_Binary_Not_Json_Metadata()
{
using var content = await _peopleAppService.GetBinaryImageAsync();
using var ms = new MemoryStream();
await content.GetStream().CopyToAsync(ms);
var bytes = ms.ToArray();
content.FileName.ShouldBe("tiny.png");
content.ContentType.ShouldStartWith("image/png");
bytes.Length.ShouldBeGreaterThan(8);
bytes[0].ShouldBe((byte)0x89);
bytes[1].ShouldBe((byte)0x50);
bytes[2].ShouldBe((byte)0x4E);
bytes[3].ShouldBe((byte)0x47);
}
[Fact]
public async Task ThrowFromStringAsync_Should_Surface_Server_Exception_To_Client()
{
await Should.ThrowAsync<AbpRemoteCallException>(
() => _peopleAppService.ThrowFromStringAsync()
);
}
[Fact]
public async Task DownloadAsync_Should_Still_Work()
{
using var content = await _peopleAppService.DownloadAsync();
using var reader = new StreamReader(content.GetStream());
var text = await reader.ReadToEndAsync();
text.ShouldBe("DownloadAsync");
content.FileName.ShouldBe("download.rtf");
content.ContentType.ShouldStartWith("application/rtf");
}
[Fact]
public async Task UploadAsync_String_Return_Should_Stay_Unquoted()
{
using var ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("hello"));
var result = await _peopleAppService.UploadAsync(
new Content.RemoteStreamContent(ms, "upload.txt", "text/plain"));
result.ShouldBe("hello:text/plain:upload.txt");
result.StartsWith("\"").ShouldBeFalse();
}
}

72
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs

@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Content;
namespace Volo.Abp.Http.DynamicProxying;
@ -20,6 +23,75 @@ public class RegularTestController : AbpController, IRegularTestController
return Task.FromResult(value + 1);
}
[HttpGet]
[Route("plain-string")]
public Task<string> GetPlainStringAsync()
{
return Task.FromResult("Open");
}
[HttpGet]
[Route("produces-json-string")]
[Produces("application/json")]
public Task<string> GetProducesJsonStringAsync()
{
return Task.FromResult("Open");
}
[HttpGet]
[Route("produces-text-string")]
[Produces("text/plain")]
public Task<string> GetProducesTextStringAsync()
{
return Task.FromResult("Open");
}
[HttpGet]
[Route("null-string")]
public Task<string> GetNullStringAsync()
{
return Task.FromResult<string>(null!);
}
[HttpGet]
[Route("produces-json-null-string")]
[Produces("application/json")]
public Task<string> GetProducesJsonNullStringAsync()
{
return Task.FromResult<string>(null!);
}
[HttpGet]
[Route("empty-string")]
public Task<string> GetEmptyStringAsync()
{
return Task.FromResult(string.Empty);
}
[HttpGet]
[Route("escaped-string")]
[Produces("application/json")]
public Task<string> GetEscapedStringAsync()
{
return Task.FromResult("a\"b\\c\nd");
}
[HttpGet]
[Route("download-icon")]
public Task<IRemoteStreamContent> DownloadIconAsync()
{
var bytes = Encoding.UTF8.GetBytes("ICON-BYTES");
return Task.FromResult<IRemoteStreamContent>(
new RemoteStreamContent(new MemoryStream(bytes), "icon.bin", "application/octet-stream"));
}
[HttpGet]
[Route("byte-array")]
public Task<byte[]> GetByteArrayAsync()
{
return Task.FromResult(new byte[] { 1, 2, 3, 4 });
}
[HttpGet]
[Route("get-exception1")]
public Task GetException1Async()

102
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_ReturnContentTypes_Tests.cs

@ -0,0 +1,102 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
namespace Volo.Abp.Http.DynamicProxying;
public class RegularTestControllerClientProxy_ReturnContentTypes_Tests : AbpHttpClientTestBase
{
private readonly IRegularTestController _controller;
public RegularTestControllerClientProxy_ReturnContentTypes_Tests()
{
_controller = ServiceProvider.GetRequiredService<IRegularTestController>();
}
[Fact]
public async Task GetPlainStringAsync_Should_Return_Unwrapped_String()
{
var result = await _controller.GetPlainStringAsync();
result.ShouldBe("Open");
result.StartsWith("\"").ShouldBeFalse();
}
[Fact]
public async Task GetProducesJsonStringAsync_Should_Strip_Json_Quotes()
{
var result = await _controller.GetProducesJsonStringAsync();
result.ShouldBe("Open");
result.StartsWith("\"").ShouldBeFalse();
result.EndsWith("\"").ShouldBeFalse();
}
[Fact]
public async Task GetProducesTextStringAsync_Should_Return_Raw_Text_Body()
{
var result = await _controller.GetProducesTextStringAsync();
result.ShouldBe("Open");
result.StartsWith("\"").ShouldBeFalse();
}
[Fact]
public async Task GetNullStringAsync_Should_Return_Default_Or_Empty()
{
var result = await _controller.GetNullStringAsync();
(result == null || result == string.Empty).ShouldBeTrue();
}
[Fact]
public async Task GetEmptyStringAsync_Should_Return_Empty()
{
var result = await _controller.GetEmptyStringAsync();
(result == null || result == string.Empty).ShouldBeTrue();
}
[Fact]
public async Task GetProducesJsonNullStringAsync_Should_Not_Return_Literal_Null()
{
// Server returns JSON `null` body (4 chars). The unwrap MUST NOT pass through "null" literal —
// it should produce empty/null on the client side instead.
var result = await _controller.GetProducesJsonNullStringAsync();
result.ShouldNotBe("null");
(result == null || result == string.Empty).ShouldBeTrue();
}
[Fact]
public async Task GetEscapedStringAsync_Should_Decode_Escaped_Characters()
{
// Server JSON-encodes the string with escapes: "a\"b\\c\nd"
// Without unwrap fix client would receive the raw JSON string including escapes.
var result = await _controller.GetEscapedStringAsync();
result.ShouldBe("a\"b\\c\nd");
}
[Fact]
public async Task DownloadIconAsync_Should_Return_Binary_Bytes()
{
using var content = await _controller.DownloadIconAsync();
using var ms = new MemoryStream();
await content.GetStream().CopyToAsync(ms);
ms.ToArray().ShouldBe(System.Text.Encoding.UTF8.GetBytes("ICON-BYTES"));
content.FileName.ShouldBe("icon.bin");
}
[Fact]
public async Task GetByteArrayAsync_Should_Round_Trip_Bytes()
{
// byte[] is not IRemoteStreamContent; goes through default JSON path
// (server JSON-encodes as base64). Ensures our Accept logic didn't break
// the existing non-stream binary case.
var bytes = await _controller.GetByteArrayAsync();
bytes.ShouldBe(new byte[] { 1, 2, 3, 4 });
}
[Fact]
public async Task Existing_IncrementValueAsync_Regression_Should_Still_Work()
{
var result = await _controller.IncrementValueAsync(41);
result.ShouldBe(42);
}
}

8
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs

@ -39,4 +39,12 @@ public interface IPeopleAppService : ICrudAppService<PersonDto, Guid>
Task<string> GetParamsFromQueryAsync(GetParamsInput input);
Task<string> GetParamsFromFormAsync(GetParamsInput input);
Task<string> EchoStatusAsync();
Task<string> EchoStatusWithProducesJsonAsync();
Task<IRemoteStreamContent> GetBinaryImageAsync();
Task<string> ThrowFromStringAsync();
}

25
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Shouldly;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.Domain.Repositories;
@ -94,6 +95,30 @@ public class PeopleAppService : CrudAppService<Person, PersonDto, Guid>, IPeople
return new RemoteStreamContent(memoryStream, "download.rtf", "application/rtf");
}
public Task<string> EchoStatusAsync()
{
return Task.FromResult("Open");
}
[Produces("application/json")]
public Task<string> EchoStatusWithProducesJsonAsync()
{
return Task.FromResult("Open");
}
public Task<IRemoteStreamContent> GetBinaryImageAsync()
{
var bytes = Convert.FromBase64String(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=");
return Task.FromResult<IRemoteStreamContent>(
new RemoteStreamContent(new MemoryStream(bytes), "tiny.png", "image/png"));
}
public Task<string> ThrowFromStringAsync()
{
throw new BusinessException("TestApp.StringEndpointBoom", "string endpoint failed");
}
public async Task<string> UploadAsync(IRemoteStreamContent streamContent)
{
using (var reader = new StreamReader(streamContent.GetStream()))

Loading…
Cancel
Save