Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ public async Task<VariablePath> SetVariable(

await using var provider = _variableProviderFactory.CreateProvider(configuration);

await provider.SetAsync(path.Path, value, context);
var resolvedPath = await provider.SetAsync(path.Path, value, context);

return path;
return new VariablePath(path.ProviderName, resolvedPath);
}

public async Task<IEnumerable<VariablePath>> ListVariables(IVariableProviderContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,87 @@ public async Task ResolveVariables_ProviderNotFound_ThrowsExitException()
await Assert.ThrowsAsync<ExitException>(() =>
resolver.ResolveVariables(keys, context));
}

[Fact]
public async Task SetVariable_ProviderReturnsTransformedPath_ReturnsPathWithProviderName()
{
// Arrange – simulates the `secret` provider, whose SetAsync returns the
// Base64-encoded ciphertext rather than the input path. The resolver must
// propagate that transformed path so the CLI can display it to the user.
var factoryMock = new Mock<IVariableProviderFactory>();

var configurations = new List<VariableProviderConfiguration>
{
new VariableProviderConfiguration
{
Name = "secret",
Type = "secret",
Configuration = JsonNode.Parse("{}")!
}
};

const string inputPath = "irrelevant";
const string cipherTextPath = "K2b8F2zG9HpJxMImaYwlf0ByzArc+abc/def==";
var value = JsonValue.Create("super-secret")!;

var providerMock = new Mock<IVariableProvider>();
providerMock
.Setup(p => p.SetAsync(inputPath, value, It.IsAny<IVariableProviderContext>()))
.ReturnsAsync(cipherTextPath);

factoryMock.Setup(f => f.CreateProvider(configurations[0]))
.Returns(providerMock.Object);

var resolver = new VariableResolver(factoryMock.Object, new VariableListCache(), configurations);
var context = new VariableProviderContext(null!, CancellationToken.None);

// Act
var result = await resolver.SetVariable(
new VariablePath("secret", inputPath),
value,
context);

// Assert
result.ProviderName.Should().Be("secret");
result.Path.Should().Be(cipherTextPath);
result.ToString().Should().Be($"$secret:{cipherTextPath}");
}

[Fact]
public async Task SetVariable_ProviderReturnsSamePath_ReturnsPathUnchanged()
{
// Arrange – simulates the `local` provider, whose SetAsync returns the
// original path unchanged.
var factoryMock = new Mock<IVariableProviderFactory>();

var configurations = new List<VariableProviderConfiguration>
{
new VariableProviderConfiguration
{
Name = "Provider1",
Type = "local",
Configuration = JsonNode.Parse("""{ "path": "/path/to/file.json" }""")!
}
};

var providerMock = new Mock<IVariableProvider>();
providerMock
.Setup(p => p.SetAsync("Key1", It.IsAny<JsonNode>(), It.IsAny<IVariableProviderContext>()))
.ReturnsAsync("Key1");

factoryMock.Setup(f => f.CreateProvider(configurations[0]))
.Returns(providerMock.Object);

var resolver = new VariableResolver(factoryMock.Object, new VariableListCache(), configurations);
var context = new VariableProviderContext(null!, CancellationToken.None);

// Act
var result = await resolver.SetVariable(
new VariablePath("Provider1", "Key1"),
JsonValue.Create("v")!,
context);

// Assert
result.Should().Be(new VariablePath("Provider1", "Key1"));
}
}