mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
using System.Text.Json;
|
||||
using RadicadeServer.API.Storefront.V3;
|
||||
using RadicadeServer.RecNet.Avatar;
|
||||
using RadicadeServer.RecNet.Enums.Players;
|
||||
using RadicadeServer.RecNet.Store;
|
||||
using RadicadeServer.RecNet.Storefront;
|
||||
using Xunit;
|
||||
|
||||
namespace RadicadeServer.Tests;
|
||||
|
||||
public class StorefrontRewardConfigTests
|
||||
{
|
||||
[Fact]
|
||||
public void QuestStorefrontProjectionHidesOnlyUnsupportedAvatarOnlyItems()
|
||||
{
|
||||
var supported = Purchasable(10, "supported,");
|
||||
var unsupported = Purchasable(11, "not-in-quest-build,");
|
||||
var mixedReward = Purchasable(12, "also-unsupported,");
|
||||
var originalGift = mixedReward.GiftDrops[0];
|
||||
originalGift.ConsumableItemDesc = "consumable";
|
||||
originalGift.Currency = 25;
|
||||
|
||||
var projected = StorefrontsV3.ProjectStoreItemsForClient(
|
||||
[supported, unsupported, mixedReward],
|
||||
((int)Platforms.Standalone).ToString(),
|
||||
description => description == "supported,");
|
||||
|
||||
Assert.Equal(new[] { 10, 12 }, projected.Select(item => item.PurchasableItemId));
|
||||
Assert.Equal(250, Assert.Single(projected[0].Prices).Price);
|
||||
Assert.Equal(string.Empty, projected[1].GiftDrops[0].AvatarItemDesc);
|
||||
Assert.Equal("consumable", projected[1].GiftDrops[0].ConsumableItemDesc);
|
||||
Assert.Equal(25, projected[1].GiftDrops[0].Currency);
|
||||
|
||||
// The response is a projection only; purchase resolution still sees the original gifts.
|
||||
Assert.Equal("also-unsupported,", originalGift.AvatarItemDesc);
|
||||
Assert.Equal("not-in-quest-build,", unsupported.GiftDrops[0].AvatarItemDesc);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QuestStorefrontProjectionLeavesOtherPlatformsAndResolvableGiftDropsAlone()
|
||||
{
|
||||
var item = Purchasable(20, "supported,");
|
||||
item.GiftDrops.Add(new Gift { GiftDropId = 201, AvatarItemDesc = "unsupported," });
|
||||
|
||||
var nonStandalone = StorefrontsV3.ProjectStoreItemsForClient(
|
||||
[item],
|
||||
((int)Platforms.Oculus).ToString(),
|
||||
_ => false);
|
||||
var standalone = StorefrontsV3.ProjectStoreItemsForClient(
|
||||
[item],
|
||||
((int)Platforms.Standalone).ToString(),
|
||||
description => description == "supported,");
|
||||
|
||||
Assert.Same(item, Assert.Single(nonStandalone));
|
||||
Assert.Single(Assert.Single(standalone).GiftDrops);
|
||||
Assert.Equal("supported,", standalone[0].GiftDrops[0].AvatarItemDesc);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QuestStorefrontProjectionFiltersCapturedCatalogWithoutChangingPurchaseLookup()
|
||||
{
|
||||
using var lookup = JsonDocument.Parse(File.ReadAllText(
|
||||
Path.Combine(AppContext.BaseDirectory, "Fixtures", "avatar-item-lookup.json")));
|
||||
var resolvable = lookup.RootElement.GetProperty("descriptions")
|
||||
.EnumerateArray()
|
||||
.Select(value => AvatarItemDescription.Normalize(value.GetString()))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var canonicalItems = StorefrontCatalog.GetItems((long)StorefrontTypes.Watch);
|
||||
var supportedItem = canonicalItems.First(item =>
|
||||
resolvable.Contains(AvatarItemDescription.Normalize(item.GiftDrop.AvatarItemDesc)));
|
||||
var unsupportedItem = canonicalItems.First(item =>
|
||||
!string.IsNullOrWhiteSpace(item.GiftDrop.AvatarItemDesc)
|
||||
&& !resolvable.Contains(AvatarItemDescription.Normalize(item.GiftDrop.AvatarItemDesc)));
|
||||
var projected = StorefrontsV3.ProjectStoreItemsForClient(
|
||||
canonicalItems,
|
||||
((int)Platforms.Standalone).ToString(),
|
||||
description => resolvable.Contains(AvatarItemDescription.Normalize(description)));
|
||||
|
||||
Assert.Contains(projected, item => item.PurchasableItemId == supportedItem.PurchasableItemId);
|
||||
Assert.DoesNotContain(projected, item => item.PurchasableItemId == unsupportedItem.PurchasableItemId);
|
||||
Assert.All(projected.SelectMany(item => item.GiftDrops)
|
||||
.Where(gift => !string.IsNullOrWhiteSpace(gift.AvatarItemDesc)),
|
||||
gift => Assert.Contains(
|
||||
AvatarItemDescription.Normalize(gift.AvatarItemDesc), resolvable));
|
||||
|
||||
var canonicalUnsupported = StorefrontCatalog.FindItem(
|
||||
(long)StorefrontTypes.Watch, unsupportedItem.PurchasableItemId);
|
||||
Assert.NotNull(canonicalUnsupported);
|
||||
Assert.Equal(unsupportedItem.GiftDrop.AvatarItemDesc, canonicalUnsupported.GiftDrop.AvatarItemDesc);
|
||||
}
|
||||
|
||||
private static PurchasableGiftDrop Purchasable(int itemId, string avatarItemDesc) => new()
|
||||
{
|
||||
PurchasableItemId = itemId,
|
||||
Prices = [new PurchasablePrice { Price = 250 }],
|
||||
GiftDrops = [new Gift { GiftDropId = itemId + 100, AvatarItemDesc = avatarItemDesc }]
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void PartialMultiplierSerializesAsNumber()
|
||||
{
|
||||
var json = JsonSerializer.SerializeToElement(new StorefrontBalanceAddConfigDTO());
|
||||
Assert.Equal(JsonValueKind.Number, json.GetProperty("MaxPartialMultiplier").ValueKind);
|
||||
Assert.Equal(1f, json.GetProperty("MaxPartialMultiplier").GetSingle());
|
||||
Assert.Equal(JsonValueKind.False, json.GetProperty("IgnorePartialMultiplier").ValueKind);
|
||||
Assert.Equal(JsonValueKind.False, json.GetProperty("BalanceInGiftBox").ValueKind);
|
||||
}
|
||||
|
||||
// The client asks for a storefront by number and the MVC binder drops any id that is not a
|
||||
// member of StorefrontTypes down to None, so those ids have to stay defined and keep the
|
||||
// exact numbers the 20230831 build sends (RecNet.Runtime's StorefrontTypes).
|
||||
[Theory]
|
||||
[InlineData(StorefrontTypes.LaserTag, 1)]
|
||||
[InlineData(StorefrontTypes.Watch, 3)]
|
||||
[InlineData(StorefrontTypes.Quest_GoldenTrophy, 102)]
|
||||
[InlineData(StorefrontTypes.Quest_CrimsonCauldron, 103)]
|
||||
[InlineData(StorefrontTypes.Paintball_SunsetDriveIn, 406)]
|
||||
[InlineData(StorefrontTypes.StuntRunner, 600)]
|
||||
[InlineData(StorefrontTypes.DormMirror, 700)]
|
||||
[InlineData(StorefrontTypes.InventionStore, 800)]
|
||||
[InlineData(StorefrontTypes.Player_Profile, 1000)]
|
||||
[InlineData(StorefrontTypes.PopUpShop_1, 1700)]
|
||||
[InlineData(StorefrontTypes.CustomAvatarItems, 1800)]
|
||||
[InlineData(StorefrontTypes.ProgressionEventPurchasableXpBoosts, 1900)]
|
||||
[InlineData(StorefrontTypes.PopUpShop_ProgressionEvent_1, 2000)]
|
||||
[InlineData(StorefrontTypes.PopUpShop_ProgressionEvent_5, 2004)]
|
||||
public void GameStorefrontIdStaysBound(StorefrontTypes type, int clientId)
|
||||
{
|
||||
Assert.Equal(clientId, (int)type);
|
||||
Assert.True(
|
||||
Enum.IsDefined(typeof(StorefrontTypes), clientId),
|
||||
$"storefront {clientId} must stay a defined StorefrontTypes member or the route binder serves it as None");
|
||||
}
|
||||
|
||||
// Every storefront the client knows carries a capture (RadicadeServer-Config/storefronts/sf{id}.json,
|
||||
// embedded into the server at build), so the catalog covers the whole game and every storefront is
|
||||
// editable there. A storefront with no items of its own still serves the shared storefront 0 list.
|
||||
[Fact]
|
||||
public void EveryGameStorefrontHasACapture()
|
||||
{
|
||||
var captured = StorefrontCatalog.ReadCaptures()
|
||||
.Select(capture => capture.StorefrontId)
|
||||
.ToHashSet();
|
||||
|
||||
var storefronts = Enum.GetValues<StorefrontTypes>()
|
||||
.Where(type => type != StorefrontTypes.None)
|
||||
.Select(type => (long)type)
|
||||
.ToList();
|
||||
|
||||
Assert.Equal(44, storefronts.Count);
|
||||
Assert.Empty(storefronts.Where(id => !captured.Contains(id)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user