334 lines
12 KiB
C#
334 lines
12 KiB
C#
using NeonSprawl.Server.Game.Interaction;
|
|
using NeonSprawl.Server.Game.PositionState;
|
|
|
|
namespace NeonSprawl.Server.Tests.Game.Interaction;
|
|
|
|
/// <summary>Integration tests for <see cref="InteractionApi"/> (NS-18). NEO-63: <see cref="InteractionResourceNodeGatherIntegrationTests"/> covers gather via interact.</summary>
|
|
public class InteractionApiTests
|
|
{
|
|
[Fact]
|
|
public async Task PostInteract_ShouldAllow_WhenExactlyAtHorizontalRadius()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var move = new MoveCommandRequest
|
|
{
|
|
SchemaVersion = MoveCommandRequest.CurrentSchemaVersion,
|
|
Target = new PositionVector { X = 3, Y = 0, Z = 0 },
|
|
};
|
|
var moveResponse = await client.PostAsJsonAsync("/game/players/dev-local-1/move", move);
|
|
|
|
// Act
|
|
var response = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/interact",
|
|
new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId,
|
|
});
|
|
|
|
// Assert
|
|
var body = await response.Content.ReadFromJsonAsync<InteractionResponse>();
|
|
Assert.Equal(HttpStatusCode.OK, moveResponse.StatusCode);
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.NotNull(body);
|
|
Assert.True(body!.Allowed);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldAllow_WhenPlayerInHorizontalRange()
|
|
{
|
|
// Arrange
|
|
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var move = new MoveCommandRequest
|
|
{
|
|
SchemaVersion = MoveCommandRequest.CurrentSchemaVersion,
|
|
Target = new PositionVector { X = 0, Y = 0.9, Z = 0 },
|
|
};
|
|
var moveResp = await client.PostAsJsonAsync("/game/players/dev-local-1/move", move);
|
|
|
|
var req = new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId,
|
|
};
|
|
|
|
// Act
|
|
var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req);
|
|
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, moveResp.StatusCode);
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var body = await response.Content.ReadFromJsonAsync<InteractionResponse>();
|
|
Assert.NotNull(body);
|
|
Assert.True(body!.Allowed);
|
|
Assert.Null(body.ReasonCode);
|
|
Assert.Equal(PrototypeInteractableRegistry.PrototypeTerminalId, body.InteractableId);
|
|
Assert.NotNull(body.Payload);
|
|
Assert.Equal("placeholder", body.Payload!.Kind);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldDenyOutOfRange_WhenPlayerSeededFarFromTerminal()
|
|
{
|
|
// Arrange
|
|
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var req = new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId,
|
|
};
|
|
// Act
|
|
|
|
var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req);
|
|
// Assert
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var body = await response.Content.ReadFromJsonAsync<InteractionResponse>();
|
|
Assert.NotNull(body);
|
|
Assert.False(body!.Allowed);
|
|
Assert.Equal(InteractionApi.ReasonOutOfRange, body.ReasonCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldDenyUnknownInteractable_WhenIdNotInRegistry()
|
|
{
|
|
// Arrange
|
|
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var req = new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = "no_such_thing",
|
|
};
|
|
// Act
|
|
|
|
var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req);
|
|
// Assert
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var body = await response.Content.ReadFromJsonAsync<InteractionResponse>();
|
|
Assert.NotNull(body);
|
|
Assert.False(body!.Allowed);
|
|
Assert.Equal(InteractionApi.ReasonUnknownInteractable, body.ReasonCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldBeCaseInsensitive_ForInteractableId()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var move = new MoveCommandRequest
|
|
{
|
|
SchemaVersion = MoveCommandRequest.CurrentSchemaVersion,
|
|
Target = new PositionVector { X = 0, Y = 0, Z = 0 },
|
|
};
|
|
var moveResponse = await client.PostAsJsonAsync("/game/players/dev-local-1/move", move);
|
|
|
|
var req = new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = " PROTOTYPE_TERMINAL ",
|
|
};
|
|
|
|
// Act
|
|
var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req);
|
|
|
|
// Assert
|
|
var body = await response.Content.ReadFromJsonAsync<InteractionResponse>();
|
|
Assert.Equal(HttpStatusCode.OK, moveResponse.StatusCode);
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.NotNull(body);
|
|
Assert.True(body!.Allowed);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldReturnBadRequest_WhenSchemaVersionWrong()
|
|
{
|
|
// Arrange
|
|
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var req = new InteractionRequest
|
|
{
|
|
SchemaVersion = 999,
|
|
InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId,
|
|
};
|
|
// Act
|
|
|
|
var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req);
|
|
// Assert
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldReturnBadRequest_WhenInteractableIdMissing()
|
|
{
|
|
// Arrange
|
|
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var content = new StringContent(
|
|
$"{{\"schemaVersion\":{InteractionRequest.CurrentSchemaVersion}}}",
|
|
Encoding.UTF8,
|
|
"application/json");
|
|
// Act
|
|
|
|
var response = await client.PostAsync("/game/players/dev-local-1/interact", content);
|
|
// Assert
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldReturnBadRequest_WhenInteractableIdWhitespaceOnly()
|
|
{
|
|
// Arrange
|
|
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var req = new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = " \t ",
|
|
};
|
|
// Act
|
|
|
|
var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req);
|
|
// Assert
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldReturnNotFound_WhenPlayerUnknown()
|
|
{
|
|
// Arrange
|
|
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var req = new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId,
|
|
};
|
|
// Act
|
|
|
|
var response = await client.PostAsJsonAsync("/game/players/nobody-here/interact", req);
|
|
// Assert
|
|
|
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldReflectNewPosition_AfterMove()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
|
|
// Act
|
|
var far = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/interact",
|
|
new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId,
|
|
});
|
|
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, far.StatusCode);
|
|
var farBody = await far.Content.ReadFromJsonAsync<InteractionResponse>();
|
|
Assert.False(farBody!.Allowed);
|
|
Assert.Equal(InteractionApi.ReasonOutOfRange, farBody.ReasonCode);
|
|
|
|
// Act
|
|
var move = new MoveCommandRequest
|
|
{
|
|
SchemaVersion = MoveCommandRequest.CurrentSchemaVersion,
|
|
Target = new PositionVector { X = 0, Y = 0, Z = 0 },
|
|
};
|
|
var moveResponse = await client.PostAsJsonAsync("/game/players/dev-local-1/move", move);
|
|
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, moveResponse.StatusCode);
|
|
|
|
// Act
|
|
var near = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/interact",
|
|
new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId,
|
|
});
|
|
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, near.StatusCode);
|
|
var nearBody = await near.Content.ReadFromJsonAsync<InteractionResponse>();
|
|
Assert.True(nearBody!.Allowed);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldAllowResourceNode_WhenPlayerInRangeOfResourceAnchor()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var move = new MoveCommandRequest
|
|
{
|
|
SchemaVersion = MoveCommandRequest.CurrentSchemaVersion,
|
|
Target = new PositionVector { X = 12, Y = 0.9, Z = -6 },
|
|
};
|
|
var moveResponse = await client.PostAsJsonAsync("/game/players/dev-local-1/move", move);
|
|
|
|
// Act
|
|
var response = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/interact",
|
|
new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = PrototypeInteractableRegistry.PrototypeResourceNodeAlphaId,
|
|
});
|
|
|
|
// Assert
|
|
var body = await response.Content.ReadFromJsonAsync<InteractionResponse>();
|
|
Assert.Equal(HttpStatusCode.OK, moveResponse.StatusCode);
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.NotNull(body);
|
|
Assert.True(body!.Allowed);
|
|
Assert.Equal(PrototypeInteractableRegistry.PrototypeResourceNodeAlphaId, body.InteractableId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostInteract_ShouldDenyOutOfRange_ForResourceNode_WhenPlayerFar()
|
|
{
|
|
// Arrange
|
|
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
// Act
|
|
var response = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/interact",
|
|
new InteractionRequest
|
|
{
|
|
SchemaVersion = InteractionRequest.CurrentSchemaVersion,
|
|
InteractableId = PrototypeInteractableRegistry.PrototypeResourceNodeAlphaId,
|
|
});
|
|
// Assert
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var body = await response.Content.ReadFromJsonAsync<InteractionResponse>();
|
|
Assert.NotNull(body);
|
|
Assert.False(body!.Allowed);
|
|
Assert.Equal(InteractionApi.ReasonOutOfRange, body.ReasonCode);
|
|
}
|
|
}
|