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.Contracts;
|
||||||
using NeonSprawl.Server.Game.Factions;
|
using NeonSprawl.Server.Game.Factions;
|
||||||
using NeonSprawl.Server.Game.PositionState;
|
using NeonSprawl.Server.Game.PositionState;
|
||||||
|
using NeonSprawl.Server.Game.Quests;
|
||||||
using NeonSprawl.Server.Tests;
|
using NeonSprawl.Server.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -15,6 +16,7 @@ public sealed class ContractGeneratorOperationsTests
|
||||||
private const string TemplateId = "prototype_contract_clear_combat_pocket";
|
private const string TemplateId = "prototype_contract_clear_combat_pocket";
|
||||||
private const string EncounterId = "prototype_combat_pocket";
|
private const string EncounterId = "prototype_combat_pocket";
|
||||||
private const string GridOperatorsFactionId = "prototype_faction_grid_operators";
|
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 SeedBucket = "2026-06-22";
|
||||||
private const string AlternateSeedBucket = "2026-06-23";
|
private const string AlternateSeedBucket = "2026-06-23";
|
||||||
private static readonly DateTimeOffset IssuedAt = new(2026, 6, 22, 10, 0, 0, TimeSpan.Zero);
|
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()
|
public void MakeDeterministicInstanceId_ShouldMatchForSameInputs_AndDifferForDifferentSeedBucket()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
// Act
|
||||||
var first = ContractInstanceIds.MakeDeterministicInstanceId(PlayerId, TemplateId, SeedBucket);
|
var first = ContractInstanceIds.MakeDeterministicInstanceId(PlayerId, TemplateId, SeedBucket);
|
||||||
var same = ContractInstanceIds.MakeDeterministicInstanceId(PlayerId, TemplateId, SeedBucket);
|
var same = ContractInstanceIds.MakeDeterministicInstanceId(PlayerId, TemplateId, SeedBucket);
|
||||||
var different = ContractInstanceIds.MakeDeterministicInstanceId(PlayerId, TemplateId, AlternateSeedBucket);
|
var different = ContractInstanceIds.MakeDeterministicInstanceId(PlayerId, TemplateId, AlternateSeedBucket);
|
||||||
// Act
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(first, same);
|
Assert.Equal(first, same);
|
||||||
Assert.NotEqual(first, different);
|
Assert.NotEqual(first, different);
|
||||||
|
|
@ -151,6 +153,48 @@ public sealed class ContractGeneratorOperationsTests
|
||||||
Assert.Equal(ContractGeneratorReasonCodes.NoEligibleTemplate, result.ReasonCode);
|
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]
|
[Fact]
|
||||||
public void TryIssue_ShouldDenyPlayerNotWritable_WhenPlayerUnknown()
|
public void TryIssue_ShouldDenyPlayerNotWritable_WhenPlayerUnknown()
|
||||||
{
|
{
|
||||||
|
|
@ -204,13 +248,14 @@ public sealed class ContractGeneratorOperationsTests
|
||||||
deps.StandingStore,
|
deps.StandingStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static GeneratorTestDependencies CreateDependencies()
|
private static GeneratorTestDependencies CreateDependencies(params ContractTemplateRow[] templates)
|
||||||
{
|
{
|
||||||
var template = PrototypeE7M4ContractCatalogRules.ExpectedTemplateFreeze[TemplateId];
|
var rows = templates.Length == 0
|
||||||
var rows = new Dictionary<string, ContractTemplateRow>(StringComparer.Ordinal)
|
? 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 catalog = new ContractTemplateCatalog("/tmp/catalog", rows, catalogJsonFileCount: 1);
|
||||||
var templateRegistry = new ContractTemplateRegistry(catalog);
|
var templateRegistry = new ContractTemplateRegistry(catalog);
|
||||||
var positionOptions = Options.Create(new GamePositionOptions { DevPlayerId = PlayerId });
|
var positionOptions = Options.Create(new GamePositionOptions { DevPlayerId = PlayerId });
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ public sealed class ContractGeneratorPersistenceIntegrationTests(PostgresIntegra
|
||||||
TemplateId,
|
TemplateId,
|
||||||
SeedBucket);
|
SeedBucket);
|
||||||
|
|
||||||
// Act — issue through first host
|
// Act
|
||||||
ContractIssueOperationResult issued;
|
ContractIssueOperationResult issued;
|
||||||
using (var firstScope = Factory.Services.CreateScope())
|
using (var firstScope = Factory.Services.CreateScope())
|
||||||
{
|
{
|
||||||
|
|
@ -45,19 +45,15 @@ public sealed class ContractGeneratorPersistenceIntegrationTests(PostgresIntegra
|
||||||
services.GetRequiredService<TimeProvider>());
|
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
|
||||||
Assert.True(issued.Success);
|
Assert.True(issued.Success);
|
||||||
Assert.Equal(expectedInstanceId, issued.Snapshot!.ContractInstanceId);
|
Assert.Equal(expectedInstanceId, issued.Snapshot!.ContractInstanceId);
|
||||||
Assert.Equal(EncounterId, issued.EncounterTemplateId);
|
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(expectedInstanceId, readBack.ContractInstanceId);
|
||||||
Assert.Equal(TemplateId, readBack.TemplateId);
|
Assert.Equal(TemplateId, readBack.TemplateId);
|
||||||
Assert.Equal(ContractInstanceStatus.Active, readBack.Status);
|
Assert.Equal(ContractInstanceStatus.Active, readBack.Status);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue