diff --git a/Controllers/PlayerController.cs b/Controllers/PlayerController.cs index 1892a89..0ac3f66 100644 --- a/Controllers/PlayerController.cs +++ b/Controllers/PlayerController.cs @@ -154,6 +154,9 @@ public class PlayerController : ControllerBase if (currentLocation == null) return BadRequest("Current location not found."); + if (string.IsNullOrWhiteSpace(req.Direction)) + return BadRequest("Direction is required."); + var direction = req.Direction.ToUpper(); var directionMap = new Dictionary { @@ -189,16 +192,14 @@ public class PlayerController : ControllerBase // Generate new location var newX = currentLocation.X + dx; var newY = currentLocation.Y + dy; - var random = new Random(); - var newZ = currentLocation.Z + random.Next(-1, 2); // -1, 0, or +1 + var newZ = currentLocation.Z + Random.Shared.Next(-1, 2); // -1, 0, or +1 string itemId = null; var spawnables = await _spawnableService.GetAllAsync(); if (spawnables.Any()) { - var rng = new Random(); - var roll = rng.Next(0, 100); // 0–99 + var roll = Random.Shared.Next(0, 100); // 0–99 foreach (var s in spawnables.OrderBy(s => s.SpawnRate)) { diff --git a/GameAuthApi.Tests/AuthControllerTests.cs b/GameAuthApi.Tests/AuthControllerTests.cs new file mode 100644 index 0000000..c9edd45 --- /dev/null +++ b/GameAuthApi.Tests/AuthControllerTests.cs @@ -0,0 +1,112 @@ +using GameAuthApi.Controllers; +using GameAuthApi.Models; +using GameAuthApi.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Text; + +public class AuthControllerTests +{ + private static IConfiguration GetConfig() + { + var inMemory = new Dictionary + { + {"Jwt:Key", "testkeytestkeytestkey"}, + {"Jwt:Issuer", "issuer"}, + {"MongoDB:ConnectionString", "mongodb://localhost"}, + {"MongoDB:DatabaseName", "test"} + }; + return new ConfigurationBuilder().AddInMemoryCollection(inMemory!).Build(); + } + + [Fact] + public async Task Register_UserExists_ReturnsBadRequest() + { + var mockUser = new Mock(GetConfig()); + mockUser.Setup(u => u.GetByUsernameAsync("exists")).ReturnsAsync(new User()); + var mockBlacklist = new Mock(GetConfig()); + var controller = new AuthController(mockUser.Object, GetConfig(), mockBlacklist.Object); + + var result = await controller.Register(new RegisterRequest { Username = "exists", Password = "p" }); + + Assert.IsType(result); + } + + [Fact] + public async Task Login_InvalidPassword_ReturnsUnauthorized() + { + var user = new User { Username = "user", PasswordHash = BCrypt.Net.BCrypt.HashPassword("other") }; + var mockUser = new Mock(GetConfig()); + mockUser.Setup(u => u.GetByUsernameAsync("user")).ReturnsAsync(user); + var controller = new AuthController(mockUser.Object, GetConfig(), new Mock(GetConfig()).Object); + + var result = await controller.Login(new LoginRequest { Username = "user", Password = "bad" }); + + Assert.IsType(result); + } + + [Fact] + public async Task Refresh_InvalidToken_ReturnsUnauthorized() + { + var user = new User { Username = "user", RefreshToken = "r", RefreshTokenExpiry = DateTime.UtcNow.AddMinutes(-1) }; + var mockUser = new Mock(GetConfig()); + mockUser.Setup(u => u.GetByUsernameAsync("user")).ReturnsAsync(user); + var controller = new AuthController(mockUser.Object, GetConfig(), new Mock(GetConfig()).Object); + + var result = await controller.Refresh(new AuthController.RefreshRequest { Username = "user", RefreshToken = "bad" }); + + Assert.IsType(result); + } + + [Fact] + public async Task Logout_BlacklistsToken() + { + var mockUser = new Mock(GetConfig()); + var blacklist = new Mock(GetConfig()); + blacklist.Setup(b => b.AddToBlacklistAsync(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask).Verifiable(); + var controller = new AuthController(mockUser.Object, GetConfig(), blacklist.Object); + controller.ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext() + }; + var tokenHandler = new JwtSecurityTokenHandler(); + var keyString = "abcdefghijklmnopqrstuvwxyz123456"; + var key = Encoding.UTF8.GetBytes(keyString); + var token = tokenHandler.WriteToken(new JwtSecurityToken( + "issuer", "issuer", null, + expires: DateTime.UtcNow.AddMinutes(1), + signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256))); + controller.Request.Headers["Authorization"] = $"Bearer {token}"; + + var result = await controller.Logout(); + + Assert.IsType(result); + blacklist.Verify(); + } + + [Fact] + public async Task ChangeUserRole_InvalidRole_ReturnsBadRequest() + { + var svc = new Mock(GetConfig()); + var controller = new AuthController(svc.Object, GetConfig(), new Mock(GetConfig()).Object); + var result = await controller.ChangeUserRole(new ChangeRoleRequest { Username="u", NewRole="BAD" }); + Assert.IsType(result); + } + + [Fact] + public async Task GetAllUsers_ReturnsOk() + { + var users = new List(); + var svc = new Mock(GetConfig()); + svc.Setup(s => s.GetAllAsync()).ReturnsAsync(users); + var controller = new AuthController(svc.Object, GetConfig(), new Mock(GetConfig()).Object); + + var result = await controller.GetAllUsers(); + + var ok = Assert.IsType(result); + Assert.Equal(users, ok.Value); + } +} diff --git a/GameAuthApi.Tests/GameAuthApi.Tests.csproj b/GameAuthApi.Tests/GameAuthApi.Tests.csproj new file mode 100644 index 0000000..570f144 --- /dev/null +++ b/GameAuthApi.Tests/GameAuthApi.Tests.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/GameAuthApi.Tests/GlobalUsings.cs b/GameAuthApi.Tests/GlobalUsings.cs new file mode 100644 index 0000000..5aaf570 --- /dev/null +++ b/GameAuthApi.Tests/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Xunit; +global using Moq; diff --git a/GameAuthApi.Tests/ItemControllerTests.cs b/GameAuthApi.Tests/ItemControllerTests.cs new file mode 100644 index 0000000..70db10c --- /dev/null +++ b/GameAuthApi.Tests/ItemControllerTests.cs @@ -0,0 +1,27 @@ +using GameAuthApi.Controllers; +using GameAuthApi.Models; +using GameAuthApi.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +public class ItemControllerTests +{ + private static IConfiguration config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + {"MongoDB:ConnectionString","mongodb://localhost"}, + {"MongoDB:DatabaseName","test"} + }).Build(); + + [Fact] + public async Task Create_InvalidLocation_ReturnsBadRequest() + { + var loc = new Mock(config); + loc.Setup(l => l.GetByIdAsync("bad")).ReturnsAsync((Location?)null); + var controller = new ItemController(new Mock(config).Object, loc.Object); + + var result = await controller.Create(new CreateItemRequest { LocationId = "bad", Name = "n" }); + + Assert.IsType(result); + } +} diff --git a/GameAuthApi.Tests/LocationControllerTests.cs b/GameAuthApi.Tests/LocationControllerTests.cs new file mode 100644 index 0000000..67efa85 --- /dev/null +++ b/GameAuthApi.Tests/LocationControllerTests.cs @@ -0,0 +1,40 @@ +using GameAuthApi.Controllers; +using GameAuthApi.Models; +using GameAuthApi.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +public class LocationControllerTests +{ + private static IConfiguration config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + {"MongoDB:ConnectionString","mongodb://localhost"}, + {"MongoDB:DatabaseName","test"} + }).Build(); + + [Fact] + public async Task Create_ReturnsOk() + { + var mockLoc = new Mock(config); + mockLoc.Setup(l => l.CreateAsync(It.IsAny())).Returns(Task.CompletedTask).Verifiable(); + var controller = new LocationController(mockLoc.Object, new Mock(config).Object); + + var result = await controller.Create(new CreateLocationRequest { X = 1, Y = 2, Z = 3 }); + + Assert.IsType(result); + mockLoc.Verify(); + } + + [Fact] + public async Task GetById_NotFound() + { + var mockLoc = new Mock(config); + mockLoc.Setup(l => l.GetByIdAsync("id")).ReturnsAsync((Location?)null); + var controller = new LocationController(mockLoc.Object, new Mock(config).Object); + + var result = await controller.GetById("id"); + + Assert.IsType(result); + } +} diff --git a/GameAuthApi.Tests/PlayerControllerTests.cs b/GameAuthApi.Tests/PlayerControllerTests.cs new file mode 100644 index 0000000..d8e8798 --- /dev/null +++ b/GameAuthApi.Tests/PlayerControllerTests.cs @@ -0,0 +1,93 @@ +using GameAuthApi.Controllers; +using GameAuthApi.Models; +using GameAuthApi.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using System.Security.Claims; + +public class PlayerControllerTests +{ + private static IConfiguration cfg = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + {"MongoDB:ConnectionString","mongodb://localhost"}, + {"MongoDB:DatabaseName","test"} + }).Build(); + + private static ControllerContext ContextWithUser(string id, string role="USER") + { + var ctx = new DefaultHttpContext(); + ctx.User = new ClaimsPrincipal(new ClaimsIdentity(new[] + { + new Claim(ClaimTypes.NameIdentifier, id), + new Claim(ClaimTypes.Role, role) + }, "mock")); + return new ControllerContext { HttpContext = ctx }; + } + + [Fact] + public async Task Create_ReturnsOk() + { + var playerService = new Mock(cfg); + playerService.Setup(p => p.GetByUserIdAsync("u")).ReturnsAsync((Player?)null); + playerService.Setup(p => p.CreateAsync(It.IsAny())).Returns(Task.CompletedTask).Verifiable(); + var locSvc = new Mock(cfg); + locSvc.Setup(l => l.GetSpawnLocationAsync()).ReturnsAsync((Location?)null); + locSvc.Setup(l => l.CreateAsync(It.IsAny())).Returns(Task.CompletedTask); + var controller = new PlayerController(playerService.Object, locSvc.Object, new Mock(cfg).Object, new Mock(cfg).Object) + { + ControllerContext = ContextWithUser("u") + }; + + var result = await controller.Create(new CreatePlayerRequest { Name = "p" }); + + Assert.IsType(result); + playerService.Verify(); + } + + [Fact] + public async Task Delete_NotFound() + { + var playerService = new Mock(cfg); + playerService.Setup(p => p.GetByUserIdAsync("u")).ReturnsAsync((Player?)null); + var controller = new PlayerController(playerService.Object, new Mock(cfg).Object, new Mock(cfg).Object, new Mock(cfg).Object) + { + ControllerContext = ContextWithUser("u") + }; + + var result = await controller.Delete(); + + Assert.IsType(result); + } + + [Fact] + public async Task AdminTeleport_PlayerNotFound() + { + var playerService = new Mock(cfg); + playerService.Setup(p => p.GetByIdAsync("p")).ReturnsAsync((Player?)null); + var controller = new PlayerController(playerService.Object, new Mock(cfg).Object, new Mock(cfg).Object, new Mock(cfg).Object) + { + ControllerContext = ContextWithUser("admin", "SUPER") + }; + + var result = await controller.AdminTeleport(new TeleportRequest { PlayerId = "p", LocationId = "l" }); + + Assert.IsType(result); + } + + [Fact] + public async Task Move_PlayerNotFound() + { + var playerService = new Mock(cfg); + playerService.Setup(p => p.GetByUserIdAsync("u")).ReturnsAsync((Player?)null); + var controller = new PlayerController(playerService.Object, new Mock(cfg).Object, new Mock(cfg).Object, new Mock(cfg).Object) + { + ControllerContext = ContextWithUser("u") + }; + + var result = await controller.Move(new MoveRequest { Direction = "N" }); + + Assert.IsType(result); + } +} diff --git a/GameAuthApi.Tests/SpawnableControllerTests.cs b/GameAuthApi.Tests/SpawnableControllerTests.cs new file mode 100644 index 0000000..452d58c --- /dev/null +++ b/GameAuthApi.Tests/SpawnableControllerTests.cs @@ -0,0 +1,42 @@ +using GameAuthApi.Controllers; +using GameAuthApi.Models; +using GameAuthApi.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +public class SpawnableControllerTests +{ + private static IConfiguration config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + {"MongoDB:ConnectionString","mongodb://localhost"}, + {"MongoDB:DatabaseName","test"} + }).Build(); + + [Fact] + public async Task Create_ReturnsOk() + { + var svc = new Mock(config); + svc.Setup(s => s.CreateAsync(It.IsAny())).Returns(Task.CompletedTask).Verifiable(); + var controller = new SpawnableController(svc.Object); + + var result = await controller.Create(new CreateSpawnableRequest { Name = "item", SpawnRate = 50 }); + + Assert.IsType(result); + svc.Verify(); + } + + [Fact] + public async Task GetAll_ReturnsOk() + { + var svc = new Mock(config); + var list = new List(); + svc.Setup(s => s.GetAllAsync()).ReturnsAsync(list); + var controller = new SpawnableController(svc.Object); + + var result = await controller.GetAll(); + + var ok = Assert.IsType(result); + Assert.Equal(list, ok.Value); + } +} diff --git a/GameAuthApi.Tests/UserControllerTests.cs b/GameAuthApi.Tests/UserControllerTests.cs new file mode 100644 index 0000000..6e544d3 --- /dev/null +++ b/GameAuthApi.Tests/UserControllerTests.cs @@ -0,0 +1,46 @@ +using GameAuthApi.Controllers; +using GameAuthApi.Models; +using GameAuthApi.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +public class UserControllerTests +{ + [Fact] + public async Task GetAllUsers_ReturnsOk() + { + var users = new List{ new User{ Username="u" } }; + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + {"MongoDB:ConnectionString","mongodb://localhost"}, + {"MongoDB:DatabaseName","test"} + }).Build(); + var mockService = new Mock(config); + mockService.Setup(s => s.GetAllAsync()).ReturnsAsync(users); + var controller = new UserController(mockService.Object); + + var result = await controller.GetAllUsers(); + + var ok = Assert.IsType(result); + Assert.Equal(users, ok.Value); + } + + [Fact] + public async Task DeleteUser_NotFound() + { + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + {"MongoDB:ConnectionString","mongodb://localhost"}, + {"MongoDB:DatabaseName","test"} + }).Build(); + var mockService = new Mock(config); + mockService.Setup(s => s.DeleteAsync("no")).ReturnsAsync(false); + var controller = new UserController(mockService.Object); + + var result = await controller.DeleteUser("no"); + + Assert.IsType(result); + } +} diff --git a/GameAuthApi.csproj b/GameAuthApi.csproj index e0fd56d..aa5bd6b 100644 --- a/GameAuthApi.csproj +++ b/GameAuthApi.csproj @@ -8,4 +8,7 @@ + + + \ No newline at end of file diff --git a/GameAuthApi.sln b/GameAuthApi.sln index 5cefa76..3986ed6 100644 --- a/GameAuthApi.sln +++ b/GameAuthApi.sln @@ -1,19 +1,58 @@ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.2.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameAuthApi", "GameAuthApi.csproj", "{349B73A5-7632-5ADF-4645-BB2EF73FA948}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameAuthApi.Tests", "GameAuthApi.Tests\GameAuthApi.Tests.csproj", "{7F86DBEA-032B-4A42-A7AF-F359CE210E49}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Debug|Any CPU.Build.0 = Debug|Any CPU + {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Debug|x64.ActiveCfg = Debug|Any CPU + {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Debug|x64.Build.0 = Debug|Any CPU + {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Debug|x86.ActiveCfg = Debug|Any CPU + {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Debug|x86.Build.0 = Debug|Any CPU {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Release|Any CPU.ActiveCfg = Release|Any CPU {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Release|Any CPU.Build.0 = Release|Any CPU + {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Release|x64.ActiveCfg = Release|Any CPU + {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Release|x64.Build.0 = Release|Any CPU + {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Release|x86.ActiveCfg = Release|Any CPU + {349B73A5-7632-5ADF-4645-BB2EF73FA948}.Release|x86.Build.0 = Release|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Debug|x64.ActiveCfg = Debug|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Debug|x64.Build.0 = Debug|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Debug|x86.ActiveCfg = Debug|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Debug|x86.Build.0 = Debug|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Release|Any CPU.Build.0 = Release|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Release|x64.ActiveCfg = Release|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Release|x64.Build.0 = Release|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Release|x86.ActiveCfg = Release|Any CPU + {7D40F732-3FDC-48F7-9991-BB5739B3A546}.Release|x86.Build.0 = Release|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Debug|x64.ActiveCfg = Debug|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Debug|x64.Build.0 = Debug|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Debug|x86.ActiveCfg = Debug|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Debug|x86.Build.0 = Debug|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Release|Any CPU.Build.0 = Release|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Release|x64.ActiveCfg = Release|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Release|x64.Build.0 = Release|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Release|x86.ActiveCfg = Release|Any CPU + {7F86DBEA-032B-4A42-A7AF-F359CE210E49}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Services/BlacklistService.cs b/Services/BlacklistService.cs index ec1803a..7fe8c94 100644 --- a/Services/BlacklistService.cs +++ b/Services/BlacklistService.cs @@ -17,13 +17,13 @@ public class BlacklistService _blacklist = db.GetCollection("BlacklistedTokens"); } - public async Task AddToBlacklistAsync(string token, DateTime expiry) + public virtual async Task AddToBlacklistAsync(string token, DateTime expiry) { var entry = new BlacklistedToken { Token = token, Expiry = expiry }; await _blacklist.InsertOneAsync(entry); } - public async Task IsTokenBlacklistedAsync(string token) + public virtual async Task IsTokenBlacklistedAsync(string token) { var exists = await _blacklist.Find(x => x.Token == token && x.Expiry > DateTime.UtcNow).FirstOrDefaultAsync(); return exists != null; diff --git a/Services/ItemService.cs b/Services/ItemService.cs index 2381cfb..a4e2a0a 100644 --- a/Services/ItemService.cs +++ b/Services/ItemService.cs @@ -17,22 +17,22 @@ public class ItemService _items = db.GetCollection("Items"); } - public async Task CreateAsync(Item item) + public virtual async Task CreateAsync(Item item) { await _items.InsertOneAsync(item); } - public async Task> GetByLocationAsync(string locationId) + public virtual async Task> GetByLocationAsync(string locationId) { return await _items.Find(i => i.LocationId == locationId).ToListAsync(); } - public async Task GetByIdAsync(string id) + public virtual async Task GetByIdAsync(string id) { return await _items.Find(i => i.Id == id).FirstOrDefaultAsync(); } - public async Task UpdateAsync(Item item) + public virtual async Task UpdateAsync(Item item) { await _items.ReplaceOneAsync(i => i.Id == item.Id, item); } diff --git a/Services/LocationService.cs b/Services/LocationService.cs index 72a556c..5739fa3 100644 --- a/Services/LocationService.cs +++ b/Services/LocationService.cs @@ -17,7 +17,7 @@ public class LocationService _locations = db.GetCollection("Locations"); } - public async Task GetByIdAsync(string id) + public virtual async Task GetByIdAsync(string id) { if (!ObjectId.TryParse(id, out _)) return null; // prevent crash if id is invalid @@ -25,43 +25,43 @@ public class LocationService return await _locations.Find(x => x.Id == id).FirstOrDefaultAsync(); } - public async Task CreateAsync(Location loc) => + public virtual async Task CreateAsync(Location loc) => await _locations.InsertOneAsync(loc); - public async Task DeleteAsync(string id) => + public virtual async Task DeleteAsync(string id) => await _locations.DeleteOneAsync(loc => loc.Id == id); - public async Task UpdateAsync(Location loc) => + public virtual async Task UpdateAsync(Location loc) => await _locations.ReplaceOneAsync(l => l.Id == loc.Id, loc); - public async Task AddPlayerToLocationAsync(string locationId, string playerId) + public virtual async Task AddPlayerToLocationAsync(string locationId, string playerId) { var update = Builders.Update.AddToSet(l => l.Players, playerId); await _locations.UpdateOneAsync(l => l.Id == locationId, update); } - public async Task GetByCoordinatesAsync(int x, int y, int z) + public virtual async Task GetByCoordinatesAsync(int x, int y, int z) { return await _locations.Find(loc => loc.X == x && loc.Y == y && loc.Z == z).FirstOrDefaultAsync(); } - public async Task GetSpawnLocationAsync() + public virtual async Task GetSpawnLocationAsync() { return await _locations.Find(loc => loc.X == 0 && loc.Y == 0).FirstOrDefaultAsync(); } - public async Task RemovePlayerFromLocationAsync(string locationId, string playerId) + public virtual async Task RemovePlayerFromLocationAsync(string locationId, string playerId) { var update = Builders.Update.Pull(l => l.Players, playerId); await _locations.UpdateOneAsync(l => l.Id == locationId, update); } - public async Task AddItemToLocationAsync(string locationId, string itemId) + public virtual async Task AddItemToLocationAsync(string locationId, string itemId) { var update = Builders.Update.Push(l => l.Content, itemId); await _locations.UpdateOneAsync(l => l.Id == locationId, update); } - public async Task RemoveItemFromLocationAsync(string locationId, string itemId) + public virtual async Task RemoveItemFromLocationAsync(string locationId, string itemId) { var update = Builders.Update.Pull(l => l.Content, itemId); await _locations.UpdateOneAsync(l => l.Id == locationId, update); diff --git a/Services/PlayerService.cs b/Services/PlayerService.cs index e750a63..62dcfc1 100644 --- a/Services/PlayerService.cs +++ b/Services/PlayerService.cs @@ -18,26 +18,26 @@ public class PlayerService _players = db.GetCollection("Players"); } - public async Task GetByUserIdAsync(string userId) => + public virtual async Task GetByUserIdAsync(string userId) => await _players.Find(p => p.UserId == userId).FirstOrDefaultAsync(); - public async Task> GetByLocationIdAsync(string locationId) + public virtual async Task> GetByLocationIdAsync(string locationId) { return await _players.Find(p => p.LocationId == locationId).ToListAsync(); } - public async Task GetByIdAsync(string id) => + public virtual async Task GetByIdAsync(string id) => await _players.Find(p => p.Id == id).FirstOrDefaultAsync(); - public async Task CreateAsync(Player player) => + public virtual async Task CreateAsync(Player player) => await _players.InsertOneAsync(player); - public async Task UpdateAsync(Player player) + public virtual async Task UpdateAsync(Player player) { await _players.ReplaceOneAsync(p => p.Id == player.Id, player); } - public async Task DeleteAsync(string id) + public virtual async Task DeleteAsync(string id) { await _players.DeleteOneAsync(p => p.Id == id); } diff --git a/Services/SpawnableService.cs b/Services/SpawnableService.cs index 0b545c7..4993935 100644 --- a/Services/SpawnableService.cs +++ b/Services/SpawnableService.cs @@ -17,12 +17,12 @@ public class SpawnableService _spawnables = db.GetCollection("Spawnables"); } - public async Task CreateAsync(Spawnable spawnable) + public virtual async Task CreateAsync(Spawnable spawnable) { await _spawnables.InsertOneAsync(spawnable); } - public async Task> GetAllAsync() + public virtual async Task> GetAllAsync() { return await _spawnables.Find(_ => true).ToListAsync(); } diff --git a/Services/UserService.cs b/Services/UserService.cs index 43763b0..919389f 100644 --- a/Services/UserService.cs +++ b/Services/UserService.cs @@ -17,21 +17,21 @@ public class UserService _users = database.GetCollection("Users"); } - public async Task GetByUsernameAsync(string username) => + public virtual async Task GetByUsernameAsync(string username) => await _users.Find(u => u.Username == username).FirstOrDefaultAsync(); - public async Task> GetAllAsync() => + public virtual async Task> GetAllAsync() => await _users.Find(_ => true).ToListAsync(); - public async Task DeleteAsync(string username) + public virtual async Task DeleteAsync(string username) { var result = await _users.DeleteOneAsync(u => u.Username == username); return result.DeletedCount > 0; } - public async Task CreateAsync(User user) => + public virtual async Task CreateAsync(User user) => await _users.InsertOneAsync(user); - public async Task UpdateAsync(User user) => + public virtual async Task UpdateAsync(User user) => await _users.ReplaceOneAsync(u => u.Id == user.Id, user); } \ No newline at end of file