using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RadicadeServer.API.Inventions.V1;
using RadicadeServer.DB;
using RadicadeServer.RecNet.Accounts;
using RadicadeServer.RecNet.Inventions;
using Xunit;
namespace RadicadeServer.Tests;
///
/// The two invention contracts the store reads a player's owned state through.
///
/// `api/inventions/v1/dormskinsfromids` answers the 20230831 client's
/// `HCNIEKLCFOG(IEnumerable<long>) -> Response<IReadOnlyList<long>>`, so the body is a bare array of
/// numbers and never a list of invention objects - an object body makes the client's number reader
/// throw ("expected:'Number Token', actual:'{'") instead of resolving anything.
///
/// `api/inventions/v1/fulllineageowner` answers whether the player owns the lineage, which has to be
/// true for an invention they *bought*: the purchase path writes nothing but the id into
/// PlayerUnlocks.Inventions, and the route used to ignore that list entirely (and overwrote its
/// answer on every id it was asked about, so only the last id ever decided).
///
public class InventionOwnershipTests
{
private const long Buyer = 501;
private const long Creator = 502;
private static RecDBContext NewDb() => new(
TestDbOptions.Create()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options);
private static InventionsV1 Api(RecDBContext db, long playerId) => new(db)
{
ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = new ClaimsPrincipal(new ClaimsIdentity(
[new Claim(ClaimTypes.NameIdentifier, playerId.ToString())], "test"))
}
}
};
private static InventionDB Invention(long id, long creator, bool hidden = false) => new()
{
InventionId = id,
CreatorPlayerId = creator,
Name = "Invention " + id,
IsPublished = true,
HideFromPlayer = hidden,
BlobName = "blob-" + id
};
[Fact]
public async Task DormSkinsFromIds_AnswersABareArrayOfNumbers()
{
await using var db = NewDb();
db.Inventions.AddRange(Invention(11, Creator), Invention(12, Creator), Invention(13, Creator, hidden: true));
await db.SaveChangesAsync();
var result = await Api(db, Buyer).GetDormSkinsFromIds(["11,12,13", "-1"]);
Assert.Equal(new List { 11, 12 }, result);
Assert.Equal("[11,12]", JsonSerializer.Serialize(result));
}
[Fact]
public async Task DormSkinsFromIds_AnswersInTheCallersOrder()
{
await using var db = NewDb();
db.Inventions.AddRange(Invention(61, Creator), Invention(62, Creator));
await db.SaveChangesAsync();
var result = await Api(db, Buyer).GetDormSkinsFromIds(["62", "9999", "61"]);
Assert.Equal(new List { 62, 61 }, result);
}
[Fact]
public async Task FullLineageOwner_IsTrueForABoughtInvention()
{
await using var db = NewDb();
db.Inventions.Add(Invention(21, Creator));
db.Unlocks.Add(new PlayerUnlocks { AccountId = Buyer, Inventions = "[21]" });
await db.SaveChangesAsync();
Assert.True(await Api(db, Buyer).OwnsInventionLineage([21]));
}
[Fact]
public async Task FullLineageOwner_IsFalseForAnInventionThePlayerNeitherMadeNorBought()
{
await using var db = NewDb();
db.Inventions.Add(Invention(31, Creator));
await db.SaveChangesAsync();
Assert.False(await Api(db, Buyer).OwnsInventionLineage([31]));
}
[Fact]
public async Task FullLineageOwner_AccumulatesAcrossTheIdsItIsAsked()
{
await using var db = NewDb();
db.Inventions.AddRange(Invention(41, Creator), Invention(42, Creator));
db.Unlocks.Add(new PlayerUnlocks { AccountId = Buyer, Inventions = "[42]" });
await db.SaveChangesAsync();
Assert.False(await Api(db, Buyer).OwnsInventionLineage([41]));
Assert.True(await Api(db, Buyer).OwnsInventionLineage([41, 42]));
Assert.True(await Api(db, Buyer).OwnsInventionLineage([42, 41]));
}
[Fact]
public async Task FullLineageOwner_IsTrueForTheCreator()
{
await using var db = NewDb();
db.Inventions.Add(Invention(51, Buyer));
await db.SaveChangesAsync();
Assert.True(await Api(db, Buyer).OwnsInventionLineage([51]));
}
}