Prebuilt deck purchasing and fixes
This commit is contained in:
441
SVSim.UnitTests/Controllers/BuildDeckControllerBuyTests.cs
Normal file
441
SVSim.UnitTests/Controllers/BuildDeckControllerBuyTests.cs
Normal file
@@ -0,0 +1,441 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
namespace SVSim.UnitTests.Controllers;
|
||||
|
||||
public class BuildDeckControllerBuyTests
|
||||
{
|
||||
private static StringContent JsonBody(string json) => new(json, Encoding.UTF8, "application/json");
|
||||
|
||||
/// <summary>
|
||||
/// Seeds: series 101 (enabled), one crystal-priced product 1 (intro=500/regular=750, max=3)
|
||||
/// containing 2 distinct cards (10001001 ×2, 10001002 ×1). Caller may set viewer crystals.
|
||||
/// </summary>
|
||||
private static async Task SeedCrystalProduct(SVSimTestFactory f, long viewerId, ulong crystals)
|
||||
{
|
||||
using var scope = f.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.BuildDeckSeries.Add(new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 101, OrderIndex = 22, IsEnabled = true, NameKey = "BDSSN_test", IntroKey = "BDSI_test",
|
||||
Products =
|
||||
{
|
||||
new BuildDeckProductEntry
|
||||
{
|
||||
Id = 1, SeriesId = 101, LeaderId = 1, DeckCode = "pd0101",
|
||||
PurchaseNumMax = 3, IntroPriceCrystal = 500, RegularPriceCrystal = 750,
|
||||
IsEnabled = true,
|
||||
Cards =
|
||||
{
|
||||
new BuildDeckProductCardEntry { CardId = 10001001L, Number = 2, IsSpot = false },
|
||||
new BuildDeckProductCardEntry { CardId = 10001002L, Number = 1, IsSpot = false },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
var v = await db.Viewers.FirstAsync(x => x.Id == viewerId);
|
||||
v.Currency.Crystals = crystals;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static async Task SeedRupyProduct(SVSimTestFactory f, long viewerId, ulong rupees)
|
||||
{
|
||||
using var scope = f.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.BuildDeckSeries.Add(new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 102, OrderIndex = 23, IsEnabled = true, NameKey = "BDSSN_rupy", IntroKey = "BDSI_rupy",
|
||||
Products =
|
||||
{
|
||||
new BuildDeckProductEntry
|
||||
{
|
||||
Id = 10, SeriesId = 102, LeaderId = 2, DeckCode = "pdR",
|
||||
PurchaseNumMax = 1, IntroPriceRupy = 100,
|
||||
IsEnabled = true,
|
||||
Cards = { new BuildDeckProductCardEntry { CardId = 10001001L, Number = 1, IsSpot = false } },
|
||||
},
|
||||
},
|
||||
});
|
||||
var v = await db.Viewers.FirstAsync(x => x.Id == viewerId);
|
||||
v.Currency.Rupees = rupees;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static async Task SeedFreeProduct(SVSimTestFactory f, long viewerId)
|
||||
{
|
||||
using var scope = f.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.BuildDeckSeries.Add(new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 103, OrderIndex = 24, IsEnabled = true, NameKey = "BDSSN_free", IntroKey = "BDSI_free",
|
||||
Products =
|
||||
{
|
||||
new BuildDeckProductEntry
|
||||
{
|
||||
Id = 20, SeriesId = 103, LeaderId = 3, DeckCode = "pdF",
|
||||
PurchaseNumMax = 1, IntroPriceCrystal = 0, IntroPriceRupy = 0,
|
||||
IsEnabled = true,
|
||||
Cards = { new BuildDeckProductCardEntry { CardId = 10001003L, Number = 1, IsSpot = false } },
|
||||
},
|
||||
},
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeds: series 104 + product 100 with a per-buy sleeve reward (id 3000021, a real seeded
|
||||
/// sleeve master row). Used to verify the per-buy rewards path that drops sleeve/emblem/skin
|
||||
/// grants if the controller's Rewards iteration is missing.
|
||||
/// </summary>
|
||||
private static async Task SeedProductWithSleeveReward(SVSimTestFactory f, long viewerId)
|
||||
{
|
||||
using var scope = f.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.BuildDeckSeries.Add(new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 104, OrderIndex = 19, IsEnabled = true, NameKey = "BDSSN_sleeve", IntroKey = "BDSI_sleeve",
|
||||
Products =
|
||||
{
|
||||
new BuildDeckProductEntry
|
||||
{
|
||||
Id = 100, SeriesId = 104, LeaderId = 1, DeckCode = "pd0104",
|
||||
PurchaseNumMax = 1, IntroPriceCrystal = 0, IntroPriceRupy = 0, // free
|
||||
IsEnabled = true,
|
||||
Cards = { new BuildDeckProductCardEntry { CardId = 10001001L, Number = 1, IsSpot = false } },
|
||||
Rewards =
|
||||
{
|
||||
new BuildDeckProductRewardEntry
|
||||
{
|
||||
RewardIndex = 1, RewardType = 6 /* Sleeve */,
|
||||
RewardDetailId = 3000021, RewardNumber = 1, MessageId = 51004,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Buy_grants_per_buy_sleeve_reward_to_viewer_collection()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedProductWithSleeveReward(factory, viewerId);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":100,"sales_type":0}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var v = await db.Viewers.Include(x => x.Sleeves).FirstAsync(x => x.Id == viewerId);
|
||||
Assert.That(v.Sleeves.Any(s => s.Id == 3000021), Is.True,
|
||||
"per-buy sleeve reward must land in viewer's owned collection");
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var entries = doc.RootElement.GetProperty("reward_list");
|
||||
bool foundSleeve = false;
|
||||
for (int i = 0; i < entries.GetArrayLength(); i++)
|
||||
{
|
||||
var e = entries[i];
|
||||
if (e.GetProperty("reward_type").GetInt32() == 6 && e.GetProperty("reward_id").GetInt64() == 3000021)
|
||||
foundSleeve = true;
|
||||
}
|
||||
Assert.That(foundSleeve, Is.True, "reward_list must include the granted sleeve entry");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Crystal_buy_debits_intro_price_and_grants_cards()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedCrystalProduct(factory, viewerId, crystals: 1000);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":1,"sales_type":1}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var v = await db.Viewers
|
||||
.Include(x => x.Cards).ThenInclude(c => c.Card)
|
||||
.Include(x => x.BuildDeckPurchases)
|
||||
.FirstAsync(x => x.Id == viewerId);
|
||||
|
||||
Assert.That(v.Currency.Crystals, Is.EqualTo(500UL), "1000 - 500 intro");
|
||||
Assert.That(v.Cards.Sum(c => c.Count), Is.EqualTo(3), "2 + 1 cards granted");
|
||||
Assert.That(v.BuildDeckPurchases.Single(p => p.ProductId == 1).PurchaseCount, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Crystal_buy_emits_post_state_total_for_crystals()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedCrystalProduct(factory, viewerId, crystals: 1000);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":1,"sales_type":1}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var rewardList = doc.RootElement.GetProperty("reward_list");
|
||||
bool foundCrystals = false;
|
||||
for (int i = 0; i < rewardList.GetArrayLength(); i++)
|
||||
{
|
||||
var e = rewardList[i];
|
||||
if (e.GetProperty("reward_type").GetInt32() == 2)
|
||||
{
|
||||
Assert.That(e.GetProperty("reward_num").GetInt32(), Is.EqualTo(500), "post-state crystals total");
|
||||
foundCrystals = true;
|
||||
}
|
||||
}
|
||||
Assert.That(foundCrystals, Is.True, "crystal entry must be in reward_list");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Returns_BadRequest_when_insufficient_crystals()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedCrystalProduct(factory, viewerId, crystals: 100);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":1,"sales_type":1}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Returns_BadRequest_for_disabled_product()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.BuildDeckSeries.Add(new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 101, OrderIndex = 22, IsEnabled = true, NameKey = "x", IntroKey = "x",
|
||||
Products =
|
||||
{
|
||||
new BuildDeckProductEntry
|
||||
{
|
||||
Id = 999, SeriesId = 101, PurchaseNumMax = 1, IntroPriceCrystal = 500,
|
||||
IsEnabled = false,
|
||||
},
|
||||
},
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":999,"sales_type":1}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Returns_BadRequest_when_purchase_limit_reached()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedCrystalProduct(factory, viewerId, crystals: 10000);
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var v = await db.Viewers.Include(x => x.BuildDeckPurchases).FirstAsync(x => x.Id == viewerId);
|
||||
v.BuildDeckPurchases.Add(new ViewerBuildDeckProductPurchase { ProductId = 1, PurchaseCount = 3 });
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":1,"sales_type":1}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Returns_BadRequest_when_paying_in_unsupported_currency_for_product()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedCrystalProduct(factory, viewerId, crystals: 1000); // crystal-only product
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
// sales_type=2 (rupy) against a crystal-only product
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":1,"sales_type":2}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Returns_501_for_ticket_sales_type()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedCrystalProduct(factory, viewerId, crystals: 1000);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":1,"sales_type":3}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
Assert.That((int)response.StatusCode, Is.EqualTo(501));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Rupy_buy_debits_and_grants()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedRupyProduct(factory, viewerId, rupees: 200);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":10,"sales_type":2}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var v = await db.Viewers.Include(x => x.Cards).FirstAsync(x => x.Id == viewerId);
|
||||
Assert.That(v.Currency.Rupees, Is.EqualTo(100UL));
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var entries = doc.RootElement.GetProperty("reward_list");
|
||||
bool foundRupy = false;
|
||||
for (int i = 0; i < entries.GetArrayLength(); i++)
|
||||
{
|
||||
if (entries[i].GetProperty("reward_type").GetInt32() == 9)
|
||||
{
|
||||
Assert.That(entries[i].GetProperty("reward_num").GetInt32(), Is.EqualTo(100));
|
||||
foundRupy = true;
|
||||
}
|
||||
}
|
||||
Assert.That(foundRupy, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Free_buy_grants_cards_without_currency_entry()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedFreeProduct(factory, viewerId);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":20,"sales_type":0}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var entries = doc.RootElement.GetProperty("reward_list");
|
||||
for (int i = 0; i < entries.GetArrayLength(); i++)
|
||||
{
|
||||
int t = entries[i].GetProperty("reward_type").GetInt32();
|
||||
Assert.That(t, Is.Not.EqualTo(2), "free buy must not emit Crystal entry");
|
||||
Assert.That(t, Is.Not.EqualTo(9), "free buy must not emit Rupy entry");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Free_buy_against_nonfree_product_returns_BadRequest()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedCrystalProduct(factory, viewerId, crystals: 1000);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":1,"sales_type":0}""";
|
||||
var response = await client.PostAsync("/build_deck/buy", JsonBody(json));
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Buy_emits_newly_unlocked_series_tier_rewards()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.BuildDeckSeries.Add(new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 105, OrderIndex = 18, IsEnabled = true, NameKey = "x", IntroKey = "x",
|
||||
SeriesRewards =
|
||||
{
|
||||
// Tier 1: one card reward, unlocked on the 1st series purchase.
|
||||
new BuildDeckSeriesRewardEntry
|
||||
{
|
||||
TierIndex = 1, ItemIndex = 0, RewardType = 5,
|
||||
RewardDetailId = 10001001L, RewardNumber = 1, MessageId = 51004,
|
||||
},
|
||||
// Tier 2: one card reward, unlocked on the 2nd series purchase.
|
||||
new BuildDeckSeriesRewardEntry
|
||||
{
|
||||
TierIndex = 2, ItemIndex = 0, RewardType = 5,
|
||||
RewardDetailId = 10001002L, RewardNumber = 1, MessageId = 51004,
|
||||
},
|
||||
},
|
||||
Products =
|
||||
{
|
||||
new BuildDeckProductEntry
|
||||
{
|
||||
Id = 501, SeriesId = 105, LeaderId = 1, DeckCode = "pd0501",
|
||||
PurchaseNumMax = 3, IntroPriceCrystal = 0, RegularPriceCrystal = 0,
|
||||
IntroPriceRupy = 0, RegularPriceRupy = 0, IsEnabled = true,
|
||||
Cards = { new BuildDeckProductCardEntry { CardId = 10001003L, Number = 1, IsSpot = false } },
|
||||
},
|
||||
new BuildDeckProductEntry
|
||||
{
|
||||
Id = 502, SeriesId = 105, LeaderId = 2, DeckCode = "pd0502",
|
||||
PurchaseNumMax = 3, IntroPriceCrystal = 0, RegularPriceCrystal = 0,
|
||||
IntroPriceRupy = 0, RegularPriceRupy = 0, IsEnabled = true,
|
||||
Cards = { new BuildDeckProductCardEntry { CardId = 10001003L, Number = 1, IsSpot = false } },
|
||||
},
|
||||
},
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
|
||||
// 1st series purchase (product 501) should emit tier 1 only.
|
||||
var r1 = await client.PostAsync("/build_deck/buy",
|
||||
JsonBody("""{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":501,"sales_type":0}"""));
|
||||
Assert.That(r1.StatusCode, Is.EqualTo(HttpStatusCode.OK), await r1.Content.ReadAsStringAsync());
|
||||
|
||||
using (var doc = JsonDocument.Parse(await r1.Content.ReadAsStringAsync()))
|
||||
{
|
||||
var tiers = doc.RootElement.GetProperty("series_rewards");
|
||||
Assert.That(tiers.GetArrayLength(), Is.EqualTo(1), "only tier 1 newly crossed");
|
||||
Assert.That(tiers[0].GetProperty("reward_detail_id").GetInt64(), Is.EqualTo(10001001L));
|
||||
}
|
||||
|
||||
// 2nd series purchase (product 502) should emit tier 2 only.
|
||||
var r2 = await client.PostAsync("/build_deck/buy",
|
||||
JsonBody("""{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":502,"sales_type":0}"""));
|
||||
using (var doc = JsonDocument.Parse(await r2.Content.ReadAsStringAsync()))
|
||||
{
|
||||
var tiers = doc.RootElement.GetProperty("series_rewards");
|
||||
Assert.That(tiers.GetArrayLength(), Is.EqualTo(1));
|
||||
Assert.That(tiers[0].GetProperty("reward_detail_id").GetInt64(), Is.EqualTo(10001002L));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
namespace SVSim.UnitTests.Controllers;
|
||||
|
||||
public class BuildDeckControllerGetPurchaseCountTests
|
||||
{
|
||||
private static StringContent JsonBody(string json) => new(json, Encoding.UTF8, "application/json");
|
||||
|
||||
private static async Task SeedEnabledProduct(SVSimTestFactory f, int productId, int max)
|
||||
{
|
||||
using var scope = f.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.BuildDeckSeries.Add(new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 101, OrderIndex = 22, IsEnabled = true, NameKey = "BDSSN_test", IntroKey = "BDSI_test",
|
||||
});
|
||||
db.BuildDeckProducts.Add(new BuildDeckProductEntry
|
||||
{
|
||||
Id = productId, SeriesId = 101, IsEnabled = true,
|
||||
PurchaseNumMax = max, IntroPriceCrystal = 500,
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Returns_zero_current_and_max_for_unbought_product()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedEnabledProduct(factory, productId: 201, max: 3);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":201}""";
|
||||
var response = await client.PostAsync("/build_deck/get_purchase_count", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
Assert.That(doc.RootElement.GetProperty("purchase_num_current").GetInt32(), Is.EqualTo(0));
|
||||
Assert.That(doc.RootElement.GetProperty("purchase_num_max").GetInt32(), Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Returns_NotFound_for_unknown_product()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","product_id":99999}""";
|
||||
var response = await client.PostAsync("/build_deck/get_purchase_count", JsonBody(json));
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
|
||||
}
|
||||
}
|
||||
144
SVSim.UnitTests/Controllers/BuildDeckControllerInfoTests.cs
Normal file
144
SVSim.UnitTests/Controllers/BuildDeckControllerInfoTests.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
namespace SVSim.UnitTests.Controllers;
|
||||
|
||||
public class BuildDeckControllerInfoTests
|
||||
{
|
||||
private static StringContent JsonBody(string json) => new(json, Encoding.UTF8, "application/json");
|
||||
|
||||
private static async Task SeedTwoSeries(SVSimTestFactory f)
|
||||
{
|
||||
using var scope = f.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
var seriesA = new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 101, OrderIndex = 22, IsEnabled = true, IsNew = false,
|
||||
NameKey = "BDSSN_A", IntroKey = "BDSI_A",
|
||||
Products =
|
||||
{
|
||||
new BuildDeckProductEntry
|
||||
{
|
||||
Id = 1, SeriesId = 101, LeaderId = 1, DeckCode = "pd0101",
|
||||
ProductNameKey = "BDPN_A_elf", FeaturedCardId = 100,
|
||||
PurchaseNumMax = 3, IntroPriceCrystal = 500, RegularPriceCrystal = 750,
|
||||
IsEnabled = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
var seriesB = new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 107, OrderIndex = 15, IsEnabled = true, IsNew = false,
|
||||
NameKey = "BDSSN_B", IntroKey = "BDSI_B",
|
||||
Products =
|
||||
{
|
||||
new BuildDeckProductEntry
|
||||
{
|
||||
Id = 701, SeriesId = 107, LeaderId = 1, DeckCode = "pd0107",
|
||||
ProductNameKey = "BDPN_B_elf", FeaturedCardId = 200,
|
||||
PurchaseNumMax = 1, IntroPriceCrystal = 1200,
|
||||
IsEnabled = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
var disabled = new BuildDeckSeriesEntry
|
||||
{
|
||||
Id = 10100, OrderIndex = 999, IsEnabled = false, NameKey = "BDSSN_TEMP", IntroKey = "BDSI_TEMP",
|
||||
};
|
||||
db.BuildDeckSeries.AddRange(seriesA, seriesB, disabled);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Returns_only_enabled_series_sorted_by_order_index_desc()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedTwoSeries(factory);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","add_series_id":0}""";
|
||||
var response = await client.PostAsync("/build_deck/info", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var list = doc.RootElement; // controller returns a bare array — `data` IS the series list
|
||||
Assert.That(list.GetArrayLength(), Is.EqualTo(2));
|
||||
Assert.That(list[0].GetProperty("series_id").GetInt32(), Is.EqualTo(101), "OrderIndex 22 sorts first");
|
||||
Assert.That(list[1].GetProperty("series_id").GetInt32(), Is.EqualTo(107));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Filters_to_single_series_when_add_series_id_nonzero()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedTwoSeries(factory);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","add_series_id":107}""";
|
||||
var response = await client.PostAsync("/build_deck/info", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var list = doc.RootElement;
|
||||
Assert.That(list.GetArrayLength(), Is.EqualTo(1));
|
||||
Assert.That(list[0].GetProperty("series_id").GetInt32(), Is.EqualTo(107));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Emits_intro_price_and_is_first_price_true_for_unbought_max3_product()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedTwoSeries(factory);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","add_series_id":101}""";
|
||||
var response = await client.PostAsync("/build_deck/info", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var product = doc.RootElement[0].GetProperty("products")[0];
|
||||
Assert.That(product.GetProperty("is_first_price").GetBoolean(), Is.True);
|
||||
Assert.That(product.GetProperty("price_crystal").GetInt32(), Is.EqualTo(500));
|
||||
Assert.That(product.GetProperty("purchase_num_current").GetInt32(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Emits_regular_price_after_first_purchase_recorded()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedTwoSeries(factory);
|
||||
|
||||
// Record a purchase directly to simulate post-buy state.
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var v = await db.Viewers.Include(x => x.BuildDeckPurchases).FirstAsync(x => x.Id == viewerId);
|
||||
v.BuildDeckPurchases.Add(new ViewerBuildDeckProductPurchase { ProductId = 1, PurchaseCount = 1 });
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","add_series_id":101}""";
|
||||
var response = await client.PostAsync("/build_deck/info", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var product = doc.RootElement[0].GetProperty("products")[0];
|
||||
Assert.That(product.GetProperty("is_first_price").GetBoolean(), Is.False);
|
||||
Assert.That(product.GetProperty("price_crystal").GetInt32(), Is.EqualTo(750));
|
||||
Assert.That(product.GetProperty("purchase_num_current").GetInt32(), Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
126
SVSim.UnitTests/Controllers/LeaderSkinControllerTests.cs
Normal file
126
SVSim.UnitTests/Controllers/LeaderSkinControllerTests.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
namespace SVSim.UnitTests.Controllers;
|
||||
|
||||
public class LeaderSkinControllerTests
|
||||
{
|
||||
private static StringContent JsonBody(string json) => new(json, Encoding.UTF8, "application/json");
|
||||
|
||||
/// <summary>Adds a class-4 leader skin (id 104, "Forte") to the catalog and to the viewer's owned list.</summary>
|
||||
private static async Task SeedOwnedClass4Skin(SVSimTestFactory f, long viewerId, int skinId = 104)
|
||||
{
|
||||
using var scope = f.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var skin = await db.LeaderSkins.FindAsync(skinId);
|
||||
if (skin is null)
|
||||
{
|
||||
skin = new LeaderSkinEntry { Id = skinId, Name = "Forte", ClassId = 4 };
|
||||
db.LeaderSkins.Add(skin);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
var viewer = await db.Viewers.Include(v => v.LeaderSkins).FirstAsync(v => v.Id == viewerId);
|
||||
if (viewer.LeaderSkins.All(s => s.Id != skinId)) viewer.LeaderSkins.Add(skin);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Set_updates_viewer_class_leader_skin()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedOwnedClass4Skin(factory, viewerId, skinId: 104);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","class_id":4,"leader_skin_id":104,"is_random_leader_skin":false,"leader_skin_id_list":[]}""";
|
||||
var response = await client.PostAsync("/leader_skin/set", JsonBody(json));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var viewer = await db.Viewers
|
||||
.Include(v => v.Classes).ThenInclude(c => c.LeaderSkin)
|
||||
.Include(v => v.Classes).ThenInclude(c => c.Class)
|
||||
.FirstAsync(v => v.Id == viewerId);
|
||||
var class4 = viewer.Classes.Single(c => c.Class.Id == 4);
|
||||
Assert.That(class4.LeaderSkin.Id, Is.EqualTo(104));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Set_is_reflected_in_subsequent_deck_info_response()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
await SeedOwnedClass4Skin(factory, viewerId, skinId: 104);
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
|
||||
// Switch class 4 leader to skin 104
|
||||
await client.PostAsync("/leader_skin/set",
|
||||
JsonBody("""{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","class_id":4,"leader_skin_id":104,"is_random_leader_skin":false,"leader_skin_id_list":[]}"""));
|
||||
|
||||
// /deck/info should now report class 4 with leader_skin_id=104
|
||||
var resp = await client.PostAsync("/deck/info",
|
||||
JsonBody("""{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","deck_format":0}"""));
|
||||
var body = await resp.Content.ReadAsStringAsync();
|
||||
Assert.That(resp.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
var settings = doc.RootElement.GetProperty("user_leader_skin_setting_list");
|
||||
Assert.That(settings.TryGetProperty("4", out var class4Setting), Is.True, "class 4 entry must be present");
|
||||
Assert.That(class4Setting.GetProperty("leader_skin_id").GetInt32(), Is.EqualTo(104));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Set_rejects_skin_viewer_doesnt_own()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
// Skin 104 (Forte) is in the seeded leaderskins.csv catalog but a fresh viewer only owns
|
||||
// the 8 class default skins — confirm 104 isn't in viewer.LeaderSkins, then call /set.
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var skin = await db.LeaderSkins.FindAsync(104);
|
||||
Assert.That(skin, Is.Not.Null, "leaderskins.csv fixture should include skin 104");
|
||||
var viewer = await db.Viewers.Include(v => v.LeaderSkins).FirstAsync(v => v.Id == viewerId);
|
||||
Assert.That(viewer.LeaderSkins.Any(s => s.Id == 104), Is.False, "fresh viewer must not own skin 104");
|
||||
}
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","class_id":4,"leader_skin_id":104,"is_random_leader_skin":false,"leader_skin_id_list":[]}""";
|
||||
var resp = await client.PostAsync("/leader_skin/set", JsonBody(json));
|
||||
Assert.That(resp.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), await resp.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Set_rejects_skin_for_wrong_class()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
// Skin 104 is class 4 — try to assign it to class 6
|
||||
await SeedOwnedClass4Skin(factory, viewerId, skinId: 104);
|
||||
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","class_id":6,"leader_skin_id":104,"is_random_leader_skin":false,"leader_skin_id_list":[]}""";
|
||||
var resp = await client.PostAsync("/leader_skin/set", JsonBody(json));
|
||||
Assert.That(resp.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Set_returns_501_for_random_leader_skin_mode()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
var json = """{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","class_id":4,"leader_skin_id":0,"is_random_leader_skin":true,"leader_skin_id_list":[4,104]}""";
|
||||
var resp = await client.PostAsync("/leader_skin/set", JsonBody(json));
|
||||
Assert.That((int)resp.StatusCode, Is.EqualTo(501));
|
||||
}
|
||||
}
|
||||
110
SVSim.UnitTests/Importers/BuildDeckImporterTests.cs
Normal file
110
SVSim.UnitTests/Importers/BuildDeckImporterTests.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Bootstrap.Importers;
|
||||
using SVSim.Database;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
namespace SVSim.UnitTests.Importers;
|
||||
|
||||
public class BuildDeckImporterTests
|
||||
{
|
||||
private static string DataDir => Path.Combine(AppContext.BaseDirectory, "Data");
|
||||
|
||||
[Test]
|
||||
public async Task ImportsAll22Series_with_22_disabled_until_catalog_enables()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
await new BuildDeckImporter().ImportSeriesAsync(db, DataDir);
|
||||
|
||||
var series = await db.BuildDeckSeries.OrderBy(s => s.Id).ToListAsync();
|
||||
Assert.That(series.Count, Is.EqualTo(22));
|
||||
Assert.That(series.All(s => !s.IsEnabled), Is.True, "all series disabled until catalog importer runs");
|
||||
Assert.That(series.Any(s => s.NameKey.StartsWith("BDSSN_")), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ImportPackage_creates_stub_products_with_inferred_series_and_full_card_lists()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
await new BuildDeckImporter().ImportSeriesAsync(db, DataDir);
|
||||
await new BuildDeckImporter().ImportPackageAsync(db, DataDir);
|
||||
|
||||
var products = await db.BuildDeckProducts.Include(p => p.Cards).ToListAsync();
|
||||
Assert.That(products.Count, Is.EqualTo(112), "stubs for all 112 products");
|
||||
Assert.That(products.All(p => !p.IsEnabled), Is.True, "stubs are disabled until catalog enables");
|
||||
Assert.That(products.All(p => p.Cards.Sum(c => c.Number) == 40), Is.True, "every product is a 40-card deck");
|
||||
|
||||
// Spot-check a known mapping: product 1 -> series 101 via the InferSeriesId helper.
|
||||
var p1 = products.Single(p => p.Id == 1);
|
||||
Assert.That(p1.SeriesId, Is.EqualTo(101));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Importer_is_idempotent_on_rerun()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
var importer = new BuildDeckImporter();
|
||||
await importer.ImportSeriesAsync(db, DataDir);
|
||||
await importer.ImportPackageAsync(db, DataDir);
|
||||
await importer.ImportSeriesAsync(db, DataDir);
|
||||
await importer.ImportPackageAsync(db, DataDir);
|
||||
|
||||
Assert.That(await db.BuildDeckSeries.CountAsync(), Is.EqualTo(22));
|
||||
Assert.That(await db.BuildDeckProducts.CountAsync(), Is.EqualTo(112));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ImportCatalog_enriches_7_captured_series_with_prices_and_tiers()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
var importer = new BuildDeckImporter();
|
||||
await importer.ImportSeriesAsync(db, DataDir);
|
||||
await importer.ImportCatalogAsync(db, Path.Combine(DataDir, "prod-captures"));
|
||||
await importer.ImportPackageAsync(db, DataDir);
|
||||
|
||||
// Series 101 (Set 1) should be enabled and order_id=22 from capture
|
||||
var s101 = await db.BuildDeckSeries
|
||||
.Include(s => s.Products).ThenInclude(p => p.Cards)
|
||||
.Include(s => s.Products).ThenInclude(p => p.Rewards)
|
||||
.Include(s => s.SeriesRewards)
|
||||
.FirstAsync(s => s.Id == 101);
|
||||
Assert.That(s101.IsEnabled, Is.True);
|
||||
Assert.That(s101.OrderIndex, Is.EqualTo(22));
|
||||
Assert.That(s101.Products.Count, Is.EqualTo(7), "Set 1 has 7 products (no Nemesis)");
|
||||
|
||||
// Set 1 products: max=3, intro=500 backfilled from siblings, regular=750 backfilled from siblings
|
||||
var product1 = s101.Products.Single(p => p.Id == 1);
|
||||
Assert.That(product1.IsEnabled, Is.True);
|
||||
Assert.That(product1.PurchaseNumMax, Is.EqualTo(3));
|
||||
Assert.That(product1.IntroPriceCrystal, Is.EqualTo(500));
|
||||
Assert.That(product1.RegularPriceCrystal, Is.EqualTo(750));
|
||||
|
||||
// Series 107 (Set 7) products: max=1, intro=1200, regular=null
|
||||
var s107 = await db.BuildDeckSeries
|
||||
.Include(s => s.Products)
|
||||
.FirstAsync(s => s.Id == 107);
|
||||
Assert.That(s107.Products.All(p => p.PurchaseNumMax == 1), Is.True);
|
||||
Assert.That(s107.Products.All(p => p.IntroPriceCrystal == 1200), Is.True);
|
||||
Assert.That(s107.Products.All(p => p.RegularPriceCrystal == null), Is.True);
|
||||
|
||||
// Series 105 should have populated series-reward tiers (from the capture)
|
||||
var s105 = await db.BuildDeckSeries.Include(s => s.SeriesRewards).FirstAsync(s => s.Id == 105);
|
||||
Assert.That(s105.SeriesRewards.Count, Is.GreaterThan(0), "Set 5 has series-reward tiers");
|
||||
|
||||
// Series 10100 (Temporary Deck) should still be disabled — not in capture
|
||||
var sTemp = await db.BuildDeckSeries.FirstAsync(s => s.Id == 10100);
|
||||
Assert.That(sTemp.IsEnabled, Is.False);
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,17 @@
|
||||
<Content Include="Story\Fixtures\*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<!-- BuildDeckImporter reads CSVs from a build-deck/ subdirectory; the top-level Data\*.csv
|
||||
glob only covers the root. Link both files explicitly so they land in the same relative
|
||||
path the importer uses at runtime. -->
|
||||
<Content Include="..\SVSim.Bootstrap\Data\build-deck\build_deck_package_master.csv">
|
||||
<Link>Data\build-deck\build_deck_package_master.csv</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="..\SVSim.Bootstrap\Data\build-deck\build_deck_series_master.csv">
|
||||
<Link>Data\build-deck\build_deck_series_master.csv</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user