using Books.Api.Domain.Companies; using Books.Api.EventFlow.Repositories; using Books.Api.Tests.Helpers; using Books.Api.Tests.Infrastructure; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; namespace Books.Api.Tests.GraphQL; /// /// Integration tests for Company GraphQL operations. /// Each test class runs with its own isolated database. /// [Trait("Category", "Integration")] public class CompanyGraphQLTests(TestWebApplicationFactory factory) : IntegrationTestBase(factory) { [Fact] public async Task Query_Companies_ReturnsEmptyList_WhenNoCompaniesExist() { // Arrange var graphqlClient = new GraphQLTestClient(Client); // Act var response = await graphqlClient.QueryAsync(""" query { companies { id name } } """); // Assert response.EnsureNoErrors(); response.Data.Should().NotBeNull(); response.Data!.Companies.Should().BeEmpty(); } [Fact] public async Task Mutation_CreateCompany_CreatesCompanySuccessfully() { // Arrange var graphqlClient = new GraphQLTestClient(Client); // Act var response = await graphqlClient.MutateAsync(""" mutation CreateCompany($input: CreateCompanyInput!) { createCompany(input: $input) { id name cvr country currency fiscalYearStartMonth } } """, new { input = new { name = "Test Virksomhed A/S", cvr = "12345678", country = "DK", currency = "DKK", fiscalYearStartMonth = 1 } }); // Assert response.EnsureNoErrors(); response.Data.Should().NotBeNull(); response.Data!.CreateCompany.Should().NotBeNull(); response.Data.CreateCompany!.Name.Should().Be("Test Virksomhed A/S"); response.Data.CreateCompany.Cvr.Should().Be("12345678"); response.Data.CreateCompany.Country.Should().Be("DK"); response.Data.CreateCompany.Currency.Should().Be("DKK"); response.Data.CreateCompany.FiscalYearStartMonth.Should().Be(1); } [Fact] public async Task Query_Company_ReturnsCompany_AfterCreation() { // Arrange var graphqlClient = new GraphQLTestClient(Client); // Create a company first var createResponse = await graphqlClient.MutateAsync(""" mutation CreateCompany($input: CreateCompanyInput!) { createCompany(input: $input) { id name } } """, new { input = new { name = "Query Test Virksomhed", cvr = "87654321" } }); createResponse.EnsureNoErrors(); var companyId = createResponse.Data!.CreateCompany!.Id; // Act - Query the company by ID (with eventual consistency) var company = await Eventually.GetAsync(async () => { var response = await graphqlClient.QueryAsync(""" query GetCompany($id: ID!) { company(id: $id) { id name cvr } } """, new { id = companyId }); return response.Data?.Company; }); // Assert company.Should().NotBeNull(); company!.Name.Should().Be("Query Test Virksomhed"); company.Cvr.Should().Be("87654321"); } [Fact] public async Task Query_Companies_ReturnsAllCompanies_AfterMultipleCreations() { // Arrange var graphqlClient = new GraphQLTestClient(Client); // Create multiple companies with unique names for this test var company1Name = $"ListTest-Virksomhed-{Guid.NewGuid():N}"; var company2Name = $"ListTest-Virksomhed-{Guid.NewGuid():N}"; await graphqlClient.MutateAsync($$""" mutation { createCompany(input: { name: "{{company1Name}}" }) { id } } """); await graphqlClient.MutateAsync($$""" mutation { createCompany(input: { name: "{{company2Name}}" }) { id } } """); // Act - Wait for eventual consistency (at least 2 companies with our prefix) var companies = await Eventually.GetAsync( async () => { var response = await graphqlClient.QueryAsync(""" query { companies { id name } } """); var allCompanies = response.Data?.Companies ?? []; var ourCompanies = allCompanies.Where(c => c.Name.StartsWith("ListTest-")).ToList(); return ourCompanies.Count >= 2 ? ourCompanies : null; }); // Assert - verify our specific companies are present companies.Should().Contain(c => c.Name == company1Name); companies.Should().Contain(c => c.Name == company2Name); } [Fact] public async Task Mutation_UpdateCompany_UpdatesCompanySuccessfully() { // Arrange var graphqlClient = new GraphQLTestClient(Client); // Create a company first var createResponse = await graphqlClient.MutateAsync(""" mutation { createCompany(input: { name: "Original Name" }) { id } } """); createResponse.EnsureNoErrors(); var companyId = createResponse.Data!.CreateCompany!.Id; // Wait for the company to be created await Eventually.GetAsync(async () => { var repository = GetService(); return await repository.GetByIds([CompanyId.With(companyId)]); }); // Act - Update the company var updateResponse = await graphqlClient.MutateAsync(""" mutation UpdateCompany($id: ID!, $input: UpdateCompanyInput!) { updateCompany(id: $id, input: $input) { id name cvr } } """, new { id = companyId, input = new { name = "Updated Name", cvr = "11111111" } }); // Assert updateResponse.EnsureNoErrors(); // Wait for eventual consistency and verify update var updatedCompany = await Eventually.GetAsync(async () => { var repository = GetService(); var companies = await repository.GetByIds([CompanyId.With(companyId)]); var company = companies.First(); return company?.Name == "Updated Name" ? company : null; }); updatedCompany.Should().NotBeNull(); updatedCompany!.Name.Should().Be("Updated Name"); updatedCompany.Cvr.Should().Be("11111111"); } // Response DTOs for deserialization private class CompaniesResponse { public List Companies { get; set; } = []; } private class CompanyResponse { public CompanyDto? Company { get; set; } } private class CreateCompanyResponse { public CompanyDto? CreateCompany { get; set; } } private class UpdateCompanyResponse { public CompanyDto? UpdateCompany { get; set; } } private class CompanyDto { public string Id { get; set; } = string.Empty; public string Name { get; set; } = string.Empty; public string? Cvr { get; set; } public string? Country { get; set; } public string? Currency { get; set; } public int? FiscalYearStartMonth { get; set; } } }