summaryrefslogtreecommitdiff
path: root/tests/Agent.Core.Tests/ConfigurationTests.cs
blob: 79c746dbd0b4b4b1bf5706cbed44d4231c8c555a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
using Agent.App.Cli;
using Agent.App.Configuration;
using Agent.Core.Skills;

namespace Agent.Core.Tests;

public sealed class ConfigurationTests
{
    [Fact]
    public void ValidYamlLoadsTypedConfiguration()
    {
        using var directory = new TestDirectory();
        string config = CreateValidConfiguration(directory);
        AgentConfiguration result = ConfigurationLoader.Load(config);
        Assert.Equal(1, result.SchemaVersion);
        Assert.Equal("model", result.Ollama.Model);
        Assert.Equal("deny", result.Sandbox.Network.Mode);
    }

    [Fact]
    public void UnknownYamlPropertyIsFatal()
    {
        using var directory = new TestDirectory();
        string config = CreateValidConfiguration(directory);
        System.IO.File.AppendAllText(config, "unknown_root: true\n");
        Assert.Throws<ConfigurationException>(() => ConfigurationLoader.Load(config));
    }

    [Fact]
    public void InvalidSchemaIsFatal()
    {
        using var directory = new TestDirectory();
        string config = CreateValidConfiguration(directory);
        System.IO.File.WriteAllText(config, System.IO.File.ReadAllText(config).Replace("schema_version: 1", "schema_version: 2", StringComparison.Ordinal));
        Assert.Throws<ConfigurationException>(() => ConfigurationLoader.Load(config));
    }

    [Fact]
    public void CommandLineLanguageOverrideIsCaptured()
    {
        CommandLine result = CommandLine.Parse(["serve", "--lang", "ja", "--transport", "http"]);
        Assert.Equal("ja", result.Language);
        Assert.Equal("http", result.Transport);
    }

    [Fact]
    public void SkillLoader_loads_standard_skill_directories_only()
    {
        using var directory = new TestDirectory();
        // Legacy single markdown — should be ignored (not loaded).
        directory.File("skills/legacy.md", "legacy skill");
        // Standard skill directory with frontmatter.
        directory.File("skills/python-via-uv/SKILL.md", "---\nname: python-via-uv\ndescription: Python execution skill.\n---\n# Python via uv\nInstructions here.");
        // Non-SKILL.md files in skill directory should not be loaded as skills.
        directory.File("skills/python-via-uv/reference.md", "must not be loaded as a skill");

        var skills = SkillLoader.Load(Path.Combine(directory.Path, "skills"));

        Assert.Single(skills);
        Assert.Equal("python-via-uv", skills[0].Name);
        Assert.Equal("Python execution skill.", skills[0].Description);
        Assert.Contains("Instructions here.", skills[0].Instructions);
    }

    [Fact]
    public void SkillLoader_rejects_missing_frontmatter()
    {
        using var directory = new TestDirectory();
        directory.File("skills/no-frontmatter/SKILL.md", "# No frontmatter\nJust body.");

        Assert.Throws<SkillLoadException>(() => SkillLoader.Load(Path.Combine(directory.Path, "skills")));
    }

    [Fact]
    public void SkillLoader_rejects_name_mismatch()
    {
        using var directory = new TestDirectory();
        directory.File("skills/wrong-name/SKILL.md", "---\nname: different-name\ndescription: A skill.\n---\nBody.");

        Assert.Throws<SkillLoadException>(() => SkillLoader.Load(Path.Combine(directory.Path, "skills")));
    }

    [Fact]
    public void SkillLoader_rejects_duplicate_names()
    {
        using var directory = new TestDirectory();
        directory.File("skills/dup-skill/SKILL.md", "---\nname: dup-skill\ndescription: First.\n---\nBody1.");
        // Create a second directory with the same skill name (via symlink or junction is not needed;
        // just create another dir that also has name: dup-skill in frontmatter).
        directory.File("skills/dup-skill-copy/SKILL.md", "---\nname: dup-skill\ndescription: Second.\n---\nBody2.");

        Assert.Throws<SkillLoadException>(() => SkillLoader.Load(Path.Combine(directory.Path, "skills")));
    }

    private static string CreateValidConfiguration(TestDirectory directory)
    {
        directory.File("prompts/user-system.md", string.Empty);
        Directory.CreateDirectory(System.IO.Path.Combine(directory.Path, "config/locales"));
        Directory.CreateDirectory(System.IO.Path.Combine(directory.Path, "skills"));
        return directory.File("agent.yaml", """
            schema_version: 1
            application:
              language: en
            agent:
              user_system_prompt: prompts/user-system.md
            ollama:
              endpoint: http://127.0.0.1:11434
              model: model
            localization:
              directory: config/locales
              fallback: en
            skills:
              directory: skills
            """);
    }
}