Bläddra i källkod

Fixed git repo initialisation

Tim Jones 2 veckor sedan
förälder
incheckning
102629d554

+ 9 - 2
RackPeek.Domain/Git/LibGit2GitRepository.cs

@@ -39,9 +39,16 @@ public sealed class LibGit2GitRepository : IGitRepository {
         // config directory so the UI can immediately offer Add Remote — without
         // this, first-time users hit "Git is not available." on every action.
         // Init is idempotent for existing repos (IsValid skips the call) and
-        // does not touch existing files; it only creates .git/.
+        // does not touch existing files; it only creates .git/. Failure (e.g.
+        // a read-only mount) must not throw out of the singleton factory — the
+        // UI relies on IsAvailable=false to render the writability warning.
         if (Directory.Exists(configDirectory) && !Repository.IsValid(configDirectory))
-            Repository.Init(configDirectory);
+            try {
+                Repository.Init(configDirectory);
+            }
+            catch {
+                // Leave IsAvailable=false; surfaced via the writability warning.
+            }
 
         _isAvailable = Repository.IsValid(configDirectory);
         // When insecureTls is true, accept any TLS certificate. Required for

+ 7 - 3
Shared.Rcl/wwwroot/raw_docs/inventory-api.md

@@ -104,7 +104,7 @@ in the repository.
   "version": 3,
   "resources": [
     {
-      "kind": "server",
+      "kind": "Server",
       "name": "web-01",
       "tags": ["homelab", "prod"],
       "labels": { "env": "production" },
@@ -116,6 +116,10 @@ in the repository.
 }
 ```
 
+The `kind` discriminator is case-sensitive. Use the exact value for each
+resource type: `Server`, `Switch`, `Router`, `Firewall`, `AccessPoint`,
+`Ups`, `Desktop`, `Laptop`, `Service`, `System`.
+
 #### Response
 
 `200 OK` with a diff summary:
@@ -161,7 +165,7 @@ curl -X POST http://localhost:8080/api/inventory \
       "version": 3,
       "resources": [
         {
-          "kind": "server",
+          "kind": "Server",
           "name": "web-01",
           "tags": ["homelab"],
           "ram": { "size": 64 }
@@ -183,7 +187,7 @@ curl -X POST http://localhost:8080/api/inventory \
     "json": {
       "version": 3,
       "resources": [
-        { "kind": "server", "name": "web-01", "ram": { "size": 128 } }
+        { "kind": "Server", "name": "web-01", "ram": { "size": 128 } }
       ]
     }
   }'

+ 53 - 0
Tests/Api/InventoryEndpointTests.cs

@@ -760,6 +760,59 @@ public class InventoryEndpointTests(ITestOutputHelper output) : ApiTestBase(outp
         Assert.DoesNotContain("beta", newYaml);
     }
 
+    [Theory]
+    [InlineData("Server")]
+    [InlineData("Switch")]
+    [InlineData("Router")]
+    [InlineData("Firewall")]
+    [InlineData("AccessPoint")]
+    [InlineData("Ups")]
+    [InlineData("Desktop")]
+    [InlineData("Laptop")]
+    [InlineData("Service")]
+    [InlineData("System")]
+    public async Task Documented_Kind_Discriminator_Values_Are_Accepted(string kind) {
+        // The inventory-api doc lists the exact set of valid kind discriminator
+        // values. Every one must be accepted as-is so the doc and runtime stay
+        // in lockstep.
+        HttpClient client = CreateClient(true);
+
+        HttpResponseMessage response = await client.PostAsJsonAsync("/api/inventory",
+            new {
+                json = new {
+                    version = 3,
+                    resources = new[]
+                    {
+                        new { kind, name = $"kind-probe-{kind.ToLowerInvariant()}" }
+                    }
+                }
+            });
+
+        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+    }
+
+    [Fact]
+    public async Task Lowercase_Kind_Is_Rejected() {
+        // The discriminator is case-sensitive — the doc must use exact casing
+        // or users hit 400. This test pins that contract so a future refactor
+        // making the kind case-insensitive would surface here and prompt a
+        // doc update.
+        HttpClient client = CreateClient(true);
+
+        HttpResponseMessage response = await client.PostAsJsonAsync("/api/inventory",
+            new {
+                json = new {
+                    version = 3,
+                    resources = new[]
+                    {
+                        new { kind = "server", name = "lowercase-probe" }
+                    }
+                }
+            });
+
+        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+    }
+
     [Fact]
     public async Task Merge_Preserves_Labels_Not_In_Incoming() {
         HttpClient client = CreateClient(true);

+ 33 - 0
Tests/Git/GitRepositoryAvailabilityTests.cs

@@ -101,6 +101,39 @@ public sealed class GitRepositoryAvailabilityTests : IDisposable {
         Assert.False(repo.IsAvailable);
     }
 
+    [Fact]
+    public void Readonly_Config_Directory_Does_Not_Throw_From_Constructor() {
+        // Doc promises the UI will surface "Git configured but config directory
+        // is not writable" when init can't run — that requires the constructor
+        // to swallow the init failure and set IsAvailable=false rather than
+        // crashing the Blazor render that resolves the singleton.
+        if (OperatingSystem.IsWindows())
+            return; // chmod-style read-only doesn't translate cleanly
+
+        var readOnly = Path.Combine(_tempDir, "readonly");
+        Directory.CreateDirectory(readOnly);
+        File.WriteAllText(Path.Combine(readOnly, "config.yaml"), "");
+
+        // Strip write permission for owner + group + other (0555).
+        File.SetUnixFileMode(readOnly,
+            UnixFileMode.UserRead | UnixFileMode.UserExecute |
+            UnixFileMode.GroupRead | UnixFileMode.GroupExecute |
+            UnixFileMode.OtherRead | UnixFileMode.OtherExecute);
+
+        try {
+            var repo = new LibGit2GitRepository(readOnly, _creds);
+
+            Assert.False(repo.IsAvailable,
+                "constructor must report unavailable, not throw, so the UI can " +
+                "show the documented writability warning");
+        }
+        finally {
+            // Restore write perm so cleanup works.
+            File.SetUnixFileMode(readOnly,
+                UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
+        }
+    }
+
     [Fact]
     public async Task Add_Remote_Persists_The_Configured_Origin() {
         // The full happy path that was broken before the fix: construct a repo