|
2 | 2 |
|
3 | 3 | using System; |
4 | 4 | using System.Collections.Immutable; |
| 5 | +using System.Diagnostics; |
| 6 | +using System.IO; |
5 | 7 | using Microsoft.Agents.AI.Workflows.Declarative.Extensions; |
6 | 8 | using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; |
7 | 9 | using Microsoft.Agents.ObjectModel; |
@@ -48,20 +50,19 @@ public WorkflowExpressionEngineTests(ITestOutputHelper output) |
48 | 50 | [InlineData("Value(Env.Setting)", VariableScopeNames.Environment)] |
49 | 51 | [InlineData("Error(Env.Setting)", VariableScopeNames.Environment)] |
50 | 52 | [InlineData("Error({Kind: ErrorKind.Custom, Message: Local.Setting})", VariableScopeNames.Local)] |
51 | | - public void ExpressionErrorOmitsSensitiveInput(string expression, string scope) |
| 53 | + public void ExpressionErrorReturnsLabelledBlankForSensitiveInput(string expression, string scope) |
52 | 54 | { |
53 | 55 | // Arrange |
54 | 56 | const string Value = "sample-setting"; |
55 | 57 | this.State.Set("Setting", FormulaValue.New(Value), scope, SensitivityLevel.Sensitive); |
56 | 58 | this.State.Bind(); |
57 | 59 |
|
58 | 60 | // Act |
59 | | - DeclarativeActionException exception = Assert.Throws<DeclarativeActionException>( |
60 | | - () => this.State.Evaluator.GetValue(ValueExpression.Expression(expression))); |
| 61 | + EvaluationResult<DataValue> result = this.State.Evaluator.GetValue(ValueExpression.Expression(expression)); |
61 | 62 |
|
62 | 63 | // Assert |
63 | | - Assert.DoesNotContain(Value, exception.ToString(), StringComparison.Ordinal); |
64 | | - Assert.Null(exception.InnerException); |
| 64 | + Assert.Equal(DataValue.Blank(), result.Value); |
| 65 | + Assert.Equal(SensitivityLevel.Sensitive, result.Sensitivity); |
65 | 66 | } |
66 | 67 |
|
67 | 68 | [Theory] |
@@ -108,6 +109,113 @@ public void BoolExpressionGetValueForBlank() => |
108 | 109 | BoolExpression.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), |
109 | 110 | expectedValue: false); |
110 | 111 |
|
| 112 | + [Theory] |
| 113 | + [InlineData(true)] |
| 114 | + [InlineData(false)] |
| 115 | + public void BoolExpressionGetValueForConditionalBlankPreservesSensitivity(bool value) |
| 116 | + { |
| 117 | + // Arrange |
| 118 | + this.State.Set("Setting", FormulaValue.New(value), VariableScopeNames.Environment, SensitivityLevel.Sensitive); |
| 119 | + this.State.Bind(); |
| 120 | + |
| 121 | + // Act & Assert |
| 122 | + this.EvaluateExpression( |
| 123 | + BoolExpression.Expression("If(Env.Setting, false)"), |
| 124 | + expectedValue: false, |
| 125 | + expectedSensitivity: SensitivityLevel.Sensitive); |
| 126 | + } |
| 127 | + |
| 128 | + [Theory] |
| 129 | + [InlineData(true)] |
| 130 | + [InlineData(false)] |
| 131 | + public void ValueExpressionGetValueForConditionalErrorPreservesSensitivity(bool value) |
| 132 | + { |
| 133 | + // Arrange |
| 134 | + this.State.Set("Setting", FormulaValue.New(value), VariableScopeNames.Environment, SensitivityLevel.Sensitive); |
| 135 | + this.State.Bind(); |
| 136 | + |
| 137 | + // Act |
| 138 | + EvaluationResult<DataValue> result = |
| 139 | + this.State.Evaluator.GetValue(ValueExpression.Expression("If(Env.Setting, 1 / 0, 1)")); |
| 140 | + |
| 141 | + // Assert |
| 142 | + Assert.Equal(SensitivityLevel.Sensitive, result.Sensitivity); |
| 143 | + Assert.Equal(value ? DataValue.Blank() : NumberDataValue.Create(1), result.Value); |
| 144 | + } |
| 145 | + |
| 146 | + [Theory] |
| 147 | + [InlineData(VariableScopeNames.Environment, "Env")] |
| 148 | + [InlineData(VariableScopeNames.Local, "Local")] |
| 149 | + public void StringExpressionGetValueForConversionErrorPreservesSensitivity(string scope, string alias) |
| 150 | + { |
| 151 | + // Arrange |
| 152 | + this.State.Set("Setting", FormulaValue.New("not-a-number"), scope, SensitivityLevel.Sensitive); |
| 153 | + this.State.Bind(); |
| 154 | + |
| 155 | + // Act & Assert |
| 156 | + this.EvaluateExpression( |
| 157 | + StringExpression.Expression($"Text(Value({alias}.Setting))"), |
| 158 | + expectedValue: string.Empty, |
| 159 | + expectedSensitivity: SensitivityLevel.Sensitive); |
| 160 | + } |
| 161 | + |
| 162 | + [Fact] |
| 163 | + public void ValueExpressionGetValueForNonSensitiveErrorStillThrows() |
| 164 | + { |
| 165 | + // Arrange |
| 166 | + ValueExpression expression = ValueExpression.Expression("1 / 0"); |
| 167 | + |
| 168 | + // Act & Assert |
| 169 | + Assert.Throws<DeclarativeActionException>(() => this.State.Evaluator.GetValue(expression)); |
| 170 | + } |
| 171 | + |
| 172 | + [Theory] |
| 173 | + [InlineData("{ Item: Value(Env.Setting) }")] |
| 174 | + [InlineData("[Value(Env.Setting)]")] |
| 175 | + [InlineData("Table({ Item: Value(Env.Setting) })")] |
| 176 | + [InlineData("{ Nested: Table({ Item: Value(Env.Setting) }) }")] |
| 177 | + public void ValueExpressionGetValueForNestedErrorPreservesSensitivity(string expression) |
| 178 | + { |
| 179 | + // Arrange |
| 180 | + this.State.Set("Setting", FormulaValue.New("not-a-number"), VariableScopeNames.Environment, SensitivityLevel.Sensitive); |
| 181 | + this.State.Bind(); |
| 182 | + |
| 183 | + // Act & Assert |
| 184 | + this.EvaluateExpression( |
| 185 | + ValueExpression.Expression(expression), |
| 186 | + expectedValue: DataValue.Blank(), |
| 187 | + expectedSensitivity: SensitivityLevel.Sensitive); |
| 188 | + } |
| 189 | + |
| 190 | + [Theory] |
| 191 | + [InlineData("Value(Env.Setting)", VariableScopeNames.Environment)] |
| 192 | + [InlineData("Error(Env.Setting)", VariableScopeNames.Environment)] |
| 193 | + [InlineData("Error({Kind: ErrorKind.Custom, Message: Local.Setting})", VariableScopeNames.Local)] |
| 194 | + public void ExpressionErrorDiagnosticUsesReferenceName(string expression, string scope) |
| 195 | + { |
| 196 | + // Arrange |
| 197 | + this.State.Set("Setting", FormulaValue.New("not-a-number"), scope, SensitivityLevel.Sensitive); |
| 198 | + this.State.Bind(); |
| 199 | + using StringWriter output = new(); |
| 200 | + using TextWriterTraceListener listener = new(output); |
| 201 | + Trace.Listeners.Add(listener); |
| 202 | + |
| 203 | + try |
| 204 | + { |
| 205 | + // Act |
| 206 | + this.State.Evaluator.GetValue(ValueExpression.Expression(expression)); |
| 207 | + listener.Flush(); |
| 208 | + |
| 209 | + // Assert |
| 210 | + Assert.Contains($"{scope}.Setting", output.ToString()); |
| 211 | + Assert.DoesNotContain("not-a-number", output.ToString()); |
| 212 | + } |
| 213 | + finally |
| 214 | + { |
| 215 | + Trace.Listeners.Remove(listener); |
| 216 | + } |
| 217 | + } |
| 218 | + |
111 | 219 | [Fact] |
112 | 220 | public void BoolExpressionGetValueForVariable() |
113 | 221 | { |
|
0 commit comments