using System.Net.Http.Json; using RackPeek.Domain.Api; using Xunit.Abstractions; namespace Tests.Api; /// /// The inventory API is reached without a browser — by scripts, by CI, and by /// rpk discover --push on a timer. It therefore has to work on a server that /// nobody has opened a page on yet. /// [Collection("Process-wide static state")] public class InventoryEndpointStartupTests(ITestOutputHelper output) : ApiTestBase(output) { private const string _existingConfig = """ version: 3 resources: - kind: Server name: hand-written-server notes: documented by hand years ago connections: [] """; [Fact] public async Task The_first_request_after_a_restart_does_not_destroy_the_existing_inventory() { var configPath = Path.Combine(TempDir, "config.yaml"); await File.WriteAllTextAsync(configPath, _existingConfig); // The very first thing to touch this server is an API call, with no Blazor // circuit having ever initialised and therefore no implicit load. HttpClient client = CreateClient(true); HttpResponseMessage response = await client.PostAsJsonAsync("/api/inventory", new { yaml = """ version: 3 resources: - kind: Server name: newly-discovered-box """, mode = "Merge" }); response.EnsureSuccessStatusCode(); var stored = await File.ReadAllTextAsync(configPath); Assert.Contains("hand-written-server", stored); Assert.Contains("documented by hand years ago", stored); Assert.Contains("newly-discovered-box", stored); } [Fact] public async Task An_existing_resource_is_updated_rather_than_re_added_on_a_cold_server() { await File.WriteAllTextAsync(Path.Combine(TempDir, "config.yaml"), _existingConfig); HttpClient client = CreateClient(true); HttpResponseMessage response = await client.PostAsJsonAsync("/api/inventory", new { yaml = """ version: 3 resources: - kind: Server name: hand-written-server notes: updated """, mode = "Merge" }); ImportYamlResponse? result = await response.Content.ReadFromJsonAsync(); Assert.Empty(result!.Added); Assert.Equal(["hand-written-server"], result.Updated); } [Fact] public async Task An_unreadable_config_does_not_stop_the_server_starting() { // The web UI is how someone fixes a broken config, so it has to come up. await File.WriteAllTextAsync( Path.Combine(TempDir, "config.yaml"), "version: 3\nresources:\n - kind: Server\n name: [unterminated\n"); HttpClient client = CreateClient(); HttpResponseMessage response = await client.GetAsync("/health"); response.EnsureSuccessStatusCode(); Assert.Equal("rackpeek", await response.Content.ReadAsStringAsync()); } }