Public Access
Fix workflow rule validation logging
PR and Push Build/Test / build-and-test (push) Failing after 9m19s
PR and Push Build/Test / build-and-test (push) Failing after 9m19s
This commit is contained in:
@@ -538,34 +538,20 @@ public sealed class WorkflowRulesEditorTests
|
||||
[Fact]
|
||||
public async Task RulesEditorToolsRefuseInvalidYamlWithoutOverwritingFile()
|
||||
{
|
||||
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(root);
|
||||
var rulesPath = Path.Combine(root, "rules.yaml");
|
||||
await File.WriteAllTextAsync(rulesPath, "rules: []");
|
||||
var tools = new WorkflowRulesEditorTools(new MeetingAssistantOptions
|
||||
{
|
||||
Automation = new AutomationOptions { RulesPath = rulesPath }
|
||||
});
|
||||
var fixture = await CreateRulesEditorFixtureAsync();
|
||||
|
||||
var result = await tools.WriteRules("rules:\n- name: [", replace_file: true);
|
||||
var result = await fixture.Tools.WriteRules("rules:\n- name: [", replace_file: true);
|
||||
|
||||
Assert.StartsWith("Refused: workflow rules YAML is invalid.", result);
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(rulesPath));
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(fixture.RulesPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RulesEditorToolsRefuseWorkflowRulesThatWouldFailAtRuntime()
|
||||
{
|
||||
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(root);
|
||||
var rulesPath = Path.Combine(root, "rules.yaml");
|
||||
await File.WriteAllTextAsync(rulesPath, "rules: []");
|
||||
var tools = new WorkflowRulesEditorTools(new MeetingAssistantOptions
|
||||
{
|
||||
Automation = new AutomationOptions { RulesPath = rulesPath }
|
||||
});
|
||||
var fixture = await CreateRulesEditorFixtureAsync();
|
||||
|
||||
var result = await tools.WriteRules("""
|
||||
var result = await fixture.Tools.WriteRules("""
|
||||
rules:
|
||||
- name: broken-step
|
||||
on:
|
||||
@@ -579,22 +565,37 @@ public sealed class WorkflowRulesEditorTests
|
||||
Assert.Contains("broken-step", result);
|
||||
Assert.Contains("send_email", result);
|
||||
Assert.Contains("add_attendee", result);
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(rulesPath));
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(fixture.RulesPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RulesEditorToolsLogWorkflowRuleValidationFailures()
|
||||
{
|
||||
var logger = new CapturingLogger();
|
||||
var fixture = await CreateRulesEditorFixtureAsync(logger);
|
||||
|
||||
var result = await fixture.Tools.WriteRules("""
|
||||
rules:
|
||||
- name: broken-step
|
||||
on:
|
||||
- created: {}
|
||||
steps:
|
||||
- uses: send_email
|
||||
value: Manuel
|
||||
""", replace_file: true);
|
||||
|
||||
Assert.StartsWith("Refused: workflow rules are invalid.", result);
|
||||
Assert.Contains(logger.Messages, message =>
|
||||
message.Contains(nameof(WorkflowRulesEditorTools.WriteRules), StringComparison.Ordinal) &&
|
||||
message.Contains("send_email", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RulesEditorToolsExplainUnsupportedWorkflowProperties()
|
||||
{
|
||||
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(root);
|
||||
var rulesPath = Path.Combine(root, "rules.yaml");
|
||||
await File.WriteAllTextAsync(rulesPath, "rules: []");
|
||||
var tools = new WorkflowRulesEditorTools(new MeetingAssistantOptions
|
||||
{
|
||||
Automation = new AutomationOptions { RulesPath = rulesPath }
|
||||
});
|
||||
var fixture = await CreateRulesEditorFixtureAsync();
|
||||
|
||||
var result = await tools.WriteRules("""
|
||||
var result = await fixture.Tools.WriteRules("""
|
||||
rules:
|
||||
- name: broken-property
|
||||
on:
|
||||
@@ -609,22 +610,15 @@ public sealed class WorkflowRulesEditorTests
|
||||
Assert.Contains("broken-property", result);
|
||||
Assert.Contains("meeting.status", result);
|
||||
Assert.Contains("title", result);
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(rulesPath));
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(fixture.RulesPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RulesEditorToolsRefuseTranscriptLinePropertyOutsideTranscriptLineTrigger()
|
||||
{
|
||||
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(root);
|
||||
var rulesPath = Path.Combine(root, "rules.yaml");
|
||||
await File.WriteAllTextAsync(rulesPath, "rules: []");
|
||||
var tools = new WorkflowRulesEditorTools(new MeetingAssistantOptions
|
||||
{
|
||||
Automation = new AutomationOptions { RulesPath = rulesPath }
|
||||
});
|
||||
var fixture = await CreateRulesEditorFixtureAsync();
|
||||
|
||||
var result = await tools.WriteRules("""
|
||||
var result = await fixture.Tools.WriteRules("""
|
||||
rules:
|
||||
- name: broken-transcript-property
|
||||
on:
|
||||
@@ -639,22 +633,37 @@ public sealed class WorkflowRulesEditorTests
|
||||
Assert.Contains("broken-transcript-property", result);
|
||||
Assert.Contains("transcript.line", result);
|
||||
Assert.Contains("transcript_line", result);
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(rulesPath));
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(fixture.RulesPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RulesEditorToolsAcceptAndParseMaskedProfanityRedactionRule()
|
||||
{
|
||||
var fixture = await CreateRulesEditorFixtureAsync();
|
||||
|
||||
var result = await fixture.Tools.WriteRules(MeetingWorkflowTestRules.MaskedProfanityRedactionYaml, replace_file: true);
|
||||
|
||||
Assert.Equal(fixture.RulesPath, result);
|
||||
var provider = new FileMeetingWorkflowRulesProvider(
|
||||
NullLogger<FileMeetingWorkflowRulesProvider>.Instance);
|
||||
var rule = Assert.Single(await provider.GetRulesAsync(fixture.Options, CancellationToken.None));
|
||||
Assert.Equal("redact-masked-profanity", rule.Name);
|
||||
Assert.NotNull(Assert.Single(rule.On).TranscriptLine);
|
||||
Assert.Equal("contains(transcript.line, '*****')", Assert.Single(rule.If).Condition);
|
||||
var step = Assert.Single(rule.Steps);
|
||||
Assert.Equal("set_property", step.Uses);
|
||||
Assert.Equal("transcript.line", step.Property);
|
||||
Assert.Equal(
|
||||
"""@Model.Transcript.Line.Replace("*****", "[redacted]")""",
|
||||
step.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RulesEditorToolsRefuseMalformedRazorStepValues()
|
||||
{
|
||||
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(root);
|
||||
var rulesPath = Path.Combine(root, "rules.yaml");
|
||||
await File.WriteAllTextAsync(rulesPath, "rules: []");
|
||||
var tools = new WorkflowRulesEditorTools(new MeetingAssistantOptions
|
||||
{
|
||||
Automation = new AutomationOptions { RulesPath = rulesPath }
|
||||
});
|
||||
var fixture = await CreateRulesEditorFixtureAsync();
|
||||
|
||||
var result = await tools.WriteRules("""
|
||||
var result = await fixture.Tools.WriteRules("""
|
||||
rules:
|
||||
- name: broken-razor
|
||||
on:
|
||||
@@ -668,21 +677,15 @@ public sealed class WorkflowRulesEditorTests
|
||||
Assert.Contains("broken-razor", result);
|
||||
Assert.Contains("Razor", result);
|
||||
Assert.Contains("ThisIsNotAModelVar", result);
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(rulesPath));
|
||||
Assert.Equal("rules: []", await File.ReadAllTextAsync(fixture.RulesPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RulesEditorToolsAllowValidRazorAndEmailStepValues()
|
||||
{
|
||||
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(root);
|
||||
var rulesPath = Path.Combine(root, "rules.yaml");
|
||||
var tools = new WorkflowRulesEditorTools(new MeetingAssistantOptions
|
||||
{
|
||||
Automation = new AutomationOptions { RulesPath = rulesPath }
|
||||
});
|
||||
var fixture = await CreateRulesEditorFixtureAsync(initialContent: "");
|
||||
|
||||
var result = await tools.WriteRules("""
|
||||
var result = await fixture.Tools.WriteRules("""
|
||||
rules:
|
||||
- name: valid-razor
|
||||
on:
|
||||
@@ -692,9 +695,9 @@ public sealed class WorkflowRulesEditorTests
|
||||
value: "Speaker @Model.Speaker.Name can contact Support@example.com"
|
||||
""", replace_file: true);
|
||||
|
||||
Assert.Equal(rulesPath, result);
|
||||
Assert.Contains("@Model.Speaker.Name", await File.ReadAllTextAsync(rulesPath));
|
||||
Assert.Contains("Support@example.com", await File.ReadAllTextAsync(rulesPath));
|
||||
Assert.Equal(fixture.RulesPath, result);
|
||||
Assert.Contains("@Model.Speaker.Name", await File.ReadAllTextAsync(fixture.RulesPath));
|
||||
Assert.Contains("Support@example.com", await File.ReadAllTextAsync(fixture.RulesPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1159,6 +1162,55 @@ public sealed class WorkflowRulesEditorTests
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CapturingLogger : ILogger
|
||||
{
|
||||
public List<string> Messages { get; } = [];
|
||||
|
||||
public IDisposable? BeginScope<TState>(TState state)
|
||||
where TState : notnull
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log<TState>(
|
||||
LogLevel logLevel,
|
||||
EventId eventId,
|
||||
TState state,
|
||||
Exception? exception,
|
||||
Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
Messages.Add(formatter(state, exception));
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<RulesEditorFixture> CreateRulesEditorFixtureAsync(
|
||||
ILogger? logger = null,
|
||||
string initialContent = "rules: []")
|
||||
{
|
||||
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(root);
|
||||
var rulesPath = Path.Combine(root, "rules.yaml");
|
||||
await File.WriteAllTextAsync(rulesPath, initialContent);
|
||||
var options = new MeetingAssistantOptions
|
||||
{
|
||||
Automation = new AutomationOptions { RulesPath = rulesPath }
|
||||
};
|
||||
return new RulesEditorFixture(
|
||||
rulesPath,
|
||||
options,
|
||||
new WorkflowRulesEditorTools(options, logger: logger));
|
||||
}
|
||||
|
||||
private sealed record RulesEditorFixture(
|
||||
string RulesPath,
|
||||
MeetingAssistantOptions Options,
|
||||
WorkflowRulesEditorTools Tools);
|
||||
|
||||
private sealed class WorkflowRulesEditorIdentityFixture : IAsyncDisposable
|
||||
{
|
||||
private readonly string tempDirectory;
|
||||
|
||||
Reference in New Issue
Block a user