134 lines
4.9 KiB
C#
134 lines
4.9 KiB
C#
using System.Text.Json;
|
|
using PLib.Infrastructure.Metadata;
|
|
using Shouldly;
|
|
|
|
namespace PLib.Tests.Metadata;
|
|
|
|
/// <summary>
|
|
/// Decoding a stash-box reply, against payloads shaped like the real ones.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This is the part of the feature that cannot be checked without a server and a key, and the
|
|
/// part where a wrong guess looks exactly like "the source knows nothing about your video".
|
|
/// </remarks>
|
|
public sealed class StashBoxPayloadTests
|
|
{
|
|
/// <summary>The newer query, whose result is one group of scenes per group of fingerprints.</summary>
|
|
private static StashBoxMetadataProvider.GraphQlQuery Nested => StashBoxMetadataProvider.Queries[0];
|
|
|
|
/// <summary>The older query, kept for instances that have not been updated.</summary>
|
|
private static StashBoxMetadataProvider.GraphQlQuery Flat => StashBoxMetadataProvider.Queries[1];
|
|
|
|
[Fact]
|
|
public void The_newer_query_returns_scenes_one_level_deeper_and_they_are_flattened()
|
|
{
|
|
const string payload = """
|
|
{
|
|
"data": {
|
|
"findScenesBySceneFingerprints": [
|
|
[
|
|
{
|
|
"id": "abc",
|
|
"title": "Первая",
|
|
"details": "Описание",
|
|
"studio": { "name": "Студия" },
|
|
"tags": [ { "name": "драма" }, { "name": "нуар" } ],
|
|
"performers": [ { "performer": { "name": "Актёр" }, "as": "Псевдоним" } ]
|
|
},
|
|
{ "id": "def", "title": "Вторая", "studio": null, "tags": [], "performers": [] }
|
|
]
|
|
]
|
|
}
|
|
}
|
|
""";
|
|
|
|
var matches = Read(payload, Nested);
|
|
|
|
matches.Count.ShouldBe(2);
|
|
|
|
var first = matches[0];
|
|
first.SourceName.ShouldBe("StashDB");
|
|
first.RemoteId.ShouldBe("abc");
|
|
first.Title.ShouldBe("Первая");
|
|
first.Description.ShouldBe("Описание");
|
|
first.Studios.ShouldBe(["Студия"]);
|
|
first.Tags.ShouldBe(["драма", "нуар"]);
|
|
|
|
// The credited alias is not the person; the label has to be the performer's own name.
|
|
first.Performers.ShouldBe(["Актёр"]);
|
|
|
|
matches[1].Studios.ShouldBeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void The_older_query_returns_a_flat_list()
|
|
{
|
|
const string payload = """
|
|
{
|
|
"data": {
|
|
"findSceneByFingerprint": [
|
|
{ "id": "abc", "title": "Сцена", "studio": { "name": "Студия" }, "tags": [], "performers": [] }
|
|
]
|
|
}
|
|
}
|
|
""";
|
|
|
|
Read(payload, Flat).ShouldHaveSingleItem().Title.ShouldBe("Сцена");
|
|
}
|
|
|
|
[Fact]
|
|
public void A_reply_with_no_matches_is_an_empty_list_rather_than_a_failure()
|
|
{
|
|
Read("""{ "data": { "findScenesBySceneFingerprints": [ [] ] } }""", Nested).ShouldBeEmpty();
|
|
Read("""{ "data": { "findScenesBySceneFingerprints": null } }""", Nested).ShouldBeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void A_scene_without_a_title_still_produces_a_match_that_can_be_shown()
|
|
{
|
|
var match = Read(
|
|
"""{ "data": { "findSceneByFingerprint": [ { "id": "abc", "title": null } ] } }""",
|
|
Flat).ShouldHaveSingleItem();
|
|
|
|
match.Title.ShouldNotBeNullOrWhiteSpace();
|
|
match.Tags.ShouldBeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void Errors_are_raised_even_though_the_server_answered_two_hundred()
|
|
{
|
|
// A rejected key comes back as 200 with an errors array; treating that as "nothing
|
|
// found" would tell the user their video is unknown rather than their key is wrong.
|
|
var payload = """{ "errors": [ { "message": "unauthorized" } ], "data": null }""";
|
|
|
|
Should.Throw<InvalidOperationException>(() => Read(payload, Nested))
|
|
.Message.ShouldBe("unauthorized");
|
|
}
|
|
|
|
[Fact]
|
|
public void A_missing_field_is_reported_apart_from_other_errors_so_the_older_query_gets_a_turn()
|
|
{
|
|
// Verbatim from a live instance that only has the newer query.
|
|
var payload = """
|
|
{
|
|
"errors": [
|
|
{ "message": "Cannot query field \"findSceneByFingerprint\" on type \"Query\". Did you mean \"findScenesBySceneFingerprints\"?" }
|
|
]
|
|
}
|
|
""";
|
|
|
|
var rejected = Should.Throw<Exception>(() => Read(payload, Flat));
|
|
|
|
// Not the type the caller reports as a failure: this one means "try the next query".
|
|
rejected.ShouldNotBeOfType<InvalidOperationException>();
|
|
}
|
|
|
|
private static IReadOnlyList<PLib.Application.Metadata.VideoMetadataMatch> Read(
|
|
string payload,
|
|
StashBoxMetadataProvider.GraphQlQuery query)
|
|
{
|
|
using var document = JsonDocument.Parse(payload);
|
|
return StashBoxMetadataProvider.ReadPayload(document.RootElement, query, "StashDB");
|
|
}
|
|
}
|