Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions S1API.Tests/Entities/CustomNpcReadinessCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace S1API.Tests.Entities;

[CollectionDefinition(Name, DisableParallelization = true)]
public sealed class CustomNpcReadinessCollection
{
public const string Name = "Custom NPC readiness";
}
12 changes: 12 additions & 0 deletions S1API.Tests/Entities/CustomNpcReadinessPolicyTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using S1API.Entities;
using S1API.Internal.Entities;
using S1API.Internal.Patches;
using S1API.Internal.Utils;

namespace S1API.Tests.Entities;

[Collection(CustomNpcReadinessCollection.Name)]
public sealed class CustomNpcReadinessPolicyTests
{
[Fact]
Expand All @@ -14,6 +16,16 @@ public void ClientHydrationSignalsReadyOnlyAfterEveryCustomNpcTypeCompletes()

try
{
foreach (Type npcType in ReflectionUtils.GetDerivedClasses<NPC>())
{
if (npcType.Assembly != typeof(NPC).Assembly
&& npcType != typeof(DealerNpc)
&& npcType != typeof(CustomerNpc))
{
NPC.FinalizedCustomNpcTypes.Add(npcType);
}
}

Comment thread
ifBars marked this conversation as resolved.
var dealer = TestObjectFactory.CreateUninitialized<DealerNpc>();
var customer = TestObjectFactory.CreateUninitialized<CustomerNpc>();

Expand Down
160 changes: 160 additions & 0 deletions S1API.Tests/Entities/NPCRoleDeclarationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
using System.Reflection;
using S1API.Entities;
using S1API.Internal.Entities;

namespace S1API.Tests.Entities;

public sealed class NPCRoleDeclarationTests
{
[Fact]
public void IsCustomerIsVirtualReadOnlyBooleanDefaultingToFalse()
{
PropertyInfo? property = typeof(NPC).GetProperty(nameof(NPC.IsCustomer));
MethodInfo? getter = property?.GetMethod;
var npc = (NPC)System.Runtime.CompilerServices.RuntimeHelpers
.GetUninitializedObject(typeof(RoleTestNpc));

Assert.NotNull(property);
Assert.Equal(typeof(bool), property!.PropertyType);
Assert.True(getter!.IsVirtual);
Assert.False(getter.IsFinal);
Assert.Null(property.SetMethod);
Assert.False(npc.IsCustomer);
}

[Fact]
public void DeclaredPropertiesRejectsNullType()
{
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(
() => NpcRoleDeclarationResolver.GetDeclaredProperties(null!));

Assert.Equal("npcType", exception.ParamName);
}

[Fact]
public void DeclaredPropertiesRejectsNonNpcType()
{
ArgumentException exception = Assert.Throws<ArgumentException>(
() => NpcRoleDeclarationResolver.GetDeclaredProperties(typeof(string)));

Assert.Equal("npcType", exception.ParamName);
Assert.Contains("does not derive from", exception.Message);
}

[Theory]
[InlineData(false, false, false, false)]
[InlineData(false, true, false, false)]
[InlineData(true, false, false, false)]
[InlineData(true, true, false, false)]
[InlineData(true, false, true, false)]
[InlineData(true, true, true, false)]
[InlineData(true, false, false, true)]
[InlineData(true, true, false, true)]
public void ValidRoleCombinationsRemainComposable(
bool isPhysical,
bool isCustomer,
bool isDealer,
bool isSupplier)
{
var declaration = new NpcRoleDeclaration(
isPhysical,
isCustomer,
isDealer,
isSupplier);

NpcRoleDeclaration validated = declaration.Validate(typeof(NPC));

Assert.Equal(isCustomer, validated.IsCustomer);
Assert.Equal(isDealer, validated.IsDealer);
Assert.Equal(isSupplier, validated.IsSupplier);
}

[Fact]
public void DealerAndSupplierDeclarationFailsEarly()
{
var declaration = new NpcRoleDeclaration(
isPhysical: true,
isCustomer: false,
isDealer: true,
isSupplier: true);

InvalidOperationException exception = Assert.Throws<InvalidOperationException>(
() => declaration.Validate(typeof(NPC)));

Assert.Contains("cannot be both a dealer and a supplier", exception.Message);
}

[Fact]
public void NonPhysicalSupplierDeclarationFailsEarly()
{
var declaration = new NpcRoleDeclaration(
isPhysical: false,
isCustomer: false,
isDealer: false,
isSupplier: true);

InvalidOperationException exception = Assert.Throws<InvalidOperationException>(
() => declaration.Validate(typeof(NPC)));

Assert.Contains("must override IsPhysical to return true", exception.Message);
}

[Fact]
public void CompatibilityDeclarationsOnlyAddLegacyCapabilities()
{
var properties = new NpcRoleDeclaration(
isPhysical: true,
isCustomer: false,
isDealer: false,
isSupplier: false);

NpcRoleDeclaration effective = properties
.WithCompatibilityRoles(
isCustomer: true,
isDealer: true,
isSupplier: false)
.Validate(typeof(NPC));

Assert.True(effective.IsPhysical);
Assert.True(effective.IsCustomer);
Assert.True(effective.IsDealer);
Assert.False(effective.IsSupplier);
Assert.Equal(NpcRootRole.Dealer, effective.RootRole);
}

[Theory]
[InlineData(nameof(NPCPrefabBuilder.EnsureCustomer), nameof(NPC.IsCustomer))]
[InlineData(nameof(NPCPrefabBuilder.EnsureDealer), nameof(NPC.IsDealer))]
[InlineData(nameof(NPCPrefabBuilder.EnsureSupplier), nameof(NPC.IsSupplier))]
public void LegacyEnsureMethodsRemainFluentNonErrorObsoleteShims(
string methodName,
string replacementProperty)
{
MethodInfo? method = typeof(NPCPrefabBuilder).GetMethod(
methodName,
BindingFlags.Public | BindingFlags.Instance,
binder: null,
types: Type.EmptyTypes,
modifiers: null);
ObsoleteAttribute? obsolete = method?.GetCustomAttribute<ObsoleteAttribute>();

Assert.NotNull(method);
Assert.Equal(typeof(NPCPrefabBuilder), method!.ReturnType);
Assert.Empty(method.GetParameters());
Assert.NotNull(obsolete);
Assert.False(obsolete!.IsError);
Assert.Contains(replacementProperty, obsolete.Message);
}

#pragma warning disable CS0618
private static NPCPrefabBuilder CompileLegacyFluentCalls(NPCPrefabBuilder builder) =>
builder.EnsureCustomer().EnsureDealer().EnsureSupplier();
#pragma warning restore CS0618

private sealed class RoleTestNpc : NPC
{
internal override void CreateInternal()
{
}
}
}
Loading
Loading