using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using SQLInsight.Contracts; using SQLInsight.Hub.Data; using SQLInsight.Licensing; namespace SQLInsight.IntegrationTests; [Trait("Category", "Integration")] public class HubSupportApiTests(HubFixture hub) : IClassFixture { private static SupportCaseRequest Request(Guid? id = null, string title = "Backup fejler") => new(id ?? Guid.NewGuid(), title, "Siden i går", "Anna Andersen", "anna@kunde.dk", "12345678", @"KUNDE\anna"); private async Task PostAsync(string license, SupportCaseRequest request) { using var message = new HttpRequestMessage(HttpMethod.Post, "/api/v1/cases") { Content = JsonContent.Create(request) }; message.Headers.Authorization = new AuthenticationHeaderValue("SQLInsight-License", license); return await hub.Factory.CreateClient().SendAsync(message); } private async Task CreateAsync(string license, SupportCaseRequest request) { var response = await PostAsync(license, request); Assert.True(response.IsSuccessStatusCode, await response.Content.ReadAsStringAsync()); return (await response.Content.ReadFromJsonAsync(JsonSerializerOptions.Web))!; } private static string Name(string prefix) => $"{prefix} {Guid.NewGuid():N}"[..24]; [Fact] public async Task Customer_cases_are_numbered_SUP_000001_then_SUP_000002() { var customer = Name("Nummer"); var license = hub.License(customer); var first = await CreateAsync(license, Request()); var second = await CreateAsync(license, Request()); Assert.Equal("SUP-000001", first.Reference); Assert.Equal("SUP-000002", second.Reference); Assert.Equal(SupportCaseSources.Customer, first.Source); Assert.True(first.IsOpen); await using var scope = hub.Factory.Services.CreateAsyncScope(); var stored = await scope.ServiceProvider.GetRequiredService().SupportCases.SingleAsync(c => c.Customer!.Name == customer && c.Reference == "SUP-000001"); Assert.Equal(("Anna Andersen", "anna@kunde.dk", @"KUNDE\anna"), (stored.ContactName, stored.ContactEmail, stored.RequestedBy)); } [Fact] public async Task A_retried_request_returns_the_same_case() { var customer = Name("Igen"); var license = hub.License(customer); var request = Request(); var first = await CreateAsync(license, request); var again = await CreateAsync(license, request); Assert.Equal(first.Reference, again.Reference); await using var scope = hub.Factory.Services.CreateAsyncScope(); Assert.Equal(1, await scope.ServiceProvider.GetRequiredService().SupportCases.CountAsync(c => c.Customer!.Name == customer)); } [Fact] public async Task Each_customer_has_its_own_sequence() { await CreateAsync(hub.License(Name("Første")), Request()); var other = await CreateAsync(hub.License(Name("Anden")), Request()); Assert.Equal("SUP-000001", other.Reference); } [Fact] public async Task Community_license_cannot_create_cases() { var response = await PostAsync(hub.License(Name("Gratis"), LicensePlan.Community), Request()); Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact] public async Task Invalid_request_is_rejected() { var response = await PostAsync(hub.License(Name("Ugyldig")), Request() with { ContactEmail = "ingen-snabel-a" }); Assert.Equal(HttpStatusCode.UnprocessableEntity, response.StatusCode); } [Fact] public async Task Customer_sees_its_own_cases_including_those_support_created() { var customer = Name("Liste"); var license = hub.License(customer); await CreateAsync(license, Request(title: "Fra kunden")); await CreateAsync(hub.License(Name("Fremmed")), Request(title: "Andens sag")); await using (var scope = hub.Factory.Services.CreateAsyncScope()) { var db = scope.ServiceProvider.GetRequiredService(); var customerId = await db.Customers.Where(c => c.Name == customer).Select(c => c.Id).SingleAsync(); await SQLInsight.Hub.Support.SupportCaseStore.AddAsync(db, new SupportCase { CustomerId = customerId, Reference = string.Empty, Title = "Fra SEOSoft", CreatedAt = DateTime.UtcNow, IsOpen = false, ClosedAt = DateTime.UtcNow, }); } using var message = new HttpRequestMessage(HttpMethod.Get, "/api/v1/cases"); message.Headers.Authorization = new AuthenticationHeaderValue("SQLInsight-License", license); var cases = (await (await hub.Factory.CreateClient().SendAsync(message)).Content.ReadFromJsonAsync>(JsonSerializerOptions.Web))!; Assert.Equal(["Fra SEOSoft", "Fra kunden"], cases.Select(c => c.Title)); Assert.Equal("SUP-000002", cases[0].Reference); Assert.False(cases[0].IsOpen); } [Fact] public async Task Case_created_from_a_finding_is_linked_to_it() { var customer = Name("Fund"); var license = hub.License(customer); using (var submit = new HttpRequestMessage(HttpMethod.Post, "/api/v1/findings") { Content = JsonContent.Create(HubApiTests.Submission("sql01", DateTimeOffset.UtcNow, ("SQLI-DB-007", "database: Sales", "Critical"))), }) { submit.Headers.Authorization = new AuthenticationHeaderValue("SQLInsight-License", license); (await hub.Factory.CreateClient().SendAsync(submit)).EnsureSuccessStatusCode(); } var created = await CreateAsync(license, Request() with { InstanceName = "sql01", RuleId = "SQLI-DB-007", Subject = "database: Sales" }); await using var scope = hub.Factory.Services.CreateAsyncScope(); var finding = await scope.ServiceProvider.GetRequiredService().Findings.Include(f => f.SupportCase) .SingleAsync(f => f.Instance!.Customer!.Name == customer); Assert.Equal(created.Reference, finding.SupportCase?.Reference); } }