Files
DodoSSH/tests/DodoSSH.Client.Domain.Tests/HostFactory.cs
T
jaap-jan 689ba6c1bf Merge origin/main into claude/spelunk-tunnel
Port forwarding took host schema version 8 first, so the Spelunk route moves to 9 and is written after the forwards. A host may carry both.
2026-09-28 21:38:09 +02:00

121 lines
5.2 KiB
C#

namespace DodoSSH.Client.Domain.Tests;
/// <summary>
/// Builds hosts, and the groups they are filed under, so each test varies only what it is about.
/// </summary>
internal static class HostFactory
{
internal static Guid Bastion { get; } = Guid.Parse("0192f0c8-1111-7c3d-8e4f-5a6b7c8d9e01");
internal static Guid Relay { get; } = Guid.Parse("0192f0c8-2222-7c3d-8e4f-5a6b7c8d9e02");
/// <summary>A vault SSH key id, for the hosts that bind one.</summary>
internal static Guid DeployKey { get; } = Guid.Parse("0192f0c8-3333-7c3d-8e4f-5a6b7c8d9e03");
/// <summary>A group id, for the hosts that are filed under one.</summary>
internal static Guid Production { get; } = Guid.Parse("0192f0c8-4444-7c3d-8e4f-5a6b7c8d9e04");
/// <summary>A tag id, for the hosts that wear one.</summary>
internal static Guid Pci { get; } = Guid.Parse("0192f0c8-8888-7c3d-8e4f-5a6b7c8d9e08");
/// <summary>A second tag id, for the tests about two people tagging one host.</summary>
internal static Guid EuWest { get; } = Guid.Parse("0192f0c8-9999-7c3d-8e4f-5a6b7c8d9e09");
/// <summary>A forward to the database, as the host would carry it.</summary>
internal static PortForwardRule Postgres { get; } = new()
{
Id = Guid.Parse("0192f0c8-aaaa-7c3d-8e4f-5a6b7c8d9e0a"),
Kind = PortForwardKind.Local,
BindPort = 5432,
TargetHost = "db.internal",
TargetPort = 5432,
};
/// <summary>A SOCKS forward, for the tests about two forwards on one host.</summary>
internal static PortForwardRule Socks { get; } = new()
{
Id = Guid.Parse("0192f0c8-bbbb-7c3d-8e4f-5a6b7c8d9e0b"),
Kind = PortForwardKind.Dynamic,
BindPort = 1080,
};
/// <summary>A credential holding a Spelunk API key, for the hosts reached through Spelunk.</summary>
internal static Guid SpelunkKey { get; } = Guid.Parse("0192f0c8-cccc-7c3d-8e4f-5a6b7c8d9e0c");
/// <summary>A server's id in Spelunk.</summary>
internal static Guid SpelunkServer { get; } = Guid.Parse("0192f0c8-dddd-7c3d-8e4f-5a6b7c8d9e0d");
/// <summary>A route to <see cref="SpelunkServer"/>, with <see cref="SpelunkKey"/>.</summary>
internal static SpelunkRoute ThroughSpelunk(string apiBase = "https://api.spelunk.example.com") =>
new() { ApiBase = apiBase, ServerId = SpelunkServer, ApiKeyCredentialId = SpelunkKey };
/// <remarks>
/// <paramref name="port"/> defaults to an explicit 22 rather than to null, so a test that says nothing
/// about ports gets the host shape that existed before inheritance did — which is what nearly every
/// suite here is still about. Passing <see langword="null"/> is how a test asks for the new one.
/// </remarks>
internal static HostSecret Host(
string label = "prod-db",
string hostname = "db.internal",
int? port = 22,
string? username = "deploy",
string? notes = null,
Guid[]? jumps = null,
(string Name, string Value)[]? options = null,
bool relayEnabled = false,
Guid? sshKeyId = null,
Guid? credentialId = null,
bool? asksForPassword = null,
Guid? groupId = null,
Guid[]? tags = null,
string[]? pinnedPaths = null,
PortForwardRule[]? portForwards = null,
SpelunkRoute? spelunk = null) =>
new()
{
Label = label,
Hostname = hostname,
Port = port,
Username = username,
Notes = notes,
JumpHostIds = jumps is null ? JumpChain.Empty : JumpChain.Create(jumps),
Options = options is null
? HostOptions.Empty
: HostOptions.Create(options.Select(o => new HostOption(o.Name, o.Value))),
RelayEnabled = relayEnabled,
SshKeyId = sshKeyId,
CredentialId = credentialId,
AsksForPassword = asksForPassword,
GroupId = groupId,
TagIds = tags is null ? TagSet.Empty : TagSet.Create(tags),
PinnedPaths = pinnedPaths is null ? PinnedPathList.Empty : PinnedPathList.Create(pinnedPaths),
PortForwards = portForwards is null ? PortForwardList.Empty : PortForwardList.Create(portForwards),
Spelunk = spelunk,
};
/// <summary>
/// A group, flat and defaulting nothing unless the test says otherwise.
/// </summary>
/// <remarks>
/// The defaults of this builder are the shape a group had before it could nest or default anything,
/// which is what most suites still want: a heading with a name. A test that passes nothing here is
/// asserting about the old shape on purpose.
/// </remarks>
internal static HostGroupSecret Group(
string label = "production",
Guid? parentId = null,
int? defaultPort = null,
string? defaultUsername = null,
Guid? defaultSshKeyId = null,
Guid? defaultCredentialId = null) =>
new()
{
Label = label,
ParentId = parentId,
DefaultPort = defaultPort,
DefaultUsername = defaultUsername,
DefaultSshKeyId = defaultSshKeyId,
DefaultCredentialId = defaultCredentialId,
};
}