diff --git a/ClaudeDo.slnx b/ClaudeDo.slnx
index 4fbdd50..178690d 100644
--- a/ClaudeDo.slnx
+++ b/ClaudeDo.slnx
@@ -9,5 +9,6 @@
+
diff --git a/tests/ClaudeDo.Installer.Tests/ClaudeDo.Installer.Tests.csproj b/tests/ClaudeDo.Installer.Tests/ClaudeDo.Installer.Tests.csproj
new file mode 100644
index 0000000..5db6af4
--- /dev/null
+++ b/tests/ClaudeDo.Installer.Tests/ClaudeDo.Installer.Tests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net8.0-windows
+ true
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/ClaudeDo.Installer.Tests/FakeHttpMessageHandler.cs b/tests/ClaudeDo.Installer.Tests/FakeHttpMessageHandler.cs
new file mode 100644
index 0000000..00f009c
--- /dev/null
+++ b/tests/ClaudeDo.Installer.Tests/FakeHttpMessageHandler.cs
@@ -0,0 +1,27 @@
+using System.Net;
+using System.Net.Http;
+
+namespace ClaudeDo.Installer.Tests;
+
+internal sealed class FakeHttpMessageHandler : HttpMessageHandler
+{
+ private readonly Func _handler;
+
+ public FakeHttpMessageHandler(Func handler)
+ {
+ _handler = handler;
+ }
+
+ public FakeHttpMessageHandler(HttpStatusCode status, string body)
+ : this(_ => new HttpResponseMessage(status) { Content = new StringContent(body) })
+ {
+ }
+
+ public List Requests { get; } = new();
+
+ protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ Requests.Add(request);
+ return Task.FromResult(_handler(request));
+ }
+}