acdream/tests/AcDream.App.Tests/Credentials/AppCredentialResolverTests.cs
Erik db9ad53c1c docs: Campaign LA — pinned launch-contract schema COMMITTED into plan LA1
The LA3 Opus review process note was right: the contract both sides
implement lived only in orchestrator prompts, which is exactly the drift
mode the pin exists to prevent (and it produced the paths-key CRITICAL).
The schema, field rules, probe-mode discriminator, and status vocabulary
are now a binding plan section; amendments change this text first,
implementations second. Ledger: LA3 fix round dispatched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:04:32 +02:00

167 lines
5.5 KiB
C#

using AcDream.App.Configuration;
using AcDream.App.Credentials;
namespace AcDream.App.Tests.Credentials;
/// <summary>
/// Campaign LA slice LA1: <see cref="AppCredentialResolver"/> is a minimal
/// port of <c>AcDream.Headless.Credentials.HeadlessCredentialResolver</c>
/// scoped to the App session-config credential shape — see that file's own
/// doc for why it is an independent port rather than a shared reference.
/// Mirrors <c>HeadlessCredentialResolverTests</c>'s coverage.
/// </summary>
public sealed class AppCredentialResolverTests
{
[Fact]
public void EnvironmentSecretIsRedactedAndErasable()
{
const string variable = "ACDREAM_LA1_TEST_ENV_SECRET";
const string secretValue = "test-secret-value";
Environment.SetEnvironmentVariable(variable, secretValue);
try
{
var resolver = new AppCredentialResolver(
TextReader.Null,
Environment.CurrentDirectory,
isLinux: false);
AppCredentialSecret secret = resolver.Resolve(
"session",
new SessionCredentialDescriptor
{
Provider = SessionCredentialProviderKind.Environment,
Reference = variable,
});
Assert.Equal(secretValue, secret.Reveal());
Assert.DoesNotContain(secretValue, secret.ToString());
secret.Dispose();
Assert.True(secret.IsDisposed);
Assert.Throws<ObjectDisposedException>(secret.Reveal);
}
finally
{
Environment.SetEnvironmentVariable(variable, null);
}
}
[Fact]
public void StandardInputConsumesOneSecretWithoutEchoingIt()
{
const string secretValue = "stdin-secret";
var resolver = new AppCredentialResolver(
new StringReader(secretValue + Environment.NewLine),
Environment.CurrentDirectory,
isLinux: false);
using AppCredentialSecret secret = resolver.Resolve(
"session",
new SessionCredentialDescriptor
{
Provider = SessionCredentialProviderKind.StandardInput,
Reference = "session-stdin",
});
Assert.Equal(secretValue, secret.Reveal());
Assert.DoesNotContain(secretValue, secret.ToString());
}
[Fact]
public void CredentialFileIsResolvedRelativeToConfiguredDirectory()
{
string directory = Path.Combine(
Path.GetTempPath(),
$"acdream-app-credentials-{Guid.NewGuid():N}");
Directory.CreateDirectory(directory);
string path = Path.Combine(directory, "session.pass");
File.WriteAllText(path, "file-secret" + Environment.NewLine);
try
{
var resolver = new AppCredentialResolver(
TextReader.Null,
directory,
isLinux: false);
using AppCredentialSecret secret = resolver.Resolve(
"session",
new SessionCredentialDescriptor
{
Provider = SessionCredentialProviderKind.File,
Reference = "session.pass",
});
Assert.Equal("file-secret", secret.Reveal());
}
finally
{
File.Delete(path);
Directory.Delete(directory);
}
}
[Fact]
public void MissingSecretErrorNeverContainsAnotherSecret()
{
const string variable = "ACDREAM_LA1_TEST_OTHER_SECRET";
const string unrelatedSecret = "must-not-leak";
Environment.SetEnvironmentVariable(variable, unrelatedSecret);
try
{
var resolver = new AppCredentialResolver(
new StringReader(string.Empty),
Environment.CurrentDirectory,
isLinux: false);
AppCredentialException error =
Assert.Throws<AppCredentialException>(() =>
resolver.Resolve(
"session",
new SessionCredentialDescriptor
{
Provider = SessionCredentialProviderKind.Environment,
Reference = "ACDREAM_LA1_TEST_DOES_NOT_EXIST",
}));
Assert.DoesNotContain(unrelatedSecret, error.ToString());
}
finally
{
Environment.SetEnvironmentVariable(variable, null);
}
}
[Fact]
public void LinuxRejectsGroupOrOtherCredentialPermissions()
{
if (!OperatingSystem.IsLinux())
return;
string path = Path.Combine(
Path.GetTempPath(),
$"acdream-app-credential-{Guid.NewGuid():N}");
File.WriteAllText(path, "linux-secret");
File.SetUnixFileMode(
path,
UnixFileMode.UserRead | UnixFileMode.GroupRead);
try
{
var resolver = new AppCredentialResolver(
TextReader.Null,
Path.GetDirectoryName(path)!,
isLinux: true);
Assert.Throws<AppCredentialException>(() =>
resolver.Resolve(
"session",
new SessionCredentialDescriptor
{
Provider = SessionCredentialProviderKind.File,
Reference = Path.GetFileName(path),
}));
}
finally
{
File.Delete(path);
}
}
}