using ClawdDotNet.App.Services;
using ClawdDotNet.App.Settings;
using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Tools;
using Microsoft.Extensions.Logging.Abstractions;
using ClawdDotNet.Core.Instances;
namespace ClawdDotNet.App.Tests.Services;
///
/// An without engine, scanner and network — enough for the
/// administration services.
///
/// This is possible because AppHost.StartAsync is only one way to
/// build one: the properties are required init, nothing more is needed. What
/// is missing here — Engine, Scanner, Staging — is regularly null in operation
/// too, and exactly that behavior is to be tested.
///
internal sealed class TestHost : IDisposable
{
private readonly string _root;
public AppHost Host { get; }
/// Every change that went through SaveInstance.
public List Changes { get; } = [];
public TestHost(params AgentConfig[] agents)
{
_root = Path.Combine(
Path.GetTempPath(), "clawd-tests", "host-" + Guid.NewGuid().ToString("N")[..8]);
var instancesDir = Path.Combine(_root, "Instances");
var instancePath = Path.Combine(instancesDir, "Test");
Directory.CreateDirectory(instancePath);
var instance = new InstanceConfig { InstanceName = "Test" };
instance.Agents.AddRange(agents);
Host = new AppHost
{
Settings = new SettingsManager(_root),
Directories = new InstanceDirectoryManager(instancesDir),
Instance = instance,
InstancePath = instancePath,
LogDirectory = Path.Combine(_root, "Logs"),
LoggerFactory = NullLoggerFactory.Instance,
Tools = new ToolRegistry()
};
Host.InstanceChanged += Changes.Add;
}
public void Dispose()
{
try { Directory.Delete(_root, recursive: true); }
catch { /* A leftover test folder is no reason to turn a run red. */ }
}
public static AgentConfig Agent(string agentId, string displayName = "")
=> new()
{
AgentId = agentId,
DisplayName = displayName.Length == 0 ? agentId : displayName,
Model = "anthropic/claude-sonnet-4-5"
};
}