fix(driver-ablegacy-cli): resolve Medium code-review finding (Driver.AbLegacy.Cli-001)
WriteCommand.ParseValue wraps FormatException/OverflowException as CliFx CommandException so a bad --value yields a clean one-line CLI error naming the value and target type instead of a raw .NET stack trace. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
| Review date | 2026-05-22 |
|
| Review date | 2026-05-22 |
|
||||||
| Commit reviewed | `76d35d1` |
|
| Commit reviewed | `76d35d1` |
|
||||||
| Status | Reviewed |
|
| Status | Reviewed |
|
||||||
| Open findings | 7 |
|
| Open findings | 6 |
|
||||||
|
|
||||||
## Checklist coverage
|
## Checklist coverage
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ a category produced nothing rather than leaving it blank.
|
|||||||
| Severity | Medium |
|
| Severity | Medium |
|
||||||
| Category | Error handling & resilience |
|
| Category | Error handling & resilience |
|
||||||
| Location | `Commands/WriteCommand.cs:46`, `Commands/WriteCommand.cs:62-72` |
|
| Location | `Commands/WriteCommand.cs:46`, `Commands/WriteCommand.cs:62-72` |
|
||||||
| Status | Open |
|
| Status | Resolved |
|
||||||
|
|
||||||
**Description:** `WriteCommand.ExecuteAsync` calls `ParseValue(Value, DataType)` at
|
**Description:** `WriteCommand.ExecuteAsync` calls `ParseValue(Value, DataType)` at
|
||||||
line 46, *before* the `try` block and outside any catch. `ParseValue` uses
|
line 46, *before* the `try` block and outside any catch. `ParseValue` uses
|
||||||
@@ -59,7 +59,7 @@ type (mirroring the existing `Bit` and unsupported-type branches). Either catch
|
|||||||
`FormatException`/`OverflowException` inside `ParseValue` and rethrow as
|
`FormatException`/`OverflowException` inside `ParseValue` and rethrow as
|
||||||
`CommandException`, or use `TryParse` and throw `CommandException` on failure.
|
`CommandException`, or use `TryParse` and throw `CommandException` on failure.
|
||||||
|
|
||||||
**Resolution:** _(open)_
|
**Resolution:** Resolved 2026-05-22 — wrapped numeric parses in `ParseValue` with `try/catch` for `FormatException`/`OverflowException`, rethrowing as `CommandException` with a message naming the offending value and type; updated test to assert `CommandException` and added overflow regression test.
|
||||||
|
|
||||||
### Driver.AbLegacy.Cli-002
|
### Driver.AbLegacy.Cli-002
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,16 @@ public sealed class WriteCommand : AbLegacyCommandBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Parse <c>--value</c> per <see cref="AbLegacyDataType"/>, invariant culture.</summary>
|
/// <summary>Parse <c>--value</c> per <see cref="AbLegacyDataType"/>, invariant culture.</summary>
|
||||||
internal static object ParseValue(string raw, AbLegacyDataType type) => type switch
|
/// <exception cref="CliFx.Exceptions.CommandException">
|
||||||
|
/// Thrown when <paramref name="raw"/> cannot be parsed as the requested type (malformed
|
||||||
|
/// input or out-of-range value) so CliFx renders a clean one-line error instead of a raw
|
||||||
|
/// stack trace.
|
||||||
|
/// </exception>
|
||||||
|
internal static object ParseValue(string raw, AbLegacyDataType type)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
{
|
{
|
||||||
AbLegacyDataType.Bit => ParseBool(raw),
|
AbLegacyDataType.Bit => ParseBool(raw),
|
||||||
AbLegacyDataType.Int or AbLegacyDataType.AnalogInt => short.Parse(raw, CultureInfo.InvariantCulture),
|
AbLegacyDataType.Int or AbLegacyDataType.AnalogInt => short.Parse(raw, CultureInfo.InvariantCulture),
|
||||||
@@ -70,6 +79,18 @@ public sealed class WriteCommand : AbLegacyCommandBase
|
|||||||
or AbLegacyDataType.ControlElement => int.Parse(raw, CultureInfo.InvariantCulture),
|
or AbLegacyDataType.ControlElement => int.Parse(raw, CultureInfo.InvariantCulture),
|
||||||
_ => throw new CliFx.Exceptions.CommandException($"Unsupported DataType '{type}' for write."),
|
_ => throw new CliFx.Exceptions.CommandException($"Unsupported DataType '{type}' for write."),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
catch (FormatException ex)
|
||||||
|
{
|
||||||
|
throw new CliFx.Exceptions.CommandException(
|
||||||
|
$"Value '{raw}' is not a valid {type}: {ex.Message}", innerException: ex);
|
||||||
|
}
|
||||||
|
catch (OverflowException ex)
|
||||||
|
{
|
||||||
|
throw new CliFx.Exceptions.CommandException(
|
||||||
|
$"Value '{raw}' is out of range for {type}: {ex.Message}", innerException: ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static bool ParseBool(string raw) => raw.Trim().ToLowerInvariant() switch
|
private static bool ParseBool(string raw) => raw.Trim().ToLowerInvariant() switch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -72,12 +72,24 @@ public sealed class WriteCommandParseValueTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ParseValue_non_numeric_for_numeric_types_throws()
|
public void ParseValue_non_numeric_for_numeric_types_throws_CommandException()
|
||||||
{
|
{
|
||||||
Should.Throw<FormatException>(
|
// Bad input must surface as CommandException (clean one-line error), not a raw FormatException.
|
||||||
|
Should.Throw<CliFx.Exceptions.CommandException>(
|
||||||
() => WriteCommand.ParseValue("xyz", AbLegacyDataType.Int));
|
() => WriteCommand.ParseValue("xyz", AbLegacyDataType.Int));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("99999", AbLegacyDataType.Int)] // short range is ±32767
|
||||||
|
[InlineData("-99999", AbLegacyDataType.Int)]
|
||||||
|
[InlineData("-99999", AbLegacyDataType.AnalogInt)]
|
||||||
|
public void ParseValue_out_of_range_throws_CommandException(string raw, AbLegacyDataType type)
|
||||||
|
{
|
||||||
|
// Out-of-range values must surface as CommandException, not OverflowException.
|
||||||
|
Should.Throw<CliFx.Exceptions.CommandException>(
|
||||||
|
() => WriteCommand.ParseValue(raw, type));
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("N7:0", AbLegacyDataType.Int, "N7:0:Int")]
|
[InlineData("N7:0", AbLegacyDataType.Int, "N7:0:Int")]
|
||||||
[InlineData("B3:0/3", AbLegacyDataType.Bit, "B3:0/3:Bit")]
|
[InlineData("B3:0/3", AbLegacyDataType.Bit, "B3:0/3:Bit")]
|
||||||
|
|||||||
Reference in New Issue
Block a user