When commit 0e95e283 moved BlogPost from PostIt.Models to
Yavsc.Blogspot, it created an unfortunate collision with the
server-side EF entity Yavsc.Models.Blog.BlogPost. The two
classes have nothing in common beyond the name; the DTO is
the wire shape PostIt exchanges with the Blogs API, the EF
entity is the persistence model. Server code that imports both
namespaces (BlogSpotService.cs, etc.) ended up with 'BlogPost
is an ambiguous reference between X and Y' errors.
Renaming the client DTO to BlogPostDto (matching the
naming convention of the other DTOs in Yavsc.Api.Client.Dtos
— CircleDto, CircleAuthorizationDto, UserSearchResultDto)
disambiguates without renaming the EF entity on the server.
The namespace stays Yavsc.Blogspot; only the class name
changes. All call sites (client code, tests, XAML DataTemplates,
XML doc comments) are updated mechanically.
106 lines
4 KiB
C#
106 lines
4 KiB
C#
using Yavsc.Blogspot;
|
|
using Yavsc.Api.Client;
|
|
using PostIt.Services;
|
|
using PostIt.ViewModels;
|
|
|
|
namespace PostIt.Tests;
|
|
|
|
public class PostItViewModelTests
|
|
{
|
|
|
|
[Fact]
|
|
public void SearchCommand_filters_posts_by_title_article_or_author()
|
|
{
|
|
// MainPageViewModel no longer owns a BlogApiClient instance by
|
|
// default; tests construct one with a fake YavscApiClient that
|
|
// throws on any call (we never call the API in this test).
|
|
var fakeApi = new ThrowingYavscApiClient();
|
|
var blog = new BlogApiClient(fakeApi, "http://localhost/");
|
|
var viewModel = new MainPageViewModel(blog);
|
|
|
|
viewModel.Posts.Add(new BlogPostDto { Id = 1, Title = "First post", Article = "Hello world", AuthorId = "alice" });
|
|
viewModel.Posts.Add(new BlogPostDto { Id = 2, Title = "Second post", Article = "Nothing here", AuthorId = "bob" });
|
|
viewModel.Posts.Add(new BlogPostDto { Id = 3, Title = "Third post", Article = "Search me", AuthorId = "carol" });
|
|
|
|
viewModel.SearchText = "search";
|
|
viewModel.SearchCommand.Execute(null);
|
|
|
|
Assert.Single(viewModel.FilteredPosts);
|
|
Assert.Equal(3, viewModel.FilteredPosts[0].Id);
|
|
|
|
viewModel.SearchText = "bob";
|
|
viewModel.SearchCommand.Execute(null);
|
|
|
|
Assert.Single(viewModel.FilteredPosts);
|
|
Assert.Equal(2, viewModel.FilteredPosts[0].Id);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BlogApiClient_GetPostsAsync_returns_posts_from_api()
|
|
{
|
|
// The new BlogApiClient delegates transport to YavscApiClient.
|
|
// We feed it a fake YavscApiClient that returns the expected
|
|
// list straight from CallAsync.
|
|
var expected = new List<BlogPostDto>
|
|
{
|
|
new() { Id = 1, Title = "Hello" },
|
|
new() { Id = 2, Title = "World" }
|
|
};
|
|
var api = new StubYavscApiClient(expected);
|
|
var blog = new BlogApiClient(api, "http://localhost/");
|
|
|
|
var posts = await blog.GetPostsAsync();
|
|
|
|
Assert.Equal(2, posts.Count);
|
|
Assert.Equal("Hello", posts[0].Title);
|
|
}
|
|
|
|
/// <summary>Test fake that always throws if the API is invoked.</summary>
|
|
private sealed class ThrowingYavscApiClient : YavscApiClient
|
|
{
|
|
public ThrowingYavscApiClient() : base(
|
|
new Settings
|
|
{
|
|
Authentication = new AuthenticationSettings
|
|
{
|
|
Authority = "https://stub.invalid",
|
|
ClientId = "stub",
|
|
Scopes = new[] { "openid" },
|
|
},
|
|
},
|
|
new TokenStore(System.IO.Path.GetTempFileName()))
|
|
{ }
|
|
public override Task<T> CallAsync<T>(HttpMethod method, string path, object? body = null, CancellationToken ct = default)
|
|
=> throw new System.InvalidOperationException("ThrowingYavscApiClient: API not stubbed.");
|
|
}
|
|
|
|
/// <summary>Test fake that hands back a canned list of posts from any CallAsync.</summary>
|
|
private sealed class StubYavscApiClient : YavscApiClient
|
|
{
|
|
private readonly List<BlogPostDto> _posts;
|
|
public StubYavscApiClient(List<BlogPostDto> posts)
|
|
: base(
|
|
new Settings
|
|
{
|
|
Authentication = new AuthenticationSettings
|
|
{
|
|
Authority = "https://stub.invalid",
|
|
ClientId = "stub",
|
|
Scopes = new[] { "openid" },
|
|
},
|
|
},
|
|
new TokenStore(System.IO.Path.GetTempFileName()))
|
|
{
|
|
_posts = posts;
|
|
}
|
|
|
|
public override Task<T> CallAsync<T>(HttpMethod method, string path, object? body = null, CancellationToken ct = default)
|
|
{
|
|
// The canned fake only knows about a list of posts; the
|
|
// BlogApiClient test asserts on that list directly.
|
|
if (typeof(T) == typeof(List<BlogPostDto>))
|
|
return Task.FromResult((T)(object)_posts);
|
|
return Task.FromResult(default(T)!);
|
|
}
|
|
}
|
|
}
|