NEO-147: add standing and duplicate-seed review follow-up tests
parent
be0525b152
commit
3549b647a5
|
|
@ -3,6 +3,7 @@ using Microsoft.Extensions.Options;
|
|||
using NeonSprawl.Server.Game.Contracts;
|
||||
using NeonSprawl.Server.Game.Factions;
|
||||
using NeonSprawl.Server.Game.PositionState;
|
||||
using NeonSprawl.Server.Game.Quests;
|
||||
using NeonSprawl.Server.Tests;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ public sealed class ContractGeneratorOperationsTests
|
|||
private const string TemplateId = "prototype_contract_clear_combat_pocket";
|
||||
private const string EncounterId = "prototype_combat_pocket";
|
||||
private const string GridOperatorsFactionId = "prototype_faction_grid_operators";
|
||||
private const string GatedTemplateId = "test_gated_contract_template";
|
||||
private const string SeedBucket = "2026-06-22";
|
||||
private const string AlternateSeedBucket = "2026-06-23";
|
||||
private static readonly DateTimeOffset IssuedAt = new(2026, 6, 22, 10, 0, 0, TimeSpan.Zero);
|
||||
|
|
@ -63,10 +65,10 @@ public sealed class ContractGeneratorOperationsTests
|
|||
public void MakeDeterministicInstanceId_ShouldMatchForSameInputs_AndDifferForDifferentSeedBucket()
|
||||
{
|
||||
// Arrange
|
||||
// Act
|
||||
var first = ContractInstanceIds.MakeDeterministicInstanceId(PlayerId, TemplateId, SeedBucket);
|
||||
var same = ContractInstanceIds.MakeDeterministicInstanceId(PlayerId, TemplateId, SeedBucket);
|
||||
var different = ContractInstanceIds.MakeDeterministicInstanceId(PlayerId, TemplateId, AlternateSeedBucket);
|
||||
// Act
|
||||
// Assert
|
||||
Assert.Equal(first, same);
|
||||
Assert.NotEqual(first, different);
|
||||
|
|
@ -151,6 +153,48 @@ public sealed class ContractGeneratorOperationsTests
|
|||
Assert.Equal(ContractGeneratorReasonCodes.NoEligibleTemplate, result.ReasonCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryIssue_ShouldDenyNoEligibleTemplate_WhenFactionStandingBelowMin()
|
||||
{
|
||||
// Arrange
|
||||
var prototype = PrototypeE7M4ContractCatalogRules.ExpectedTemplateFreeze[TemplateId];
|
||||
var gatedTemplate = prototype with
|
||||
{
|
||||
Id = GatedTemplateId,
|
||||
MinFactionStanding = new FactionGateRuleRow(GridOperatorsFactionId, 1),
|
||||
};
|
||||
var deps = CreateDependencies(gatedTemplate);
|
||||
// Act
|
||||
var result = TryIssue(deps, templateId: GatedTemplateId, seedBucket: SeedBucket);
|
||||
// Assert
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal(ContractGeneratorReasonCodes.NoEligibleTemplate, result.ReasonCode);
|
||||
Assert.Null(result.Snapshot);
|
||||
Assert.False(deps.InstanceStore.TryGetActiveForPlayer(PlayerId, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryIssue_ShouldDenyInvalidIds_WhenReissuingSameSeedAfterComplete()
|
||||
{
|
||||
// Arrange
|
||||
var deps = CreateDependencies();
|
||||
var first = TryIssue(deps, templateId: TemplateId, seedBucket: SeedBucket);
|
||||
Assert.True(first.Success);
|
||||
Assert.True(deps.InstanceStore.TryMarkComplete(
|
||||
PlayerId,
|
||||
first.Snapshot!.ContractInstanceId,
|
||||
CompletedAt,
|
||||
out _));
|
||||
// Act
|
||||
var second = TryIssue(deps, templateId: TemplateId, seedBucket: SeedBucket);
|
||||
// Assert
|
||||
Assert.False(second.Success);
|
||||
Assert.Equal(ContractGeneratorReasonCodes.InvalidIds, second.ReasonCode);
|
||||
Assert.NotNull(second.Snapshot);
|
||||
Assert.Equal(ContractInstanceStatus.Completed, second.Snapshot!.Status);
|
||||
Assert.False(deps.InstanceStore.TryGetActiveForPlayer(PlayerId, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryIssue_ShouldDenyPlayerNotWritable_WhenPlayerUnknown()
|
||||
{
|
||||
|
|
@ -204,13 +248,14 @@ public sealed class ContractGeneratorOperationsTests
|
|||
deps.StandingStore,
|
||||
deps.TimeProvider);
|
||||
|
||||
private static GeneratorTestDependencies CreateDependencies()
|
||||
private static GeneratorTestDependencies CreateDependencies(params ContractTemplateRow[] templates)
|
||||
{
|
||||
var template = PrototypeE7M4ContractCatalogRules.ExpectedTemplateFreeze[TemplateId];
|
||||
var rows = new Dictionary<string, ContractTemplateRow>(StringComparer.Ordinal)
|
||||
var rows = templates.Length == 0
|
||||
? new Dictionary<string, ContractTemplateRow>(StringComparer.Ordinal)
|
||||
{
|
||||
[template.Id] = template,
|
||||
};
|
||||
[TemplateId] = PrototypeE7M4ContractCatalogRules.ExpectedTemplateFreeze[TemplateId],
|
||||
}
|
||||
: templates.ToDictionary(static t => t.Id, StringComparer.Ordinal);
|
||||
var catalog = new ContractTemplateCatalog("/tmp/catalog", rows, catalogJsonFileCount: 1);
|
||||
var templateRegistry = new ContractTemplateRegistry(catalog);
|
||||
var positionOptions = Options.Create(new GamePositionOptions { DevPlayerId = PlayerId });
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ public sealed class ContractGeneratorPersistenceIntegrationTests(PostgresIntegra
|
|||
TemplateId,
|
||||
SeedBucket);
|
||||
|
||||
// Act — issue through first host
|
||||
// Act
|
||||
ContractIssueOperationResult issued;
|
||||
using (var firstScope = Factory.Services.CreateScope())
|
||||
{
|
||||
|
|
@ -45,19 +45,15 @@ public sealed class ContractGeneratorPersistenceIntegrationTests(PostgresIntegra
|
|||
services.GetRequiredService<TimeProvider>());
|
||||
}
|
||||
|
||||
ContractInstanceState readBack;
|
||||
await using var secondFactory = new PostgresWebApplicationFactory();
|
||||
using var secondScope = secondFactory.Services.CreateScope();
|
||||
var readStore = secondScope.ServiceProvider.GetRequiredService<IContractInstanceStore>();
|
||||
readBack = readStore.TryGetActiveForPlayer(PlayerId, out var snapshot)
|
||||
? snapshot
|
||||
: null!;
|
||||
|
||||
// Assert
|
||||
Assert.True(issued.Success);
|
||||
Assert.Equal(expectedInstanceId, issued.Snapshot!.ContractInstanceId);
|
||||
Assert.Equal(EncounterId, issued.EncounterTemplateId);
|
||||
Assert.NotNull(readBack);
|
||||
|
||||
await using var secondFactory = new PostgresWebApplicationFactory();
|
||||
using var secondScope = secondFactory.Services.CreateScope();
|
||||
var readStore = secondScope.ServiceProvider.GetRequiredService<IContractInstanceStore>();
|
||||
Assert.True(readStore.TryGetActiveForPlayer(PlayerId, out var readBack));
|
||||
Assert.Equal(expectedInstanceId, readBack.ContractInstanceId);
|
||||
Assert.Equal(TemplateId, readBack.TemplateId);
|
||||
Assert.Equal(ContractInstanceStatus.Active, readBack.Status);
|
||||
|
|
|
|||
Loading…
Reference in New Issue