API Test
This commit is contained in:
@@ -19,7 +19,7 @@ public class CardsPageTests : PageTest
|
||||
Page.PageError += (_, err) => Console.WriteLine($"[BROWSER PAGE ERROR] {err}");
|
||||
|
||||
await Page.GotoAsync($"{BaseUrl}/cards");
|
||||
await Page.WaitForSelectorAsync("h1", new() { Timeout = 30000 });
|
||||
await Page.WaitForSelectorAsync("h1", new PageWaitForSelectorOptions { Timeout = 30000 });
|
||||
|
||||
// Give Blazor WASM a moment to render
|
||||
await Page.WaitForTimeoutAsync(3000);
|
||||
@@ -31,4 +31,4 @@ public class CardsPageTests : PageTest
|
||||
var html = await Page.ContentAsync();
|
||||
File.WriteAllText("cards_page_dump.html", html);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,6 @@ namespace Tests;
|
||||
[TestFixture]
|
||||
public class DatabaseServiceTests
|
||||
{
|
||||
private IDatabaseConfigService _configService = null!;
|
||||
private IDatabaseService _dbService = null!;
|
||||
private string _testDbName = null!;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
@@ -29,6 +25,10 @@ public class DatabaseServiceTests
|
||||
await _dbService.EnsureDatabaseCreatedAsync();
|
||||
}
|
||||
|
||||
private IDatabaseConfigService _configService = null!;
|
||||
private IDatabaseService _dbService = null!;
|
||||
private string _testDbName = null!;
|
||||
|
||||
[Test]
|
||||
public async Task CheckHealth_InMemory_ReturnsConnected()
|
||||
{
|
||||
@@ -38,33 +38,65 @@ public class DatabaseServiceTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CardNotes_FullCrudLifecycle()
|
||||
public async Task Users_FullCrudLifecycle()
|
||||
{
|
||||
var cardName = "Test Hero " + Guid.NewGuid().ToString("N");
|
||||
var initialNote = new CardNote { CardName = cardName, Note = "Initial note text" };
|
||||
var username = "player_" + Guid.NewGuid().ToString("N")[..8];
|
||||
var initialUser = new User { Username = username, Password = "password123", CreatedAt = DateTime.UtcNow };
|
||||
|
||||
// 1. Create
|
||||
var createResult = await _dbService.SaveCardNoteAsync(initialNote, isNew: true);
|
||||
var createResult = await _dbService.SaveUserAsync(initialUser, true);
|
||||
Assert.That(createResult.Success, Is.True);
|
||||
|
||||
// 2. Read
|
||||
var fetched = await _dbService.GetCardNoteAsync(cardName);
|
||||
var fetched = await _dbService.GetUserAsync(username);
|
||||
Assert.That(fetched, Is.Not.Null);
|
||||
Assert.That(fetched!.Password, Is.EqualTo("password123"));
|
||||
|
||||
// 3. Update
|
||||
fetched.Password = "newpassword456";
|
||||
var updateResult = await _dbService.SaveUserAsync(fetched, false);
|
||||
Assert.That(updateResult.Success, Is.True);
|
||||
|
||||
var updated = await _dbService.GetUserAsync(username);
|
||||
Assert.That(updated!.Password, Is.EqualTo("newpassword456"));
|
||||
|
||||
// 4. Delete
|
||||
var deleteResult = await _dbService.DeleteUserAsync(username);
|
||||
Assert.That(deleteResult.Success, Is.True);
|
||||
|
||||
var deleted = await _dbService.GetUserAsync(username);
|
||||
Assert.That(deleted, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CardNotes_FullCrudLifecycle()
|
||||
{
|
||||
var cardName = "Test Hero " + Guid.NewGuid().ToString("N");
|
||||
var username = "user1";
|
||||
var initialNote = new CardNote { Username = username, CardName = cardName, Note = "Initial note text" };
|
||||
|
||||
// 1. Create
|
||||
var createResult = await _dbService.SaveCardNoteAsync(initialNote, true);
|
||||
Assert.That(createResult.Success, Is.True);
|
||||
|
||||
// 2. Read
|
||||
var fetched = await _dbService.GetCardNoteAsync(cardName, username);
|
||||
Assert.That(fetched, Is.Not.Null);
|
||||
Assert.That(fetched!.Note, Is.EqualTo("Initial note text"));
|
||||
|
||||
// 3. Update
|
||||
fetched.Note = "Updated note text";
|
||||
var updateResult = await _dbService.SaveCardNoteAsync(fetched, isNew: false);
|
||||
var updateResult = await _dbService.SaveCardNoteAsync(fetched, false);
|
||||
Assert.That(updateResult.Success, Is.True);
|
||||
|
||||
var updated = await _dbService.GetCardNoteAsync(cardName);
|
||||
var updated = await _dbService.GetCardNoteAsync(cardName, username);
|
||||
Assert.That(updated!.Note, Is.EqualTo("Updated note text"));
|
||||
|
||||
// 4. Delete
|
||||
var deleteResult = await _dbService.DeleteCardNoteAsync(cardName);
|
||||
var deleteResult = await _dbService.DeleteCardNoteAsync(cardName, username);
|
||||
Assert.That(deleteResult.Success, Is.True);
|
||||
|
||||
var deleted = await _dbService.GetCardNoteAsync(cardName);
|
||||
var deleted = await _dbService.GetCardNoteAsync(cardName, username);
|
||||
Assert.That(deleted, Is.Null);
|
||||
}
|
||||
|
||||
@@ -72,13 +104,14 @@ public class DatabaseServiceTests
|
||||
public async Task DuplicateCardNote_ReturnsFailureResult()
|
||||
{
|
||||
var cardName = "Unique Card " + Guid.NewGuid().ToString("N");
|
||||
var note1 = new CardNote { CardName = cardName, Note = "First" };
|
||||
var note2 = new CardNote { CardName = cardName, Note = "Second" };
|
||||
var username = "user1";
|
||||
var note1 = new CardNote { Username = username, CardName = cardName, Note = "First" };
|
||||
var note2 = new CardNote { Username = username, CardName = cardName, Note = "Second" };
|
||||
|
||||
var res1 = await _dbService.SaveCardNoteAsync(note1, isNew: true);
|
||||
var res1 = await _dbService.SaveCardNoteAsync(note1, true);
|
||||
Assert.That(res1.Success, Is.True);
|
||||
|
||||
var res2 = await _dbService.SaveCardNoteAsync(note2, isNew: true);
|
||||
var res2 = await _dbService.SaveCardNoteAsync(note2, true);
|
||||
Assert.That(res2.Success, Is.False);
|
||||
Assert.That(res2.Message, Does.Contain("already exists"));
|
||||
}
|
||||
@@ -98,7 +131,7 @@ public class DatabaseServiceTests
|
||||
};
|
||||
|
||||
// 1. Create
|
||||
var createResult = await _dbService.SaveUserDeckAsync(deck, isNew: true);
|
||||
var createResult = await _dbService.SaveUserDeckAsync(deck, true);
|
||||
Assert.That(createResult.Success, Is.True);
|
||||
|
||||
// 2. Read
|
||||
@@ -111,7 +144,7 @@ public class DatabaseServiceTests
|
||||
// 3. Update
|
||||
fetched.Cards.Add("Added Card");
|
||||
fetched.Name = "Control Deck v2";
|
||||
var updateResult = await _dbService.SaveUserDeckAsync(fetched, isNew: false);
|
||||
var updateResult = await _dbService.SaveUserDeckAsync(fetched, false);
|
||||
Assert.That(updateResult.Success, Is.True);
|
||||
|
||||
var updated = await _dbService.GetUserDeckAsync(deckId);
|
||||
@@ -143,7 +176,7 @@ public class DatabaseServiceTests
|
||||
};
|
||||
|
||||
// 1. Create
|
||||
var createResult = await _dbService.SavePasskeyAsync(passkey, isNew: true);
|
||||
var createResult = await _dbService.SavePasskeyAsync(passkey, true);
|
||||
Assert.That(createResult.Success, Is.True);
|
||||
Assert.That(passkey.Id, Is.GreaterThan(0));
|
||||
|
||||
@@ -157,7 +190,7 @@ public class DatabaseServiceTests
|
||||
|
||||
// 3. Update
|
||||
fetched.SignCount = 10;
|
||||
var updateResult = await _dbService.SavePasskeyAsync(fetched, isNew: false);
|
||||
var updateResult = await _dbService.SavePasskeyAsync(fetched, false);
|
||||
Assert.That(updateResult.Success, Is.True);
|
||||
|
||||
var updated = await _dbService.GetPasskeyAsync(passkey.Id);
|
||||
@@ -177,14 +210,17 @@ public class DatabaseServiceTests
|
||||
// 1. Seed
|
||||
var seedResult = await _dbService.SeedSampleDataAsync();
|
||||
Assert.That(seedResult.Success, Is.True);
|
||||
Assert.That(seedResult.UsersSeeded, Is.GreaterThan(0));
|
||||
Assert.That(seedResult.CardNotesSeeded, Is.GreaterThan(0));
|
||||
Assert.That(seedResult.UserDecksSeeded, Is.GreaterThan(0));
|
||||
Assert.That(seedResult.PasskeysSeeded, Is.GreaterThan(0));
|
||||
|
||||
var users = await _dbService.GetUsersAsync();
|
||||
var notes = await _dbService.GetCardNotesAsync();
|
||||
var decks = await _dbService.GetUserDecksAsync();
|
||||
var passkeys = await _dbService.GetPasskeysAsync();
|
||||
|
||||
Assert.That(users.Count, Is.GreaterThan(0));
|
||||
Assert.That(notes.Count, Is.GreaterThan(0));
|
||||
Assert.That(decks.Count, Is.GreaterThan(0));
|
||||
Assert.That(passkeys.Count, Is.GreaterThan(0));
|
||||
@@ -193,10 +229,12 @@ public class DatabaseServiceTests
|
||||
var resetResult = await _dbService.ResetDatabaseAsync();
|
||||
Assert.That(resetResult.Success, Is.True);
|
||||
|
||||
var usersAfterReset = await _dbService.GetUsersAsync();
|
||||
var notesAfterReset = await _dbService.GetCardNotesAsync();
|
||||
var decksAfterReset = await _dbService.GetUserDecksAsync();
|
||||
var passkeysAfterReset = await _dbService.GetPasskeysAsync();
|
||||
|
||||
Assert.That(usersAfterReset, Is.Empty);
|
||||
Assert.That(notesAfterReset, Is.Empty);
|
||||
Assert.That(decksAfterReset, Is.Empty);
|
||||
Assert.That(passkeysAfterReset, Is.Empty);
|
||||
@@ -209,9 +247,40 @@ public class DatabaseServiceTests
|
||||
Assert.That(testResults, Is.Not.Empty);
|
||||
|
||||
var failed = testResults.Where(t => t.Status == TestStatus.Failed).ToList();
|
||||
Assert.That(failed, Is.Empty, () => $"Failed tests: {string.Join(", ", failed.Select(f => $"{f.Name}: {f.ErrorMessage}"))}");
|
||||
Assert.That(failed, Is.Empty,
|
||||
() => $"Failed tests: {string.Join(", ", failed.Select(f => $"{f.Name}: {f.ErrorMessage}"))}");
|
||||
|
||||
var passedCount = testResults.Count(t => t.Status == TestStatus.Passed);
|
||||
Assert.That(passedCount, Is.GreaterThanOrEqualTo(6));
|
||||
Assert.That(passedCount, Is.GreaterThanOrEqualTo(7));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DatabaseConfigService_WithPostgreSQL_Password_EnvironmentVariable_AppliesPassword()
|
||||
{
|
||||
var testPassword = "test_custom_env_password_123";
|
||||
Environment.SetEnvironmentVariable("PostgreSQL_Password", testPassword);
|
||||
try
|
||||
{
|
||||
var config = new ConfigurationBuilder().Build();
|
||||
var service = new DatabaseConfigService(config);
|
||||
|
||||
Assert.That(service.ConnectionString, Does.Contain(testPassword));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable("PostgreSQL_Password", null);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DatabaseConfigService_MasksPassword_InDisplay()
|
||||
{
|
||||
var config = new ConfigurationBuilder().Build();
|
||||
var service = new DatabaseConfigService(config);
|
||||
service.SetConnectionString("Host=localhost;Database=chrono;Username=postgres;Password=secret_password;");
|
||||
|
||||
var description = service.GetActiveProviderDescription();
|
||||
Assert.That(description, Does.Contain("Password=******"));
|
||||
Assert.That(description, Does.Not.Contain("secret_password"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
using System.Net;
|
||||
using API.Data;
|
||||
using API.GraphQL;
|
||||
using API.Services;
|
||||
using HotChocolate.Execution;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.JSInterop;
|
||||
using Model;
|
||||
using Web.Services;
|
||||
|
||||
namespace Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class GraphQLTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<AppDbContext>()
|
||||
.UseInMemoryDatabase($"TestChronoDb_{Guid.NewGuid()}")
|
||||
.Options;
|
||||
_db = new AppDbContext(options);
|
||||
_tokenService = new TokenService();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
_db.Dispose();
|
||||
}
|
||||
|
||||
private AppDbContext _db = null!;
|
||||
private ITokenService _tokenService = null!;
|
||||
|
||||
private class MockHttpContextAccessor : IHttpContextAccessor
|
||||
{
|
||||
public HttpContext? HttpContext { get; set; }
|
||||
}
|
||||
|
||||
private IHttpContextAccessor CreateMockHttpContextAccessor(string? token = null)
|
||||
{
|
||||
var context = new DefaultHttpContext();
|
||||
if (!string.IsNullOrEmpty(token)) context.Request.Headers["Authorization"] = $"Bearer {token}";
|
||||
return new MockHttpContextAccessor { HttpContext = context };
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task HotChocolateSchema_BuildsSuccessfully_AndContainsQueriesAndMutations()
|
||||
{
|
||||
var schema = await new ServiceCollection()
|
||||
.AddSingleton(_tokenService)
|
||||
.AddHttpContextAccessor()
|
||||
.AddGraphQLServer()
|
||||
.AddQueryType<Query>()
|
||||
.AddMutationType<Mutation>()
|
||||
.BuildSchemaAsync();
|
||||
|
||||
Assert.That(schema, Is.Not.Null);
|
||||
var schemaString = schema.ToString();
|
||||
Assert.That(schemaString, Does.Contain("login("));
|
||||
Assert.That(schemaString, Does.Contain("note("));
|
||||
Assert.That(schemaString, Does.Contain("saveNote("));
|
||||
Assert.That(schemaString, Does.Contain("deleteNote("));
|
||||
Assert.That(schemaString, Does.Contain("decks"));
|
||||
Assert.That(schemaString, Does.Contain("saveDeck("));
|
||||
Assert.That(schemaString, Does.Contain("deleteDeck("));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Mutation_Login_AutoCreatesNewUser_AndReturnsValidToken()
|
||||
{
|
||||
var mutation = new Mutation();
|
||||
var result = await mutation.Login(_db, _tokenService, "newplayer", "password123");
|
||||
|
||||
Assert.That(result.Success, Is.True);
|
||||
Assert.That(result.Username, Is.EqualTo("newplayer"));
|
||||
Assert.That(result.Token, Is.Not.Null.And.Not.Empty);
|
||||
|
||||
var validatedUser = _tokenService.ValidateToken(result.Token);
|
||||
Assert.That(validatedUser, Is.EqualTo("newplayer"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Mutation_Login_ExistingUser_ValidatesPassword()
|
||||
{
|
||||
_db.Users.Add(new User
|
||||
{ Username = "existinguser", Password = "correctpassword", CreatedAt = DateTime.UtcNow });
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
var mutation = new Mutation();
|
||||
|
||||
// 1. Invalid password
|
||||
var failedResult = await mutation.Login(_db, _tokenService, "existinguser", "wrongpassword");
|
||||
Assert.That(failedResult.Success, Is.False);
|
||||
Assert.That(failedResult.Token, Is.Null);
|
||||
|
||||
// 2. Valid password
|
||||
var successResult = await mutation.Login(_db, _tokenService, "existinguser", "correctpassword");
|
||||
Assert.That(successResult.Success, Is.True);
|
||||
Assert.That(successResult.Username, Is.EqualTo("existinguser"));
|
||||
Assert.That(successResult.Token, Is.Not.Null.And.Not.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Query_And_Mutation_WithoutAuth_ThrowsException()
|
||||
{
|
||||
var query = new Query();
|
||||
var mutation = new Mutation();
|
||||
var unauthenticatedAccessor = CreateMockHttpContextAccessor(null);
|
||||
|
||||
Assert.ThrowsAsync<Exception>(async () =>
|
||||
{
|
||||
await query.GetNote(_db, unauthenticatedAccessor, _tokenService, "Alina, the Overflowing Cup");
|
||||
});
|
||||
|
||||
Assert.ThrowsAsync<Exception>(async () =>
|
||||
{
|
||||
await mutation.SaveNote(_db, unauthenticatedAccessor, _tokenService, "Alina, the Overflowing Cup",
|
||||
"note");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CardNotes_UserIsolation_EnsuresUserOnlySeesAndWritesOwnNote()
|
||||
{
|
||||
var mutation = new Mutation();
|
||||
var query = new Query();
|
||||
|
||||
var aliceLogin = await mutation.Login(_db, _tokenService, "alice", "alicepass");
|
||||
var bobLogin = await mutation.Login(_db, _tokenService, "bob", "bobpass");
|
||||
|
||||
var aliceAccessor = CreateMockHttpContextAccessor(aliceLogin.Token);
|
||||
var bobAccessor = CreateMockHttpContextAccessor(bobLogin.Token);
|
||||
|
||||
var cardName = "Chronos, Time Shifter";
|
||||
|
||||
// 1. Alice saves note on card
|
||||
var aliceSaved = await mutation.SaveNote(_db, aliceAccessor, _tokenService, cardName, "Alice's Secret Combo");
|
||||
Assert.That(aliceSaved.Note, Is.EqualTo("Alice's Secret Combo"));
|
||||
Assert.That(aliceSaved.Username, Is.EqualTo("alice"));
|
||||
|
||||
// 2. Bob queries same card -> gets empty note
|
||||
var bobQuery1 = await query.GetNote(_db, bobAccessor, _tokenService, cardName);
|
||||
Assert.That(bobQuery1, Is.Not.Null);
|
||||
Assert.That(bobQuery1!.Note, Is.EqualTo(""));
|
||||
Assert.That(bobQuery1.Username, Is.EqualTo("bob"));
|
||||
|
||||
// 3. Bob saves his own note on the same card
|
||||
var bobSaved = await mutation.SaveNote(_db, bobAccessor, _tokenService, cardName, "Bob's Fast Aggro Note");
|
||||
Assert.That(bobSaved.Note, Is.EqualTo("Bob's Fast Aggro Note"));
|
||||
Assert.That(bobSaved.Username, Is.EqualTo("bob"));
|
||||
|
||||
// 4. Verify Alice still sees only her note
|
||||
var aliceQuery = await query.GetNote(_db, aliceAccessor, _tokenService, cardName);
|
||||
Assert.That(aliceQuery!.Note, Is.EqualTo("Alice's Secret Combo"));
|
||||
Assert.That(aliceQuery.Username, Is.EqualTo("alice"));
|
||||
|
||||
// 5. Verify Bob sees only his note
|
||||
var bobQuery2 = await query.GetNote(_db, bobAccessor, _tokenService, cardName);
|
||||
Assert.That(bobQuery2!.Note, Is.EqualTo("Bob's Fast Aggro Note"));
|
||||
Assert.That(bobQuery2.Username, Is.EqualTo("bob"));
|
||||
|
||||
// 6. Alice deletes her note -> Bob's note is untouched
|
||||
var aliceDeleted = await mutation.DeleteNote(_db, aliceAccessor, _tokenService, cardName);
|
||||
Assert.That(aliceDeleted, Is.True);
|
||||
|
||||
var aliceQueryAfterDelete = await query.GetNote(_db, aliceAccessor, _tokenService, cardName);
|
||||
Assert.That(aliceQueryAfterDelete!.Note, Is.EqualTo(""));
|
||||
|
||||
var bobQueryAfterAliceDelete = await query.GetNote(_db, bobAccessor, _tokenService, cardName);
|
||||
Assert.That(bobQueryAfterAliceDelete!.Note, Is.EqualTo("Bob's Fast Aggro Note"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Deck_MutationsAndQueries_WorkWithAuthentication()
|
||||
{
|
||||
var mutation = new Mutation();
|
||||
var query = new Query();
|
||||
|
||||
var token = _tokenService.GenerateToken("player1");
|
||||
var accessor = CreateMockHttpContextAccessor(token);
|
||||
|
||||
var deckId = Guid.NewGuid();
|
||||
var input = new UserDeckInput(deckId, "My Custom Deck", ["Card1", "Card2"], ["Diver1"], "Some notes",
|
||||
"Season 1");
|
||||
var savedDeck = await mutation.SaveDeck(_db, accessor, _tokenService, input);
|
||||
|
||||
Assert.That(savedDeck.Id, Is.EqualTo(deckId));
|
||||
Assert.That(savedDeck.Name, Is.EqualTo("My Custom Deck"));
|
||||
|
||||
var retrieved = await query.GetDeck(_db, accessor, _tokenService, deckId);
|
||||
Assert.That(retrieved, Is.Not.Null);
|
||||
Assert.That(retrieved!.Name, Is.EqualTo("My Custom Deck"));
|
||||
|
||||
var allDecks = await query.GetDecks(_db, accessor, _tokenService);
|
||||
Assert.That(allDecks.Any(d => d.Id == deckId), Is.True);
|
||||
|
||||
var deleted = await mutation.DeleteDeck(_db, accessor, _tokenService, deckId);
|
||||
Assert.That(deleted, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CardNotesGraphQLService_WhenOffline_ReturnsEmptyWithoutCallingNetwork()
|
||||
{
|
||||
var mockHttp = new HttpClient(new TestHttpMessageHandler(_ =>
|
||||
throw new InvalidOperationException("Network should not be called when offline")));
|
||||
|
||||
var networkStatus = new NetworkStatusService(new DummyJsRuntime(false));
|
||||
await networkStatus.InitializeAsync();
|
||||
|
||||
var sp = new ServiceCollection().BuildServiceProvider();
|
||||
var authService = new AuthService(mockHttp, networkStatus, sp);
|
||||
authService.SetAuth("player1", "dummy_token");
|
||||
|
||||
var service = new CardNotesGraphQLService(mockHttp, networkStatus, authService);
|
||||
|
||||
var note = await service.GetNoteAsync("Alina, the Overflowing Cup");
|
||||
Assert.That(note, Is.EqualTo(string.Empty));
|
||||
|
||||
var saved = await service.SaveNoteAsync("Alina, the Overflowing Cup", "test note");
|
||||
Assert.That(saved, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CardNotesGraphQLService_WhenOnlineAndAuthenticated_SendsAuthorizationHeader()
|
||||
{
|
||||
string? capturedAuthHeader = null;
|
||||
var mockHttp = new HttpClient(new TestHttpMessageHandler(req =>
|
||||
{
|
||||
capturedAuthHeader = req.Headers.Authorization?.ToString();
|
||||
var content = req.Content?.ReadAsStringAsync().Result ?? "";
|
||||
if (content.Contains("GetCardNote"))
|
||||
return new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(
|
||||
@"{""data"":{""note"":{""username"":""player1"",""cardName"":""Alina, the Overflowing Cup"",""note"":""Awesome!""}}}")
|
||||
};
|
||||
if (content.Contains("SaveCardNote"))
|
||||
return new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(
|
||||
@"{""data"":{""saveNote"":{""username"":""player1"",""cardName"":""Alina, the Overflowing Cup"",""note"":""Saved!""}}}")
|
||||
};
|
||||
if (content.Contains("__typename"))
|
||||
return new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(@"{""data"":{""__typename"":""Query""}}")
|
||||
};
|
||||
return new HttpResponseMessage(HttpStatusCode.BadRequest);
|
||||
}));
|
||||
|
||||
var networkStatus = new NetworkStatusService(new DummyJsRuntime(true));
|
||||
await networkStatus.InitializeAsync();
|
||||
|
||||
var sp = new ServiceCollection().BuildServiceProvider();
|
||||
var authService = new AuthService(mockHttp, networkStatus, sp);
|
||||
authService.SetAuth("player1", "my_test_token_123");
|
||||
|
||||
var service = new CardNotesGraphQLService(mockHttp, networkStatus, authService);
|
||||
|
||||
var isApiOnline = await service.IsApiOnlineAsync();
|
||||
Assert.That(isApiOnline, Is.True);
|
||||
|
||||
var note = await service.GetNoteAsync("Alina, the Overflowing Cup");
|
||||
Assert.That(note, Is.EqualTo("Awesome!"));
|
||||
Assert.That(capturedAuthHeader, Is.EqualTo("Bearer my_test_token_123"));
|
||||
|
||||
var saved = await service.SaveNoteAsync("Alina, the Overflowing Cup", "Saved!");
|
||||
Assert.That(saved, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AppDbContext_Model_ConfiguresRequiredEntities()
|
||||
{
|
||||
var cardNotesEntity = _db.Model.FindEntityType(typeof(CardNote));
|
||||
Assert.That(cardNotesEntity, Is.Not.Null);
|
||||
|
||||
var usersEntity = _db.Model.FindEntityType(typeof(User));
|
||||
Assert.That(usersEntity, Is.Not.Null);
|
||||
|
||||
var userDecksEntity = _db.Model.FindEntityType(typeof(API.Models.UserDeck));
|
||||
Assert.That(userDecksEntity, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AppDbContext_Migrations_ArePresentInAssembly()
|
||||
{
|
||||
var migrationsAssembly = typeof(AppDbContext).Assembly;
|
||||
var migrationTypes = migrationsAssembly.GetTypes()
|
||||
.Where(t => typeof(Microsoft.EntityFrameworkCore.Migrations.Migration).IsAssignableFrom(t) && !t.IsAbstract)
|
||||
.ToList();
|
||||
|
||||
Assert.That(migrationTypes, Is.Not.Empty, "Expected EF Core migrations to be present in API project.");
|
||||
Assert.That(migrationTypes.Any(m => m.Name.Contains("InitialCreate")), Is.True);
|
||||
}
|
||||
|
||||
private class TestHttpMessageHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly Func<HttpRequestMessage, HttpResponseMessage> _handler;
|
||||
|
||||
public TestHttpMessageHandler(Func<HttpRequestMessage, HttpResponseMessage> handler)
|
||||
{
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(_handler(request));
|
||||
}
|
||||
}
|
||||
|
||||
private class DummyJsRuntime : IJSRuntime
|
||||
{
|
||||
private readonly bool _online;
|
||||
|
||||
public DummyJsRuntime(bool online)
|
||||
{
|
||||
_online = online;
|
||||
}
|
||||
|
||||
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
|
||||
{
|
||||
if (identifier == "networkStatus.initialize" && typeof(TValue) == typeof(bool))
|
||||
return ValueTask.FromResult((TValue)(object)_online);
|
||||
return ValueTask.FromResult(default(TValue)!);
|
||||
}
|
||||
|
||||
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken,
|
||||
object?[]? args)
|
||||
{
|
||||
return InvokeAsync<TValue>(identifier, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace Tests;
|
||||
|
||||
using Microsoft.JSInterop;
|
||||
using Web.Services;
|
||||
|
||||
namespace Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class NetworkStatusTests
|
||||
{
|
||||
@@ -23,7 +23,8 @@ public class NetworkStatusTests
|
||||
return new ValueTask<TValue>(default(TValue)!);
|
||||
}
|
||||
|
||||
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
|
||||
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken,
|
||||
object?[]? args)
|
||||
{
|
||||
return InvokeAsync<TValue>(identifier, args);
|
||||
}
|
||||
@@ -92,4 +93,4 @@ public class NetworkStatusTests
|
||||
Assert.That(eventCount, Is.EqualTo(2));
|
||||
Assert.That(currentStatus, Is.True);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\API\API.csproj"/>
|
||||
<ProjectReference Include="..\Model\Model.csproj"/>
|
||||
<ProjectReference Include="..\DatabaseTest\DatabaseTest.csproj"/>
|
||||
<ProjectReference Include="..\Web\Web.csproj"/>
|
||||
|
||||
Reference in New Issue
Block a user