Consolidation
This commit is contained in:
@@ -28,7 +28,7 @@ public class StoryServiceTests
|
||||
_viewer = new Mock<IViewerStoryProgressRepository>();
|
||||
// Non-reward tests never exercise the DB/reward path; use a stub InMemory context.
|
||||
var db = StoryServiceTestHelpers.NewInMemoryDb(nameof(SetUp));
|
||||
var rewards = new RewardGrantService(db);
|
||||
var rewards = new RewardGrantService(db, NullLogger<RewardGrantService>.Instance);
|
||||
_service = new StoryService(
|
||||
_master.Object, _viewer.Object,
|
||||
rewards: rewards,
|
||||
@@ -394,6 +394,137 @@ public class StoryServiceTests
|
||||
Assert.That(freshViewer.Currency.RedEther, Is.EqualTo(100UL));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FinishAsync_play_shape_first_clear_grants_card_and_cascades_cosmetic()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
|
||||
const long testCardId = 998_001_010L;
|
||||
const int testSkinId = 998_001_011;
|
||||
const int testStoryId = 998_001_500;
|
||||
|
||||
using (var seedScope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = seedScope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.Cards.Add(new ShadowverseCardEntry { Id = testCardId, Name = "StoryCascadeCard", Rarity = SVSim.Database.Enums.Rarity.Gold });
|
||||
db.LeaderSkins.Add(new LeaderSkinEntry { Id = testSkinId, Name = "StoryCascadeSkin" });
|
||||
db.CardCosmeticRewards.Add(new CardCosmeticReward
|
||||
{
|
||||
CardId = testCardId,
|
||||
Type = SVSim.Database.Enums.CosmeticType.Skin,
|
||||
CosmeticId = testSkinId,
|
||||
Quantity = 1,
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var svc = NewServiceWithSeededViewer(factory, out var scope, out var viewerId);
|
||||
using (scope)
|
||||
{
|
||||
var chapter = Ch(testStoryId, 1, 2, "1", "2");
|
||||
chapter.Rewards.Add(new StoryChapterReward
|
||||
{
|
||||
RewardType = 5, // UserGoodsType.Card
|
||||
RewardDetailId = testCardId,
|
||||
RewardNumber = 1,
|
||||
});
|
||||
_master.Setup(m => m.GetChapterByIdAsync(testStoryId)).ReturnsAsync(chapter);
|
||||
_viewer.Setup(v => v.GetProgressForChaptersAsync(viewerId, It.IsAny<IEnumerable<int>>()))
|
||||
.ReturnsAsync(new Dictionary<int, ViewerStoryProgress>());
|
||||
|
||||
var req = new FinishRequest { StoryId = testStoryId, IsFinish = 1, ClassId = 2 };
|
||||
var resp = await svc.FinishAsync(StoryApiType.Main, req, viewerId: viewerId);
|
||||
|
||||
// reward_list (post-state) gets BOTH the Card entry AND the cascaded Skin entry.
|
||||
Assert.That(resp.RewardList.Any(r => r.RewardType == "5" && r.RewardId == testCardId.ToString()), Is.True,
|
||||
"card reward should appear in reward_list");
|
||||
Assert.That(resp.RewardList.Any(r => r.RewardType == "10" && r.RewardId == testSkinId.ToString()), Is.True,
|
||||
"cascade skin should appear in reward_list");
|
||||
|
||||
// story_reward_list (deltas) only carries the top-level chapter reward.
|
||||
Assert.That(resp.StoryRewardList.Count(r => r.RewardType == "5"), Is.EqualTo(1));
|
||||
Assert.That(resp.StoryRewardList.Any(r => r.RewardType == "10"), Is.False,
|
||||
"cascade cosmetics should not appear in story_reward_list deltas");
|
||||
}
|
||||
|
||||
// Verify viewer ownership was persisted.
|
||||
using var verifyScope = factory.Services.CreateScope();
|
||||
var verifyDb = verifyScope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var viewer = await verifyDb.Viewers
|
||||
.Include(v => v.Cards).ThenInclude(c => c.Card)
|
||||
.Include(v => v.LeaderSkins)
|
||||
.AsSplitQuery()
|
||||
.FirstAsync(v => v.Id == viewerId);
|
||||
Assert.That(viewer.Cards.Any(c => c.Card.Id == testCardId), Is.True);
|
||||
Assert.That(viewer.LeaderSkins.Any(s => s.Id == testSkinId), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FinishAsync_card_grant_for_already_owned_card_increments_not_duplicates()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
|
||||
const long testCardId = 998_002_010L;
|
||||
const int testStoryId = 998_002_500;
|
||||
|
||||
// Pre-seed the card in the catalog AND give the viewer 2 copies of it before the story finish.
|
||||
using (var seedScope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = seedScope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.Cards.Add(new ShadowverseCardEntry
|
||||
{
|
||||
Id = testCardId,
|
||||
Name = "ExistingOwnedCard",
|
||||
Rarity = SVSim.Database.Enums.Rarity.Silver,
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var svc = NewServiceWithSeededViewer(factory, out var scope, out var viewerId);
|
||||
using (scope)
|
||||
{
|
||||
// Seed 2 owned copies of the card under the same viewer used by NewServiceWithSeededViewer.
|
||||
var scopeDb = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var seedViewer = await scopeDb.Viewers
|
||||
.Include(v => v.Cards).ThenInclude(c => c.Card)
|
||||
.FirstAsync(v => v.Id == viewerId);
|
||||
var card = await scopeDb.Cards.FirstAsync(c => c.Id == testCardId);
|
||||
seedViewer.Cards.Add(new OwnedCardEntry { Card = card, Count = 2, IsProtected = false });
|
||||
await scopeDb.SaveChangesAsync();
|
||||
|
||||
// Configure a chapter that grants 1 copy of the same card.
|
||||
var chapter = Ch(testStoryId, 1, 2, "1", "2");
|
||||
chapter.Rewards.Add(new StoryChapterReward
|
||||
{
|
||||
RewardType = 5, // UserGoodsType.Card
|
||||
RewardDetailId = testCardId,
|
||||
RewardNumber = 1,
|
||||
});
|
||||
_master.Setup(m => m.GetChapterByIdAsync(testStoryId)).ReturnsAsync(chapter);
|
||||
_viewer.Setup(v => v.GetProgressForChaptersAsync(viewerId, It.IsAny<IEnumerable<int>>()))
|
||||
.ReturnsAsync(new Dictionary<int, ViewerStoryProgress>());
|
||||
|
||||
var req = new FinishRequest { StoryId = testStoryId, IsFinish = 1, ClassId = 2 };
|
||||
var resp = await svc.FinishAsync(StoryApiType.Main, req, viewerId: viewerId);
|
||||
|
||||
// Post-state count on the wire should be 3 (2 owned + 1 granted).
|
||||
var cardEntry = resp.RewardList.SingleOrDefault(r => r.RewardType == "5" && r.RewardId == testCardId.ToString());
|
||||
Assert.That(cardEntry, Is.Not.Null, "card reward should appear in reward_list");
|
||||
Assert.That(cardEntry!.RewardNum, Is.EqualTo("3"), "post-state count should be incremented, not reset to 1");
|
||||
}
|
||||
|
||||
// Verify the viewer has exactly ONE OwnedCardEntry row for this card, with Count=3.
|
||||
using var verifyScope = factory.Services.CreateScope();
|
||||
var verifyDb = verifyScope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var viewer = await verifyDb.Viewers
|
||||
.Include(v => v.Cards).ThenInclude(c => c.Card)
|
||||
.AsSplitQuery()
|
||||
.FirstAsync(v => v.Id == viewerId);
|
||||
var ownedRows = viewer.Cards.Where(c => c.Card.Id == testCardId).ToList();
|
||||
Assert.That(ownedRows, Has.Count.EqualTo(1), "exactly one OwnedCardEntry row should exist (no duplicates)");
|
||||
Assert.That(ownedRows[0].Count, Is.EqualTo(3));
|
||||
}
|
||||
}
|
||||
|
||||
internal static class StoryServiceTestHelpers
|
||||
|
||||
Reference in New Issue
Block a user