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
44 changes: 44 additions & 0 deletions src/AppModel/NetDaemon.AppModel.Tests/Config/ConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,50 @@ public void TestDuplicateKeyShouldThrowInvalidDataException()
Assert.Throws<InvalidDataException>(() => configurationBuilder.Build());
}

// Regression: a YAML file whose ROOT is not a mapping must not crash config-build.
// Home Assistant !include targets such as automations.yaml hold a sequence root ("[]"),
// and a bare value is a scalar root. The parser previously cast the root unconditionally
// to YamlMappingNode and threw InvalidCastException at host-config time, before the host
// (and any logging) started. A non-mapping root carries no key/value config, so it must
// contribute no data instead.
[Theory]
[InlineData("[]")] // sequence root (the automations.yaml case)
[InlineData("- one\n- two")] // sequence root with items
[InlineData("just-a-scalar")] // scalar root
public void NonMappingRootedYaml_ContributesNoData_InsteadOfThrowing(string yamlContent)
{
BuildYamlFileAndAssertNoData(yamlContent);
}

// Regression: an empty YAML file (no documents) likewise contributes no data.
[Fact]
public void EmptyYaml_ContributesNoData_InsteadOfThrowing()
{
BuildYamlFileAndAssertNoData(string.Empty);
}

private static void BuildYamlFileAndAssertNoData(string yamlContent)
{
// ARRANGE
var yamlPath = Path.Combine(Path.GetTempPath(), $"nd-yaml-root-{Guid.NewGuid():N}.yaml");
File.WriteAllText(yamlPath, yamlContent);
try
{
var configurationBuilder = new ConfigurationBuilder() as IConfigurationBuilder;
configurationBuilder.AddYamlFile(yamlPath, false, false);

// ACT
var root = configurationBuilder.Build();

// CHECK
root.AsEnumerable().Should().BeEmpty();
}
finally
{
File.Delete(yamlPath);
}
}

[Fact]
public void TestAppGetCorrectJsonConfigInjected()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ internal class YamlConfigurationFileParser
yaml.Load(new StreamReader(input, true));

if (yaml.Documents.Count == 0) return _data;
var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;

// The document node is a mapping node
// The configuration model is key/value pairs, so only a mapping root carries config data.
// Home Assistant !include targets such as automations.yaml have a sequence root ("[]") or
// may be empty (scalar/null). Those hold no key/value config, so treat them as no data
// rather than casting and throwing InvalidCastException at host-config time.
if (yaml.Documents[0].RootNode is not YamlMappingNode mapping) return _data;

VisitYamlMappingNode(mapping);

return _data;
Expand Down
Loading