using System.Net; using System.Net.Http.Json; using System.Text.Json; using NeonSprawl.Server.Game.Quests; using Xunit; namespace NeonSprawl.Server.Tests.Game.Quests; public class QuestDefinitionsWorldApiTests { [Fact] public async Task GetQuestDefinitions_ShouldReturnSchemaV1_WithFrozenQuestsInIdOrder() { // Arrange await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); // Act var response = await client.GetAsync("/game/world/quest-definitions"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); Assert.NotNull(body); Assert.Equal(QuestDefinitionsListResponse.CurrentSchemaVersion, body!.SchemaVersion); Assert.NotNull(body.Quests); Assert.Equal(PrototypeE7M1QuestCatalogRules.ExpectedQuestIds.Count, body.Quests.Count); Assert.Equal( PrototypeE7M1QuestCatalogRules.ExpectedQuestIds.Order(StringComparer.Ordinal).ToArray(), body.Quests.Select(q => q.Id).ToArray()); var gatherIntro = body.Quests.Single(q => q.Id == "prototype_quest_gather_intro"); Assert.Equal("Intro: Salvage Run", gatherIntro.DisplayName); Assert.Empty(gatherIntro.PrerequisiteQuestIds); Assert.Single(gatherIntro.Steps); var gatherStep = gatherIntro.Steps[0]; Assert.Equal("gather_intro_step_salvage", gatherStep.Id); Assert.Equal("Gather scrap metal", gatherStep.DisplayName); Assert.Single(gatherStep.Objectives); var gatherObjective = gatherStep.Objectives[0]; Assert.Equal("gather_intro_obj_scrap", gatherObjective.Id); Assert.Equal("gather_item", gatherObjective.Kind); Assert.Equal("scrap_metal_bulk", gatherObjective.ItemId); Assert.Equal(3, gatherObjective.Quantity); Assert.Null(gatherObjective.RecipeId); Assert.Null(gatherObjective.EncounterId); var refineIntro = body.Quests.Single(q => q.Id == "prototype_quest_refine_intro"); Assert.Equal(["prototype_quest_gather_intro"], refineIntro.PrerequisiteQuestIds); var refineObjective = refineIntro.Steps[0].Objectives[0]; Assert.Equal("refine_intro_obj_recipe", refineObjective.Id); Assert.Equal("craft_recipe", refineObjective.Kind); Assert.Equal("refine_scrap_standard", refineObjective.RecipeId); Assert.Equal(1, refineObjective.Quantity); Assert.Null(refineObjective.ItemId); Assert.Null(refineObjective.EncounterId); var combatIntro = body.Quests.Single(q => q.Id == "prototype_quest_combat_intro"); Assert.Single(combatIntro.Steps); var combatObjective = combatIntro.Steps[0].Objectives[0]; Assert.Equal("encounter_complete", combatObjective.Kind); Assert.Equal("prototype_combat_pocket", combatObjective.EncounterId); Assert.Null(combatObjective.ItemId); Assert.Null(combatObjective.Quantity); Assert.Null(combatObjective.RecipeId); var operatorChain = body.Quests.Single(q => q.Id == PrototypeE7M1QuestCatalogRules.ChainQuestId); Assert.Equal(4, operatorChain.Steps.Count); Assert.Equal( [ "prototype_quest_gather_intro", "prototype_quest_refine_intro", "prototype_quest_combat_intro", ], operatorChain.PrerequisiteQuestIds); var terminalObjective = operatorChain.Steps[3].Objectives[0]; Assert.Equal("inventory_has_item", terminalObjective.Kind); Assert.Equal(PrototypeE7M1QuestCatalogRules.ChainTerminalItemId, terminalObjective.ItemId); Assert.Equal(1, terminalObjective.Quantity); Assert.Null(operatorChain.FactionGateRules); var gridContract = body.Quests.Single(q => q.Id == PrototypeE7M1QuestCatalogRules.GridContractQuestId); Assert.NotNull(gridContract.FactionGateRules); Assert.Single(gridContract.FactionGateRules!); Assert.Equal(PrototypeE7M3QuestFactionRules.GridContractGateFactionId, gridContract.FactionGateRules![0].FactionId); Assert.Equal(PrototypeE7M3QuestFactionRules.GridContractMinStanding, gridContract.FactionGateRules![0].MinStanding); Assert.Null(gatherIntro.FactionGateRules); } [Fact] public async Task GetQuestDefinitions_ShouldOmitFactionGateRulesJsonProperty_WhenQuestHasNoGates() { // Arrange await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); // Act var response = await client.GetAsync("/game/world/quest-definitions"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); foreach (var quest in doc.RootElement.GetProperty("quests").EnumerateArray()) { var id = quest.GetProperty("id").GetString(); if (id is "prototype_quest_gather_intro" or PrototypeE7M1QuestCatalogRules.ChainQuestId) { Assert.False(quest.TryGetProperty("factionGateRules", out _)); } if (id == PrototypeE7M1QuestCatalogRules.GridContractQuestId) { Assert.True(quest.TryGetProperty("factionGateRules", out var rules)); Assert.Equal(JsonValueKind.Array, rules.ValueKind); Assert.Equal(1, rules.GetArrayLength()); } } } }